Merge branch 'main' into BauSystem/RedstoneEngineCommand

This commit is contained in:
2026-06-10 09:03:55 +02:00
12 changed files with 546 additions and 253 deletions
@@ -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_ALREADY_EXISTS = §cThis skin already exists like this
SKIN_MESSAGE = §7Skin created SKIN_MESSAGE = §7Skin created
SKIN_MESSAGE_HOVER = §eClick to copy for YoyoNow and send 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
PANZERN_HELP = §8/§epanzern §8[§7Block§8] §8[§7Slab§8] §8- §7Armor your WorldEdit selection 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. PANZERN_PREPARE1 = §71. Check, if barrels reach until border of armor.
@@ -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_ALREADY_EXISTS = §cDieser Skin existiert in der Form bereits
SKIN_MESSAGE = §7Skin erstellt SKIN_MESSAGE = §7Skin erstellt
SKIN_MESSAGE_HOVER = §eKlicken zum kopieren für YoyoNow und an diesen senden 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
PANZERN_HELP = §8/§epanzern §8[§7Block§8] §8[§7Slab§8] §8- §7Panzer deine WorldEdit Auswahl 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. PANZERN_PREPARE1 = §71. Gucke nochmal nach, ob Läufe auch bis zur Panzergrenze führen.
@@ -19,7 +19,6 @@
package de.steamwar.bausystem.features.simulator; package de.steamwar.bausystem.features.simulator;
import com.comphenix.tinyprotocol.TinyProtocol;
import de.steamwar.bausystem.BauSystem; import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.Permission; import de.steamwar.bausystem.Permission;
import de.steamwar.bausystem.SWUtils; import de.steamwar.bausystem.SWUtils;
@@ -38,77 +37,88 @@ import de.steamwar.bausystem.features.simulator.gui.SimulatorGui;
import de.steamwar.bausystem.features.simulator.gui.base.SimulatorBaseGui; import de.steamwar.bausystem.features.simulator.gui.base.SimulatorBaseGui;
import de.steamwar.bausystem.utils.BauMemberUpdateEvent; import de.steamwar.bausystem.utils.BauMemberUpdateEvent;
import de.steamwar.bausystem.utils.ItemUtils; import de.steamwar.bausystem.utils.ItemUtils;
import de.steamwar.bausystem.utils.RayTraceUtils; import de.steamwar.core.SWPlayer;
import de.steamwar.cursor.Cursor;
import de.steamwar.entity.REntity; import de.steamwar.entity.REntity;
import de.steamwar.entity.REntityServer; import de.steamwar.entity.REntityServer;
import de.steamwar.entity.RFallingBlockEntity;
import de.steamwar.inventory.SWAnvilInv; import de.steamwar.inventory.SWAnvilInv;
import de.steamwar.linkage.Linked; import de.steamwar.linkage.Linked;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.block.Action; import org.bukkit.event.block.Action;
import org.bukkit.event.player.*; 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;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.player.PlayerToggleSneakEvent;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import java.util.*; import java.util.Collections;
import java.util.function.BiFunction; import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Linked @Linked
public class SimulatorCursor implements Listener { public class SimulatorCursor implements Listener {
private static final World WORLD = Bukkit.getWorlds().get(0); private static final Map<Player, CursorType> cursorType = Collections.synchronizedMap(new HashMap<>());
private static final Map<Player, REntityServer> emptyTargetServers = Collections.synchronizedMap(new HashMap<>());
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) { public static boolean isSimulatorItem(ItemStack itemStack) {
return ItemUtils.isItem(itemStack, "simulator"); return ItemUtils.isItem(itemStack, "simulator");
} }
public SimulatorCursor() { private static boolean hasSimulatorItem(Player player) {
BiFunction<Player, ServerboundMovePlayerPacket, Object> function = (player, object) -> { return isSimulatorItem(player.getInventory().getItemInMainHand()) || isSimulatorItem(player.getInventory().getItemInOffHand());
calcCursor(player); }
return object;
}; private static void scheduleCursorUpdate(Player player) {
TinyProtocol.instance.addFilter(ServerboundMovePlayerPacket.Pos.class, function); BauSystem.runTaskLater(BauSystem.getInstance(), () -> calcCursor(player), 1);
TinyProtocol.instance.addFilter(ServerboundMovePlayerPacket.Rot.class, function);
TinyProtocol.instance.addFilter(ServerboundMovePlayerPacket.PosRot.class, function);
} }
@EventHandler @EventHandler
public void onPlayerJoin(PlayerJoinEvent event) { public void onPlayerJoin(PlayerJoinEvent event) {
if (!Permission.BUILD.hasPermission(event.getPlayer())) return; if (!Permission.BUILD.hasPermission(event.getPlayer())) return;
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> { scheduleCursorUpdate(event.getPlayer());
calcCursor(event.getPlayer());
}, 0);
} }
@EventHandler @EventHandler
public void onPlayerDropItem(PlayerDropItemEvent event) { public void onPlayerDropItem(PlayerDropItemEvent event) {
if (!Permission.BUILD.hasPermission(event.getPlayer())) return; if (!Permission.BUILD.hasPermission(event.getPlayer())) return;
calcCursor(event.getPlayer()); scheduleCursorUpdate(event.getPlayer());
} }
@EventHandler @EventHandler
public void onPlayerItemHeld(PlayerItemHeldEvent event) { public void onPlayerItemHeld(PlayerItemHeldEvent event) {
if (!Permission.BUILD.hasPermission(event.getPlayer())) return; if (!Permission.BUILD.hasPermission(event.getPlayer())) return;
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> { scheduleCursorUpdate(event.getPlayer());
calcCursor(event.getPlayer()); }
}, 1);
@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 @EventHandler
@@ -119,10 +129,7 @@ public class SimulatorCursor implements Listener {
@EventHandler @EventHandler
public void onPlayerQuit(PlayerQuitEvent event) { public void onPlayerQuit(PlayerQuitEvent event) {
cursorType.remove(event.getPlayer()); cursorType.remove(event.getPlayer());
cursors.remove(event.getPlayer()); removeCursor(event.getPlayer());
synchronized (calculating) {
calculating.remove(event.getPlayer());
}
} }
private static final Map<Player, Long> LAST_SNEAKS = new HashMap<>(); private static final Map<Player, Long> LAST_SNEAKS = new HashMap<>();
@@ -138,7 +145,7 @@ public class SimulatorCursor implements Listener {
public void onPlayerToggleSneak(PlayerToggleSneakEvent event) { public void onPlayerToggleSneak(PlayerToggleSneakEvent event) {
if (!event.isSneaking()) return; if (!event.isSneaking()) return;
Player player = event.getPlayer(); Player player = event.getPlayer();
if (!isSimulatorItem(player.getInventory().getItemInMainHand()) && !isSimulatorItem(player.getInventory().getItemInOffHand())) { if (!hasSimulatorItem(player)) {
return; return;
} }
if (LAST_SNEAKS.containsKey(player)) { if (LAST_SNEAKS.containsKey(player)) {
@@ -164,17 +171,10 @@ public class SimulatorCursor implements Listener {
} }
public static void calcCursor(Player player) { public static void calcCursor(Player player) {
synchronized (calculating) { if (!Permission.BUILD.hasPermission(player) || !hasSimulatorItem(player)) {
if (calculating.contains(player)) return; if (removeCursor(player) | SimulatorWatcher.show(null, player)) {
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, ""); SWUtils.sendToActionbar(player, "");
} }
synchronized (calculating) {
calculating.remove(player);
}
return; return;
} }
@@ -183,203 +183,98 @@ public class SimulatorCursor implements Listener {
removeCursor(player); removeCursor(player);
SimulatorWatcher.show(null, player); SimulatorWatcher.show(null, player);
SWUtils.sendToActionbar(player, "§cGenerating Stab"); SWUtils.sendToActionbar(player, "§cGenerating Stab");
synchronized (calculating) {
calculating.remove(player);
}
return; return;
} }
SimulatorWatcher.show(simulator, player); SimulatorWatcher.show(simulator, player);
List<REntity> entities = SimulatorWatcher.getEntitiesOfSimulator(simulator); Cursor cursor = getOrCreateCursor(player, simulator, cursorType.getOrDefault(player, CursorType.TNT));
RayTraceUtils.RRayTraceResult rayTraceResult = RayTraceUtils.traceREntity(player, player.getLocation(), entities); cursor.renderDeduplicated();
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); private static Cursor getOrCreateCursor(Player player, Simulator simulator, CursorType type) {
synchronized (calculating) { REntityServer targetServer = simulator == null ? emptyTargetServers.computeIfAbsent(player, __ -> new REntityServer()) : SimulatorWatcher.getEntityServerOfSimulator(simulator);
calculating.remove(player); SWPlayer swPlayer = SWPlayer.of(player);
Optional<Cursor> activeCursor = swPlayer.getComponent(Cursor.class);
Cursor cursor = activeCursor.orElse(null);
if (cursor == null || cursor.getTargetServer() != targetServer) {
swPlayer.removeComponent(Cursor.class);
cursor = new Cursor(
targetServer,
player,
Material.GLASS,
type.material,
type.cursorModes,
(location, hitEntity, action) -> handlePlayerClick(player, location, hitEntity, action),
(location, hitEntity) -> sendCursorActionbar(player, SimulatorStorage.getSimulator(player), location != null, hitEntity.isPresent())
);
} else {
cursor.setCursorMaterial(type.material);
cursor.setAllowedCursorModes(type.cursorModes);
} }
return cursor;
} }
private static synchronized boolean removeCursor(Player player) { private static synchronized boolean removeCursor(Player player) {
REntityServer entityServer = cursors.get(player); Optional<Cursor> cursor = SWPlayer.of(player).getComponent(Cursor.class);
boolean hadCursor = entityServer != null && !entityServer.getEntities().isEmpty(); cursor.ifPresent(__ -> SWPlayer.of(player).removeComponent(Cursor.class));
if (entityServer != null) { REntityServer emptyTargetServer = emptyTargetServers.remove(player);
entityServer.getEntities().forEach(REntity::die); if (emptyTargetServer != null) {
emptyTargetServer.close();
} }
return hadCursor; return cursor.isPresent();
} }
private static synchronized void showCursor(Player player, RayTraceUtils.RRayTraceResult rayTraceResult, boolean hasSimulatorSelected) { private static void sendCursorActionbar(Player player, Simulator simulator, boolean hasCursorLocation, boolean hasHitEntity) {
REntityServer entityServer = cursors.computeIfAbsent(player, __ -> { if (!hasCursorLocation) {
REntityServer rEntityServer = new REntityServer(); SWUtils.sendToActionbar(player, simulator == null ? "§eSelect Simulator" : "§eOpen Simulator");
rEntityServer.addPlayer(player); } else if (simulator == null) {
return rEntityServer; SWUtils.sendToActionbar(player, "§eCreate new Simulator");
}); } else if (hasHitEntity) {
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"); SWUtils.sendToActionbar(player, "§eEdit Position");
} else { } else {
SWUtils.sendToActionbar(player, "§eAdd new " + type.name); SWUtils.sendToActionbar(player, "§eAdd new " + cursorType.getOrDefault(player, CursorType.TNT).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 @Getter
@AllArgsConstructor @AllArgsConstructor
public enum CursorType { public enum CursorType {
TNT(Material.TNT, Material.GUNPOWDER, SimulatorCursor::getPosFree, "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, SimulatorCursor::getPosBlockAligned, "Redstone Block", vector -> new RedstoneElement(vector).add(new RedstonePhase())), 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, SimulatorCursor::getPosBlockAligned, "Observer", vector -> new ObserverElement(vector).add(new ObserverPhase())), 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 material;
public final Material nonSelectedMaterial; public final Material nonSelectedMaterial;
public final BiFunction<Player, RayTraceUtils.RRayTraceResult, Vector> position; public final List<Cursor.CursorMode> cursorModes;
public final String name; public final String name;
public final Function<Vector, SimulatorElement<?>> elementFunction; public final Function<Vector, SimulatorElement<?>> elementFunction;
} }
@EventHandler private static void handlePlayerClick(Player player, Location cursorLocation, Optional<REntity> hitEntity, Action action) {
public void onPlayerInteract(PlayerInteractEvent event) { if (!Permission.BUILD.hasPermission(player)) return;
if (!Permission.BUILD.hasPermission(event.getPlayer())) return; if (!hasSimulatorItem(player)) {
if (!ItemUtils.isItem(event.getItem(), "simulator")) {
return; return;
} }
event.setCancelled(true);
Player player = event.getPlayer();
Simulator simulator = SimulatorStorage.getSimulator(player); 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) { if (simulator == null) {
return; return;
} }
SimulatorExecutor.run(event.getPlayer(), simulator, null); SimulatorExecutor.run(player, simulator, null);
return; 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; return;
} }
RayTraceUtils.RRayTraceResult rayTraceResult = RayTraceUtils.traceREntity(player, player.getLocation(), SimulatorWatcher.getEntitiesOfSimulator(simulator));
if (simulator == null) { if (simulator == null) {
if (rayTraceResult == null) { if (cursorLocation == null) {
SimulatorStorage.openSimulatorSelector(player); SimulatorStorage.openSimulatorSelector(player);
} else { } else {
SWAnvilInv anvilInv = new SWAnvilInv(player, "Name"); SWAnvilInv anvilInv = new SWAnvilInv(player, "Name");
@@ -395,7 +290,7 @@ public class SimulatorCursor implements Listener {
} }
sim = new Simulator(s); sim = new Simulator(s);
SimulatorStorage.addSimulator(s, sim); SimulatorStorage.addSimulator(s, sim);
createElement(player, rayTraceResult, sim); createElement(player, cursorLocation, sim);
SimulatorStorage.setSimulator(player, sim); SimulatorStorage.setSimulator(player, sim);
}); });
anvilInv.open(); anvilInv.open();
@@ -403,13 +298,20 @@ public class SimulatorCursor implements Listener {
return; return;
} }
if (rayTraceResult == null) { if (cursorLocation == null) {
new SimulatorGui(player, simulator).open(); new SimulatorGui(player, simulator).open();
return; return;
} }
if (rayTraceResult.getHitEntity() != null) { if (hitEntity.isPresent()) {
REntity hitEntity = rayTraceResult.getHitEntity(); 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()); Vector vector = new Vector(hitEntity.getX(), hitEntity.getY(), hitEntity.getZ());
List<SimulatorElement<?>> elements = simulator.getGroups().stream().map(SimulatorGroup::getElements).flatMap(List::stream).filter(element -> { 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); return element.getWorldPos().distanceSquared(vector) < (1 / 16.0) * (1 / 16.0);
@@ -419,7 +321,6 @@ public class SimulatorCursor implements Listener {
case 0: case 0:
return; return;
case 1: case 1:
// Open single element present in Simulator
SimulatorElement<?> element = elements.get(0); SimulatorElement<?> element = elements.get(0);
SimulatorGroup group1 = element.getGroup(simulator); SimulatorGroup group1 = element.getGroup(simulator);
SimulatorBaseGui back = new SimulatorGui(player, simulator); SimulatorBaseGui back = new SimulatorGui(player, simulator);
@@ -431,11 +332,9 @@ public class SimulatorCursor implements Listener {
default: default:
List<SimulatorGroup> parents = elements.stream().map(e -> e.getGroup(simulator)).distinct().collect(Collectors.toList()); List<SimulatorGroup> parents = elements.stream().map(e -> e.getGroup(simulator)).distinct().collect(Collectors.toList());
if (parents.size() == 1) { if (parents.size() == 1) {
// Open multi element present in Simulator in existing group
SimulatorGui simulatorGui = new SimulatorGui(player, simulator); SimulatorGui simulatorGui = new SimulatorGui(player, simulator);
new SimulatorGroupGui(player, simulator, parents.get(0), simulatorGui).open(); new SimulatorGroupGui(player, simulator, parents.get(0), simulatorGui).open();
} else { } else {
// Open multi element present in Simulator in implicit group
SimulatorGroup group2 = new SimulatorGroup(); SimulatorGroup group2 = new SimulatorGroup();
group2.setMaterial(null); group2.setMaterial(null);
group2.getElements().addAll(elements); group2.getElements().addAll(elements);
@@ -444,16 +343,11 @@ public class SimulatorCursor implements Listener {
} }
break; break;
} }
return;
} }
// Add new Element to current simulator private static void createElement(Player player, Location cursorLocation, Simulator simulator) {
createElement(player, rayTraceResult, simulator);
}
private void createElement(Player player, RayTraceUtils.RRayTraceResult rayTraceResult, Simulator simulator) {
CursorType type = cursorType.getOrDefault(player, CursorType.TNT); CursorType type = cursorType.getOrDefault(player, CursorType.TNT);
Vector vector = type.position.apply(player, rayTraceResult); Vector vector = cursorLocation.toVector();
if (type == CursorType.REDSTONE_BLOCK) { if (type == CursorType.REDSTONE_BLOCK) {
vector.subtract(new Vector(0.5, 0, 0.5)); vector.subtract(new Vector(0.5, 0, 0.5));
} }
@@ -124,4 +124,11 @@ public class SimulatorWatcher {
} }
return entityServer.getEntities(); return entityServer.getEntities();
} }
synchronized REntityServer getEntityServerOfSimulator(Simulator simulator) {
if (simulator == null) {
return null;
}
return entityServers.computeIfAbsent(simulator, __ -> createSim(new REntityServer(), simulator));
}
} }
@@ -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 <https://www.gnu.org/licenses/>.
*/
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(@Validator 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());
}
}
@@ -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());
});
}
}
@@ -1,7 +1,7 @@
/* /*
* This file is a part of the SteamWar software. * 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 * 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 * 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/>. * 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 de.steamwar.entity.REntity;
import lombok.Data; import lombok.Data;
@@ -152,7 +152,7 @@ public class TechHider {
ClientboundSetHeldSlotPacket.class, // 7.1.104 Set Held Item (Player owning the channel) ClientboundSetHeldSlotPacket.class, // 7.1.104 Set Held Item (Player owning the channel)
ClientboundSetObjectivePacket.class, // 7.1.105 Update Objectives ClientboundSetObjectivePacket.class, // 7.1.105 Update Objectives
ClientboundSetPlayerInventoryPacket.class, // 7.1.107 Set Player Inventory Slot (Player owning the channel) 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 ClientboundSetScorePacket.class, // 7.1.109 Update Score
ClientboundSetSimulationDistancePacket.class, // 7.1.110 Set Simulation Distance ClientboundSetSimulationDistancePacket.class, // 7.1.110 Set Simulation Distance
ClientboundSetSubtitleTextPacket.class, // 7.1.111 Set Subtitle Text ClientboundSetSubtitleTextPacket.class, // 7.1.111 Set Subtitle Text
+2 -1
View File
@@ -42,11 +42,12 @@ dependencies {
implementation(project(":CommandFramework")) implementation(project(":CommandFramework"))
} }
tasks.register<DevServer>("DevVelocity") { tasks.register<VelocityServer>("DevVelocity") {
group = "run" group = "run"
description = "Run a Dev Velocity" description = "Run a Dev Velocity"
dependsOn(":VelocityCore:shadowJar") dependsOn(":VelocityCore:shadowJar")
dependsOn(":VelocityCore:Persistent:jar") dependsOn(":VelocityCore:Persistent:jar")
dependsOn(":VelocityCore:Dependencies:shadowJar") dependsOn(":VelocityCore:Dependencies:shadowJar")
template = "DevVelocity" template = "DevVelocity"
packetDecodeLogging=true
} }
@@ -42,13 +42,6 @@ public class ReplayCommand extends SWCommand {
super("replay"); super("replay");
} }
@Register
public void genericCommand(PlayerChatter sender) {
sender.system("REPLAY_UNAVAILABLE");
return;
}
/*
@Register @Register
public void genericCommand(PlayerChatter sender, int replayId, @StaticValue(value = {"", "-a"}, allowISE = true) @OptionalValue("") boolean isAdmin, @OptionalValue("") String optionalMap) { public void genericCommand(PlayerChatter sender, int replayId, @StaticValue(value = {"", "-a"}, allowISE = true) @OptionalValue("") boolean isAdmin, @OptionalValue("") String optionalMap) {
Fight fight = Fight.getById(replayId); Fight fight = Fight.getById(replayId);
@@ -64,6 +57,9 @@ public class ReplayCommand extends SWCommand {
@Register @Register
public void genericCommand(PlayerChatter sender, @OptionalValue("") String optionalMap) { public void genericCommand(PlayerChatter sender, @OptionalValue("") String optionalMap) {
if (PunishmentCommand.isPunishedWithMessage(sender, Punishment.PunishmentType.NoFightServer)) return; if (PunishmentCommand.isPunishedWithMessage(sender, Punishment.PunishmentType.NoFightServer)) return;
if (!sender.user().hasPerm(UserPerm.TEAM)) return;
new SWStreamInv<>(sender, new Message("REPLAY_TITLE"), (click, fight) -> { new SWStreamInv<>(sender, new Message("REPLAY_TITLE"), (click, fight) -> {
startReplay(sender, click.isShiftClick(), optionalMap, 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) { private void startReplay(PlayerChatter sender, boolean isAdmin, String optionalMap, Fight fight) {
if (PunishmentCommand.isPunishedWithMessage(sender, Punishment.PunishmentType.NoFightServer)) return; if (PunishmentCommand.isPunishedWithMessage(sender, Punishment.PunishmentType.NoFightServer)) return;
if (!sender.user().hasPerm(UserPerm.TEAM)) return;
GameModeConfig<String, String> mode = ArenaMode.getBySchemType(fight.getSchemType()); GameModeConfig<String, String> mode = ArenaMode.getBySchemType(fight.getSchemType());
ServerStarter starter = new ServerStarter().replay(fight.getFightID()).blueLeader(sender.getPlayer()); 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) { private Message parseLeader(SteamwarUser leader, int players, boolean winner) {
return new Message("REPLAY_" + (players > 1 ? "" : "SOLO_") + (winner ? "WINNER" : "LOSER"), leader.getUserName(), players - 1); return new Message("REPLAY_" + (players > 1 ? "" : "SOLO_") + (winner ? "WINNER" : "LOSER"), leader.getUserName(), players - 1);
} }
*/
} }
+19 -2
View File
@@ -26,6 +26,10 @@ plugins {
class DevServer extends DefaultTask { class DevServer extends DefaultTask {
@Input
@Optional
boolean debug = false
@Input @Input
@Optional @Optional
String worldName = null String worldName = null
@@ -95,7 +99,7 @@ class DevServer extends DefaultTask {
doLast { doLast {
setupTemplate(template) setupTemplate(template)
uploadDependencies() uploadDependencies()
startDebugPort() if (debug) startDebugPort()
startDevServer() startDevServer()
} }
finalizedBy(new Finalizer()) finalizedBy(new Finalizer())
@@ -241,7 +245,7 @@ class DevServer extends DefaultTask {
devPy.append(" -D${dParam.key}=${dParam.value}") devPy.append(" -D${dParam.key}=${dParam.value}")
} }
devPy.append(" $template") 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") if (jvmArgs != null) devPy.append(" $jvmArgs")
println("Starting $template with command ${devPy.toString()}") println("Starting $template with command ${devPy.toString()}")
@@ -278,6 +282,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 { class FightServer extends DevServer {
@Input @Input