forked from SteamWar/SteamWar
Compare commits
12
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
04519d4cf2 | ||
|
|
88de8c81e7 | ||
|
|
db655bf9db | ||
|
|
aa807060f4 | ||
|
|
9bbbab9d4b | ||
|
|
7cca4ada10 | ||
|
|
725e6df047 | ||
|
|
f3ce124a23 | ||
|
|
e83f72a53e | ||
|
|
def0c19e43 | ||
|
|
3810ccd63d | ||
|
|
a018af1c8a |
+3
-3
@@ -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;
|
||||
}
|
||||
|
||||
-468
@@ -1,468 +0,0 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
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.bausystem.utils.RayTraceUtils;
|
||||
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.inventory.ItemStack;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.BiFunction;
|
||||
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<Player, CursorType> cursorType = Collections.synchronizedMap(new HashMap<>());
|
||||
private static Map<Player, REntityServer> cursors = Collections.synchronizedMap(new HashMap<>());
|
||||
private static final Set<Player> calculating = new HashSet<>();
|
||||
|
||||
public static boolean isSimulatorItem(ItemStack itemStack) {
|
||||
return ItemUtils.isItem(itemStack, "simulator");
|
||||
}
|
||||
|
||||
public SimulatorCursor() {
|
||||
BiFunction<Player, ServerboundMovePlayerPacket, Object> 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);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerDropItem(PlayerDropItemEvent event) {
|
||||
if (!Permission.BUILD.hasPermission(event.getPlayer())) return;
|
||||
calcCursor(event.getPlayer());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerItemHeld(PlayerItemHeldEvent event) {
|
||||
if (!Permission.BUILD.hasPermission(event.getPlayer())) return;
|
||||
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
|
||||
calcCursor(event.getPlayer());
|
||||
}, 1);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onBauMemberUpdate(BauMemberUpdateEvent event) {
|
||||
event.getChanged().forEach(SimulatorCursor::calcCursor);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||
cursorType.remove(event.getPlayer());
|
||||
cursors.remove(event.getPlayer());
|
||||
synchronized (calculating) {
|
||||
calculating.remove(event.getPlayer());
|
||||
}
|
||||
}
|
||||
|
||||
private static final Map<Player, Long> LAST_SNEAKS = new HashMap<>();
|
||||
|
||||
static {
|
||||
Bukkit.getScheduler().runTaskTimer(BauSystem.getInstance(), () -> {
|
||||
long millis = System.currentTimeMillis();
|
||||
LAST_SNEAKS.entrySet().removeIf(entry -> millis - entry.getValue() > 200);
|
||||
}, 1, 1);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGH)
|
||||
public void onPlayerToggleSneak(PlayerToggleSneakEvent event) {
|
||||
if (!event.isSneaking()) return;
|
||||
Player player = event.getPlayer();
|
||||
if (!isSimulatorItem(player.getInventory().getItemInMainHand()) && !isSimulatorItem(player.getInventory().getItemInOffHand())) {
|
||||
return;
|
||||
}
|
||||
if (LAST_SNEAKS.containsKey(player)) {
|
||||
CursorType currentType = cursorType.getOrDefault(player, CursorType.TNT);
|
||||
if (currentType == CursorType.TNT) {
|
||||
cursorType.put(player, CursorType.REDSTONE_BLOCK);
|
||||
} else {
|
||||
cursorType.put(player, CursorType.TNT);
|
||||
}
|
||||
calcCursor(player);
|
||||
} else {
|
||||
LAST_SNEAKS.put(player, System.currentTimeMillis());
|
||||
}
|
||||
}
|
||||
|
||||
public static CursorType getCursorType(Player player) {
|
||||
return cursorType.getOrDefault(player, CursorType.TNT);
|
||||
}
|
||||
|
||||
public static void setCursorType(Player player, CursorType cursorType) {
|
||||
SimulatorCursor.cursorType.put(player, cursorType);
|
||||
calcCursor(player);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
Simulator simulator = SimulatorStorage.getSimulator(player);
|
||||
if (simulator != null && simulator.getStabGenerator() != null) {
|
||||
removeCursor(player);
|
||||
SimulatorWatcher.show(null, player);
|
||||
SWUtils.sendToActionbar(player, "§cGenerating Stab");
|
||||
synchronized (calculating) {
|
||||
calculating.remove(player);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
SimulatorWatcher.show(simulator, player);
|
||||
List<REntity> 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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
return hadCursor;
|
||||
}
|
||||
|
||||
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<RFallingBlockEntity> 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 {
|
||||
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 {
|
||||
pos.setY(pos.getBlockY());
|
||||
}
|
||||
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())),
|
||||
;
|
||||
|
||||
public final Material material;
|
||||
public final Material nonSelectedMaterial;
|
||||
public final BiFunction<Player, RayTraceUtils.RRayTraceResult, Vector> position;
|
||||
public final String name;
|
||||
public final Function<Vector, SimulatorElement<?>> elementFunction;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerInteract(PlayerInteractEvent event) {
|
||||
if (!Permission.BUILD.hasPermission(event.getPlayer())) return;
|
||||
if (!ItemUtils.isItem(event.getItem(), "simulator")) {
|
||||
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 (simulator == null) {
|
||||
return;
|
||||
}
|
||||
SimulatorExecutor.run(event.getPlayer(), simulator, null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.getAction() != Action.RIGHT_CLICK_BLOCK && event.getAction() != Action.RIGHT_CLICK_AIR) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
RayTraceUtils.RRayTraceResult rayTraceResult = RayTraceUtils.traceREntity(player, player.getLocation(), SimulatorWatcher.getEntitiesOfSimulator(simulator));
|
||||
if (simulator == null) {
|
||||
if (rayTraceResult == 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, rayTraceResult, sim);
|
||||
SimulatorStorage.setSimulator(player, sim);
|
||||
});
|
||||
anvilInv.open();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (rayTraceResult == 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<SimulatorElement<?>> 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<SimulatorGroup> 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;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Add new Element to current simulator
|
||||
createElement(player, rayTraceResult, simulator);
|
||||
}
|
||||
|
||||
private void createElement(Player player, RayTraceUtils.RRayTraceResult rayTraceResult, Simulator simulator) {
|
||||
CursorType type = cursorType.getOrDefault(player, CursorType.TNT);
|
||||
Vector vector = type.position.apply(player, rayTraceResult);
|
||||
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);
|
||||
}
|
||||
}
|
||||
+440
@@ -0,0 +1,440 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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 Player player;
|
||||
private Cursor cursor;
|
||||
private CursorType cursorType = CursorType.TNT;
|
||||
private REntityServer emptyTargetServer;
|
||||
private REntityServer currentTargetServer;
|
||||
private long lastSneakMillis;
|
||||
|
||||
private SimulatorCursorComponent() {
|
||||
}
|
||||
|
||||
private SimulatorCursorComponent(CursorType cursorType) {
|
||||
this.cursorType = cursorType;
|
||||
}
|
||||
|
||||
private REntityServer getOrCreateEmptyTargetServer() {
|
||||
if (emptyTargetServer == null) {
|
||||
emptyTargetServer = new REntityServer();
|
||||
}
|
||||
return emptyTargetServer;
|
||||
}
|
||||
|
||||
private boolean handleSneak(long now) {
|
||||
boolean doubleSneak = now - lastSneakMillis <= 200;
|
||||
lastSneakMillis = doubleSneak ? 0 : now;
|
||||
return doubleSneak;
|
||||
}
|
||||
|
||||
private CursorType getCursorType() {
|
||||
return cursorType;
|
||||
}
|
||||
|
||||
private void setCursorType(CursorType cursorType) {
|
||||
this.cursorType = cursorType;
|
||||
refresh();
|
||||
}
|
||||
|
||||
private void switchCursorMode() {
|
||||
cursorType = cursorType == CursorType.TNT ? CursorType.REDSTONE_BLOCK : CursorType.TNT;
|
||||
refresh();
|
||||
}
|
||||
|
||||
private void refresh() {
|
||||
if (!Permission.BUILD.hasPermission(player) || !hasSimulatorItem(player)) {
|
||||
deactivateCursor(player);
|
||||
return;
|
||||
}
|
||||
|
||||
Simulator simulator = SimulatorStorage.getSimulator(player);
|
||||
if (simulator != null && simulator.getStabGenerator() != null) {
|
||||
removeGenericCursor();
|
||||
boolean watcherRemoved = SimulatorWatcher.show(null, player);
|
||||
if (watcherRemoved) {
|
||||
SWUtils.sendToActionbar(player, "");
|
||||
}
|
||||
SWUtils.sendToActionbar(player, "§cGenerating Stab");
|
||||
return;
|
||||
}
|
||||
|
||||
SimulatorWatcher.show(simulator, player);
|
||||
REntityServer targetServer = simulator == null ? getOrCreateEmptyTargetServer() : SimulatorWatcher.getEntityServerOfSimulator(simulator);
|
||||
if (cursor == null || currentTargetServer != targetServer) {
|
||||
removeGenericCursor();
|
||||
currentTargetServer = targetServer;
|
||||
cursor = new Cursor(
|
||||
targetServer,
|
||||
player,
|
||||
Material.GLASS,
|
||||
cursorType.material,
|
||||
cursorType.cursorModes,
|
||||
this::handlePlayerClick,
|
||||
(location, hitEntity) -> sendCursorActionbar(simulator, location != null, hitEntity.isPresent())
|
||||
);
|
||||
} else {
|
||||
cursor.setCursorMaterial(cursorType.material);
|
||||
cursor.setAllowedCursorModes(cursorType.cursorModes);
|
||||
}
|
||||
cursor.renderDeduplicated();
|
||||
}
|
||||
|
||||
private void removeGenericCursor() {
|
||||
if (cursor == null && !SWPlayer.of(player).hasComponent(Cursor.class)) {
|
||||
currentTargetServer = null;
|
||||
return;
|
||||
}
|
||||
|
||||
SWPlayer.of(player).removeComponent(Cursor.class);
|
||||
cursor = null;
|
||||
currentTargetServer = null;
|
||||
}
|
||||
|
||||
private void closeEmptyTargetServer() {
|
||||
if (emptyTargetServer == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
emptyTargetServer.close();
|
||||
emptyTargetServer = null;
|
||||
}
|
||||
|
||||
private void sendCursorActionbar(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 " + cursorType.name);
|
||||
}
|
||||
}
|
||||
|
||||
private void handlePlayerClick(Location cursorLocation, Optional<REntity> 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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMount(SWPlayer player) {
|
||||
this.player = player.getPlayer();
|
||||
refresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnmount(SWPlayer player) {
|
||||
boolean hadCursor = cursor != null || player.hasComponent(Cursor.class);
|
||||
removeGenericCursor();
|
||||
closeEmptyTargetServer();
|
||||
boolean watcherRemoved = SimulatorWatcher.show(null, this.player);
|
||||
if (hadCursor || watcherRemoved) {
|
||||
SWUtils.sendToActionbar(this.player, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 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) {
|
||||
boolean cursorRemoved = deactivateCursor(player);
|
||||
boolean watcherRemoved = SimulatorWatcher.show(null, player);
|
||||
if (cursorRemoved || watcherRemoved) {
|
||||
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 = activateOrRefresh(player);
|
||||
if (component == null) {
|
||||
return;
|
||||
}
|
||||
boolean shouldSwitch = component.handleSneak(System.currentTimeMillis());
|
||||
if (shouldSwitch) {
|
||||
component.switchCursorMode();
|
||||
}
|
||||
}
|
||||
|
||||
public static CursorType getCursorType(Player player) {
|
||||
return SWPlayer.of(player).getComponent(SimulatorCursorComponent.class)
|
||||
.map(SimulatorCursorComponent::getCursorType)
|
||||
.orElse(CursorType.TNT);
|
||||
}
|
||||
|
||||
public static void setCursorType(Player player, CursorType cursorType) {
|
||||
Optional<SimulatorCursorComponent> component = SWPlayer.of(player).getComponent(SimulatorCursorComponent.class);
|
||||
if (component.isPresent()) {
|
||||
component.get().setCursorType(cursorType);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Permission.BUILD.hasPermission(player) || !hasSimulatorItem(player)) {
|
||||
return;
|
||||
}
|
||||
SWPlayer.of(player).setComponent(new SimulatorCursorComponent(cursorType));
|
||||
}
|
||||
|
||||
public static void calcCursor(Player player) {
|
||||
activateOrRefresh(player);
|
||||
}
|
||||
|
||||
private static SimulatorCursorComponent activateOrRefresh(Player player) {
|
||||
if (!Permission.BUILD.hasPermission(player) || !hasSimulatorItem(player)) {
|
||||
boolean cursorRemoved = deactivateCursor(player);
|
||||
boolean watcherRemoved = SimulatorWatcher.show(null, player);
|
||||
if (cursorRemoved || watcherRemoved) {
|
||||
SWUtils.sendToActionbar(player, "");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
SWPlayer swPlayer = SWPlayer.of(player);
|
||||
Optional<SimulatorCursorComponent> existingComponent = swPlayer.getComponent(SimulatorCursorComponent.class);
|
||||
if (existingComponent.isPresent()) {
|
||||
SimulatorCursorComponent component = existingComponent.get();
|
||||
component.refresh();
|
||||
return component;
|
||||
}
|
||||
|
||||
SimulatorCursorComponent component = new SimulatorCursorComponent();
|
||||
swPlayer.setComponent(component);
|
||||
return component;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum CursorType {
|
||||
TNT(Material.TNT, Material.GUNPOWDER, List.of(Cursor.CursorMode.SURFACE_ALIGNED, 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<Cursor.CursorMode> cursorModes;
|
||||
public final String name;
|
||||
public final Function<Vector, SimulatorElement<?>> elementFunction;
|
||||
}
|
||||
|
||||
private static void openElement(Player player, Simulator simulator, REntity hitEntity) {
|
||||
Vector vector = new Vector(hitEntity.getX(), hitEntity.getY(), hitEntity.getZ());
|
||||
List<SimulatorElement<?>> 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<SimulatorGroup> 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);
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -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;
|
||||
|
||||
+7
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -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);
|
||||
|
||||
+2
-2
@@ -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<SimulatorGroup> {
|
||||
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();
|
||||
}));
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
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.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@Getter
|
||||
public class Cursor implements SWPlayer.Component {
|
||||
private final World WORLD = Bukkit.getWorlds().get(0);
|
||||
|
||||
private final REntityServer targetServer;
|
||||
private final Player owner;
|
||||
|
||||
private final AtomicBoolean isRendering = new AtomicBoolean(false);
|
||||
|
||||
private RFallingBlockEntity cursorEntity;
|
||||
private final REntityServer cursorServer;
|
||||
private Location cursorLocation;
|
||||
private REntity hitEntity;
|
||||
|
||||
@Setter
|
||||
private Material cursorMaterial;
|
||||
@Setter
|
||||
private List<CursorMode> allowedCursorModes;
|
||||
private final Material highlightMaterial;
|
||||
private final ClickHandler onClick;
|
||||
private final BiConsumer<Location, Optional<REntity>> onRender;
|
||||
|
||||
|
||||
public Cursor(REntityServer targetServer, Player owner, Material highlightMaterial, Material cursorMaterial, List<CursorMode> allowedModes, ClickHandler onClick) {
|
||||
this(targetServer, owner, highlightMaterial, cursorMaterial, allowedModes, onClick, (location, hitEntity) -> {
|
||||
});
|
||||
}
|
||||
|
||||
public Cursor(REntityServer targetServer, Player owner, Material highlightMaterial, Material cursorMaterial, List<CursorMode> allowedModes, ClickHandler onClick, BiConsumer<Location, Optional<REntity>> onRender) {
|
||||
this.targetServer = targetServer;
|
||||
this.owner = owner;
|
||||
this.highlightMaterial = highlightMaterial;
|
||||
this.cursorMaterial = cursorMaterial;
|
||||
this.allowedCursorModes = allowedModes;
|
||||
this.onClick = onClick;
|
||||
this.onRender = onRender;
|
||||
|
||||
cursorServer = new REntityServer();
|
||||
cursorServer.addPlayer(owner);
|
||||
|
||||
SWPlayer.of(owner).setComponent(this);
|
||||
}
|
||||
|
||||
public void renderDeduplicated() {
|
||||
if (!isRendering.getAndSet(true)) {
|
||||
render();
|
||||
isRendering.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void render() {
|
||||
RayTraceUtils.RRayTraceResult rayTraceResult = RayTraceUtils.traceREntity(owner, owner.getLocation(), targetServer.getEntities());
|
||||
if (rayTraceResult == null) {
|
||||
if (cursorEntity != null)
|
||||
cursorEntity.die();
|
||||
cursorEntity = null;
|
||||
cursorLocation = null;
|
||||
hitEntity = null;
|
||||
onRender.accept(null, Optional.empty());
|
||||
return;
|
||||
}
|
||||
|
||||
REntity hitEntity = rayTraceResult.getHitEntity() == cursorEntity ? null : rayTraceResult.getHitEntity();
|
||||
|
||||
|
||||
Material activeCursorMaterial = hitEntity == null ? cursorMaterial : highlightMaterial;
|
||||
CursorMode activeCursorMode = allowedCursorModes.stream().filter((mode) -> mode.isActive.test(owner)).min(Comparator.comparingInt(a -> a.priority))
|
||||
.orElse(CursorMode.BLOCK_ALIGNED);
|
||||
|
||||
Location activeCursorLocation = hitEntity == null ? activeCursorMode.positionTransform.apply(owner, rayTraceResult).toLocation(WORLD)
|
||||
: new Vector(hitEntity.getX(), hitEntity.getY(), hitEntity.getZ()).toLocation(WORLD);
|
||||
|
||||
cursorLocation = activeCursorLocation;
|
||||
this.hitEntity = hitEntity;
|
||||
|
||||
if (cursorEntity == null) {
|
||||
cursorEntity = new RFallingBlockEntity(cursorServer, activeCursorLocation, activeCursorMaterial);
|
||||
cursorEntity.setNoGravity(true);
|
||||
} else if (cursorEntity.getMaterial() == activeCursorMaterial) {
|
||||
cursorEntity.move(activeCursorLocation);
|
||||
} else {
|
||||
cursorEntity.die();
|
||||
cursorEntity = new RFallingBlockEntity(cursorServer, activeCursorLocation, activeCursorMaterial);
|
||||
cursorEntity.setNoGravity(true);
|
||||
if (activeCursorMaterial == highlightMaterial) {
|
||||
cursorEntity.setGlowing(true);
|
||||
}
|
||||
}
|
||||
onRender.accept(cursorLocation, Optional.ofNullable(hitEntity));
|
||||
}
|
||||
|
||||
protected void handlePlayerClick(Action clickAction) {
|
||||
renderDeduplicated();
|
||||
onClick.onClick(this.cursorLocation, Optional.ofNullable(this.hitEntity), clickAction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnmount(SWPlayer player) {
|
||||
cursorServer.close();
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
public enum CursorMode {
|
||||
FREE(1, (player, rayTraceResult) -> {
|
||||
Vector pos = rayTraceResult.getHitPosition();
|
||||
|
||||
BlockFace face = rayTraceResult.getHitBlockFace();
|
||||
if (face != null) {
|
||||
switch (face) {
|
||||
case DOWN:
|
||||
pos.setY(pos.getY() - 0.98);
|
||||
break;
|
||||
case EAST:
|
||||
pos.setX(pos.getX() + 0.49);
|
||||
break;
|
||||
case WEST:
|
||||
pos.setX(pos.getX() - 0.49);
|
||||
break;
|
||||
case NORTH:
|
||||
pos.setZ(pos.getZ() - 0.49);
|
||||
break;
|
||||
case SOUTH:
|
||||
pos.setZ(pos.getZ() + 0.49);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (face.getModY() == 0 && player.isSneaking()) {
|
||||
pos.setY(pos.getY() - 0.49);
|
||||
}
|
||||
}
|
||||
|
||||
return pos;
|
||||
}, Player::isSneaking),
|
||||
|
||||
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<Player, RayTraceUtils.RRayTraceResult, Vector> positionTransform;
|
||||
private final Predicate<Player> isActive;
|
||||
|
||||
private static Vector blockAlignedPosition(RayTraceUtils.RRayTraceResult 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;
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ClickHandler {
|
||||
void onClick(Location location, Optional<REntity> hitEntity, Action action);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
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;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
||||
@Linked
|
||||
public class CursorListener implements Listener {
|
||||
@Getter
|
||||
private static CursorListener instance;
|
||||
|
||||
public CursorListener() {
|
||||
if (instance == null) {
|
||||
instance = this;
|
||||
}
|
||||
|
||||
TinyProtocol.instance.addFilter(ServerboundMovePlayerPacket.Pos.class, this::updateCursorFromPacket);
|
||||
TinyProtocol.instance.addFilter(ServerboundMovePlayerPacket.Rot.class, this::updateCursorFromPacket);
|
||||
TinyProtocol.instance.addFilter(ServerboundMovePlayerPacket.PosRot.class, this::updateCursorFromPacket);
|
||||
|
||||
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) {
|
||||
SWPlayer swPlayer = SWPlayer.of(player);
|
||||
Optional<Cursor> activeCursor = swPlayer.getComponent(Cursor.class);
|
||||
|
||||
activeCursor.ifPresent(Cursor::renderDeduplicated);
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerInteract(PlayerInteractEvent event) {
|
||||
SWPlayer.of(event.getPlayer()).getComponent(Cursor.class).ifPresent(cursor -> {
|
||||
event.setCancelled(true);
|
||||
cursor.handlePlayerClick(event.getAction());
|
||||
});
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.bausystem.utils;
|
||||
package de.steamwar.cursor;
|
||||
|
||||
import de.steamwar.entity.REntity;
|
||||
import lombok.Data;
|
||||
@@ -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
|
||||
|
||||
@@ -42,11 +42,12 @@ dependencies {
|
||||
implementation(project(":CommandFramework"))
|
||||
}
|
||||
|
||||
tasks.register<DevServer>("DevVelocity") {
|
||||
tasks.register<VelocityServer>("DevVelocity") {
|
||||
group = "run"
|
||||
description = "Run a Dev Velocity"
|
||||
dependsOn(":VelocityCore:shadowJar")
|
||||
dependsOn(":VelocityCore:Persistent:jar")
|
||||
dependsOn(":VelocityCore:Dependencies:shadowJar")
|
||||
template = "DevVelocity"
|
||||
packetDecodeLogging=true
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user