Compare commits

..

22 Commits

Author SHA1 Message Date
YoyoNow e176b3bca8 Fix steamwar.devserver.gradle 'worldName' parameter
Deploy / Build (push) Successful in 2m30s
Deploy / Deploy (push) Successful in 11s
2026-06-10 20:46:13 +02:00
YoyoNow f81f05c3d0 Merge pull request 'Add RedstoneEngineCommand' (#406) from BauSystem/RedstoneEngineCommand into main
Deploy / Build (push) Successful in 2m15s
Deploy / Deploy (push) Successful in 11s
Reviewed-on: #406
Reviewed-by: D4rkr34lm <dark@steamwar.de>
2026-06-10 09:04:15 +02:00
YoyoNow 0afb6ce06a Merge branch 'main' into BauSystem/RedstoneEngineCommand
Backport CommonCore / Create CommonCore backport PRs (pull_request) Successful in 14s
Pull Request Build / Build (pull_request) Successful in 1m24s
2026-06-10 09:03:55 +02:00
YoyoNow a938abde3f Add 'debug' property to enable/disable debugger
Deploy / Build (push) Successful in 2m21s
Deploy / Deploy (push) Successful in 10s
2026-06-09 23:00:46 +02:00
YoyoNow ec9b0387c5 Fix permission for BlastResistanceCommand
Deploy / Build (push) Successful in 2m0s
Deploy / Deploy (push) Successful in 12s
2026-06-09 16:42:40 +02:00
YoyoNow 03c3d49659 Add BlastResistanceCommand
Deploy / Build (push) Successful in 2m10s
Deploy / Deploy (push) Successful in 11s
2026-06-09 16:38:22 +02:00
Chaoscaot 8d9a77ab67 Merge pull request 'chore(VelocityCore): reennable replays for serverteam' (#417) from wip/VelocityCore-reenable-replays-for-serverteam into main
Deploy / Build (push) Successful in 1m57s
Deploy / Deploy (push) Successful in 13s
Reviewed-on: #417
Reviewed-by: Chaoscaot <max@chaoscaot.de>
2026-06-07 00:06:07 +02:00
D4rkr34lm fc2997e011 Rennable replays for serverteam
Pull Request Build / Build (pull_request) Successful in 1m13s
Backport CommonCore / Create CommonCore backport PRs (pull_request) Successful in 7s
2026-06-06 12:17:23 +02:00
D4rkr34lm 8eb5f5ddf2 fix(BauSystem): cursor not properly disapearing
Deploy / Build (push) Successful in 1m57s
Deploy / Deploy (push) Successful in 11s
2026-06-03 21:23:36 +02:00
YoyoNow aa807060f4 Merge pull request 'fix(SpigotCore): small collection of rendering bugs in new cursor' (#413) from BauSystem/fix-small-sim-cursor-bug into main
Deploy / Build (push) Successful in 1m58s
Deploy / Deploy (push) Successful in 11s
Reviewed-on: #413
Reviewed-by: YoyoNow <4+yoyonow@noreply.localhost>
2026-06-02 21:39:13 +02:00
D4rkr34lm 9bbbab9d4b Fix collection of rendering issues
Pull Request Build / Build (pull_request) Successful in 1m8s
Backport CommonCore / Create CommonCore backport PRs (pull_request) Successful in 7s
2026-06-02 21:33:31 +02:00
YoyoNow 7cca4ada10 Merge pull request 'refactor(BauSystem): Extract sim cursor into generic curser and refactor sim cursor' (#410) from BauSystem/refactor-sim-cursor-no2 into main
Deploy / Build (push) Successful in 2m21s
Deploy / Deploy (push) Successful in 11s
Reviewed-on: #410
Reviewed-by: YoyoNow <4+yoyonow@noreply.localhost>
2026-06-02 20:22:00 +02:00
YoyoNow 725e6df047 Merge pull request 'fix(SpigotCore): versions above 1.21.6 being kicked from arena' (#412) from SpigotCore/fix-newer-version-being-kicked-from-arena into main
Deploy / Build (push) Successful in 2m52s
Deploy / Deploy (push) Successful in 11s
Reviewed-on: #412
Reviewed-by: YoyoNow <4+yoyonow@noreply.localhost>
2026-06-02 20:19:57 +02:00
D4rkr34lm f3ce124a23 Make dev velocity easier to debug and fix version issue
Backport CommonCore / Create CommonCore backport PRs (pull_request) Successful in 12s
Pull Request Build / Build (pull_request) Successful in 1m42s
2026-06-02 20:18:13 +02:00
D4rkr34lm e83f72a53e Further simplification
Pull Request Build / Build (pull_request) Successful in 1m11s
Backport CommonCore / Create CommonCore backport PRs (pull_request) Successful in 12s
2026-06-01 22:01:52 +02:00
D4rkr34lm def0c19e43 Refactor simulation cursor to use new generic cursor
Pull Request Build / Build (pull_request) Successful in 1m9s
2026-06-01 21:51:54 +02:00
D4rkr34lm 3810ccd63d Add proper onclick handleing 2026-06-01 21:27:34 +02:00
D4rkr34lm a018af1c8a Refactor to use Player components for better usability 2026-05-31 13:20:24 +02:00
YoyoNow fd8f942014 Fix RedstoneEngine not registering
Pull Request Build / Build (pull_request) Successful in 1m8s
2026-05-31 11:37:43 +02:00
YoyoNow eab8826583 Fix RedstoneEngine not registering
Pull Request Build / Build (pull_request) Successful in 1m9s
2026-05-31 11:36:21 +02:00
YoyoNow bc0177df43 Add ExperimentalCommand
Pull Request Build / Build (pull_request) Successful in 1m7s
2026-05-31 00:56:05 +02:00
YoyoNow 1dc78b8eb8 Add RedstoneEngineCommand
Pull Request Build / Build (pull_request) Successful in 1m7s
2026-05-31 00:44:56 +02:00
47 changed files with 973 additions and 1857 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_MESSAGE = §7Skin created
SKIN_MESSAGE_HOVER = §eClick to copy for YoyoNow and send
# Blast Resistance
BLASTRESISTANCE_HELP = §8/§eblastresistance §8-§7 Calculate min/max and average blast resistance of current clipboard
BLASTRESISTANCE_NO_CLIPBOARD = §cYou currently do not have a clipboard to be used.
BLASTRESISTANCE_RESULT = §7BlastResistance §8>>§7 Min§8: §e{0}§7 Max§8: §e{1}§7 Avg§8: §e{2}
# Panzern
PANZERN_HELP = §8/§epanzern §8[§7Block§8] §8[§7Slab§8] §8- §7Armor your WorldEdit selection
PANZERN_PREPARE1 = §71. Check, if barrels reach until border of armor.
@@ -772,6 +772,10 @@ SKIN_NO_REGION = §7Du steht in keiner Region, welche mit einem Skin versehen we
SKIN_ALREADY_EXISTS = §cDieser Skin existiert in der Form bereits
SKIN_MESSAGE = §7Skin erstellt
SKIN_MESSAGE_HOVER = §eKlicken zum kopieren für YoyoNow und an diesen senden
# Blast Resistance
BLASTRESISTANCE_HELP = §8/§eblastresistance §8-§7 Minimal-, Maximal- und durchschnittliche Sprengfestigkeit des aktuellen Inhalts der Zwischenablage berechnen
BLASTRESISTANCE_NO_CLIPBOARD = §cDerzeit steht Ihnen keine Zwischenablage zur Verfügung.
BLASTRESISTANCE_RESULT = §7BlastResistance §8>>§7 Min§8: §e{0}§7 Max§8: §e{1}§7 Avg§8: §e{2}
# Panzern
PANZERN_HELP = §8/§epanzern §8[§7Block§8] §8[§7Slab§8] §8- §7Panzer deine WorldEdit Auswahl
PANZERN_PREPARE1 = §71. Gucke nochmal nach, ob Läufe auch bis zur Panzergrenze führen.
@@ -133,12 +133,7 @@ public class BauSystem extends JavaPlugin implements Listener {
Bukkit.getWorlds().get(0).setGameRule(GameRule.SEND_COMMAND_FEEDBACK, false);
String identifier;
if (BauServerInfo.getWorldId() != null) {
identifier = BauServerInfo.getWorldId().toString().replace("-", "");
} else {
identifier = BauServerInfo.getOwnerUser().getUUID().toString().replace("-", "");
}
String identifier = BauServerInfo.getOwnerUser().getUUID().toString().replace("-", "");
WorldIdentifier.set("bau/" + Core.getVersion() + "/" + identifier);
}
@@ -229,4 +224,4 @@ public class BauSystem extends JavaPlugin implements Listener {
AtomicReference<BukkitTask> task = new AtomicReference<>();
task.set(runTaskTimer(plugin, () -> consumer.accept(task.get()), delay, period));
}
}
}
@@ -60,16 +60,11 @@ public enum Permission {
}
public boolean hasPermission(Player member) {
SteamwarUser steamwarUser = SteamwarUser.get(member.getUniqueId());
BauServer server = BauServer.getInstance();
if (!server.isTeamWorld() && steamwarUser.getId() == server.getOwnerID()) {
if (SteamwarUser.get(member.getUniqueId()).getId() == BauServer.getInstance().getOwnerID()) {
return this != SPECTATOR;
}
if (server.isTeamWorld() && steamwarUser.getTeam() == server.getTeamID()) {
return this != SPECTATOR;
}
BauweltMember bauweltMember = BauweltMember.getBauMember(server.getWorldID(), steamwarUser.getId());
BauweltMember bauweltMember = BauweltMember.getBauMember(BauServer.getInstance().getOwner(), member.getUniqueId());
if (bauweltMember == null) return this == SPECTATOR;
return permissionPredicate.test(bauweltMember);
}
}
}
@@ -34,41 +34,16 @@ public class BauServer {
}
private Integer owner;
private UUID world;
private Integer team;
public UUID getOwner() {
Integer ownerId = getOwnerID();
return ownerId == null ? null : SteamwarUser.byId(ownerId).getUUID();
return SteamwarUser.byId(getOwnerID()).getUUID();
}
public Integer getOwnerID() {
public int getOwnerID() {
//Lazy loading to improve startup time of the server in 1.15
if (owner == null) {
owner = BauServerInfo.getOwnerId();
}
return owner;
}
public boolean hasOwner() {
return getOwnerID() != null;
}
public UUID getWorldID() {
if (world == null) {
world = BauServerInfo.getWorldId();
}
return world;
}
public Integer getTeamID() {
if (team == null) {
team = BauServerInfo.getTeamId();
}
return team;
}
public boolean isTeamWorld() {
return getTeamID() != null;
}
}
}
@@ -47,7 +47,7 @@ public class BauInfoBauGuiItem extends BauGuiItem {
@Override
public ItemStack getItem(Player player) {
SWItem itemStack;
if (!player.getName().endsWith("") && !BauServer.getInstance().isTeamWorld()) {
if (!player.getName().endsWith("")) {
itemStack = SWItem.getPlayerSkull(SteamwarUser.get(BauServer.getInstance().getOwner()).getUserName());
} else {
itemStack = new SWItem(Material.PLAYER_HEAD, "");
@@ -47,9 +47,7 @@ public class InfoCommand extends SWCommand {
@Register(description = "BAU_INFO_COMMAND_HELP")
public void genericCommand(Player p) {
if (!bauServer.isTeamWorld()) {
BauSystem.MESSAGE.send("BAU_INFO_COMMAND_OWNER", p, SteamwarUser.byId(bauServer.getOwnerID()).getUserName());
}
BauSystem.MESSAGE.send("BAU_INFO_COMMAND_OWNER", p, SteamwarUser.byId(bauServer.getOwnerID()).getUserName());
Region region = Region.getRegion(p.getLocation());
for (Flag flag : Flag.getFlags()) {
if (!region.getRegionData().has(flag).isApplicable()) continue;
@@ -60,7 +58,7 @@ public class InfoCommand extends SWCommand {
}
if (Permission.BUILD.hasPermission(p)) {
List<BauweltMember> members = BauweltMember.getWorldMembers(bauServer.getWorldID());
List<BauweltMember> members = BauweltMember.getMembers(bauServer.getOwnerID());
Map<Permission, List<BauweltMember>> memberByPermission = new HashMap<>();
members.forEach(member -> {
if (Permission.SUPERVISOR.hasPermission(member)) {
@@ -0,0 +1,31 @@
/*
* 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.experimental;
import de.steamwar.command.SWCommand;
import de.steamwar.linkage.Linked;
@Linked
public class ExperimentalCommand extends SWCommand {
public ExperimentalCommand() {
super("experimental", "experiment");
}
}
@@ -0,0 +1,135 @@
/*
* 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.experimental.redstone_engine;
import de.steamwar.bausystem.features.experimental.ExperimentalCommand;
import de.steamwar.bausystem.region.Region;
import de.steamwar.bausystem.utils.ScoreboardElement;
import de.steamwar.command.AbstractSWCommand;
import de.steamwar.command.SWCommand;
import de.steamwar.linkage.Linked;
import io.papermc.paper.configuration.WorldConfiguration;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.title.TitlePart;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import java.util.Collection;
import java.util.List;
@AbstractSWCommand.PartOf(ExperimentalCommand.class)
@Linked
public class RedstoneEngine extends SWCommand implements Listener, ScoreboardElement {
public RedstoneEngine() {
super("");
}
private WorldConfiguration.Misc getConfig() {
return ((CraftWorld) Bukkit.getWorlds().get(0)).getHandle().paperConfig().misc;
}
@Register("redstone")
@Register("redstoneengine")
public void setRedstoneEngine(Player player, @StaticValue("alternate_current") String __, WorldConfiguration.Misc.AlternateCurrentUpdateOrder updateOrder) {
WorldConfiguration.Misc misc = getConfig();
misc.redstoneImplementation = WorldConfiguration.Misc.RedstoneImplementation.ALTERNATE_CURRENT;
misc.alternateCurrentUpdateOrder = updateOrder;
broadcastTitle(Bukkit.getOnlinePlayers());
}
@Register("redstone")
@Register("redstoneengine")
public void setRedstoneEngine(Player player, WorldConfiguration.Misc.RedstoneImplementation implementation) {
getConfig().redstoneImplementation = implementation;
broadcastTitle(Bukkit.getOnlinePlayers());
}
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
WorldConfiguration.Misc misc = getConfig();
if (misc.redstoneImplementation != WorldConfiguration.Misc.RedstoneImplementation.EIGENCRAFT) {
broadcastTitle(List.of(event.getPlayer()));
}
}
private void broadcastTitle(Collection<? extends Player> players) {
WorldConfiguration.Misc misc = getConfig();
Component title = switch (misc.redstoneImplementation) {
case VANILLA -> Component.text("").color(NamedTextColor.RED).append(Component.text(" Redstone: Vanilla ").color(NamedTextColor.WHITE)).append(Component.text("").color(NamedTextColor.RED));
case EIGENCRAFT -> Component.text("Redstone: Eigencraft");
case ALTERNATE_CURRENT -> Component.text("").color(NamedTextColor.RED).append(Component.text(" Redstone: AC ").color(NamedTextColor.WHITE)).append(Component.text("").color(NamedTextColor.RED));
};
Component subtitle;
if (misc.redstoneImplementation != WorldConfiguration.Misc.RedstoneImplementation.ALTERNATE_CURRENT) {
subtitle = Component.text().build();
} else {
subtitle = switch (misc.alternateCurrentUpdateOrder) {
case VERTICAL_FIRST_INWARD -> Component.text("Y first inwards"); // Y before XZ
case VERTICAL_FIRST_OUTWARD -> Component.text("Y first outwards"); // XZ before Y
case HORIZONTAL_FIRST_INWARD -> Component.text("XZ first inwards"); // Y before XZ
case HORIZONTAL_FIRST_OUTWARD -> Component.text("XZ first outwards"); // XZ before Y
};
}
players.forEach(player -> {
player.sendTitlePart(TitlePart.TITLE, title);
player.sendTitlePart(TitlePart.SUBTITLE, subtitle);
});
}
@Override
public ScoreboardGroup getGroup() {
return ScoreboardGroup.FOOTER;
}
@Override
public int order() {
return Integer.MAX_VALUE;
}
@Override
public String get(Region region, Player p) {
WorldConfiguration.Misc misc = getConfig();
switch (misc.redstoneImplementation) {
case ALTERNATE_CURRENT:
switch (misc.alternateCurrentUpdateOrder) {
case VERTICAL_FIRST_INWARD:
return "§eRedstone§8: §cAC §8(§7Y in§8)";
case VERTICAL_FIRST_OUTWARD:
return "§eRedstone§8: §cAC §8(§7Y out§8)";
case HORIZONTAL_FIRST_INWARD:
return "§eRedstone§8: §cAC §8(§7XZ in§8)";
case HORIZONTAL_FIRST_OUTWARD:
return "§eRedstone§8: §cAC §8(§7XZ out§8)";
}
return "§eRedstone§8: §cAC";
case EIGENCRAFT:
return null;
case VANILLA:
default:
return "§eRedstone§8: §cVanilla";
}
}
}
@@ -20,7 +20,6 @@
package de.steamwar.bausystem.features.region;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.Permission;
import de.steamwar.bausystem.config.BauServer;
import de.steamwar.bausystem.region.Region;
import de.steamwar.bausystem.region.RegionUtils;
@@ -84,9 +83,6 @@ public class ColorCommand extends SWCommand {
@ClassValidator(value = Player.class, local = true)
public TypeValidator<Player> validator() {
return (commandSender, player, messageSender) -> {
if (bauServer.isTeamWorld()) {
return !messageSender.send(!Permission.SUPERVISOR.hasPermission(player), "NO_PERMISSION");
}
return !messageSender.send(!bauServer.getOwner().equals(player.getUniqueId()), "NO_PERMISSION");
};
}
@@ -68,7 +68,7 @@ public class ResetCommand extends SWCommand {
Region region = regionCheck(p);
if (region == null) return;
if (bauServer.hasOwner() && !p.getUniqueId().equals(bauServer.getOwner())) {
if (!p.getUniqueId().equals(bauServer.getOwner())) {
if (Punishment.isPunished(SteamwarUser.get(bauServer.getOwner()), Punishment.PunishmentType.NoSchemReceiving, punishment -> BauSystem.MESSAGE.send("REGION_TB_NO_SCHEMRECEIVING", p, punishment.getEndTime()))) {
return;
}
@@ -91,7 +91,7 @@ public class TestblockCommand extends SWCommand {
}
}
if (bauServer.hasOwner() && !p.getUniqueId().equals(bauServer.getOwner())) {
if (!p.getUniqueId().equals(bauServer.getOwner())) {
if (Punishment.isPunished(SteamwarUser.get(bauServer.getOwner()), Punishment.PunishmentType.NoSchemReceiving, punishment -> BauSystem.MESSAGE.send("REGION_TB_NO_SCHEMRECEIVING", p, punishment.getEndTime()))) {
return;
}
@@ -19,7 +19,6 @@
package de.steamwar.bausystem.features.simulator;
import com.comphenix.tinyprotocol.TinyProtocol;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.Permission;
import de.steamwar.bausystem.SWUtils;
@@ -38,77 +37,88 @@ 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.core.SWPlayer;
import de.steamwar.cursor.Cursor;
import de.steamwar.entity.REntity;
import de.steamwar.entity.REntityServer;
import de.steamwar.entity.RFallingBlockEntity;
import de.steamwar.inventory.SWAnvilInv;
import de.steamwar.linkage.Linked;
import lombok.AllArgsConstructor;
import lombok.Getter;
import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.*;
import org.bukkit.event.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.util.Vector;
import java.util.*;
import java.util.function.BiFunction;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
@Linked
public class SimulatorCursor implements Listener {
private static final World WORLD = Bukkit.getWorlds().get(0);
private static Map<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<>();
private static final Map<Player, CursorType> cursorType = Collections.synchronizedMap(new HashMap<>());
private static final Map<Player, REntityServer> emptyTargetServers = Collections.synchronizedMap(new HashMap<>());
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);
private static boolean hasSimulatorItem(Player player) {
return isSimulatorItem(player.getInventory().getItemInMainHand()) || isSimulatorItem(player.getInventory().getItemInOffHand());
}
private static void scheduleCursorUpdate(Player player) {
BauSystem.runTaskLater(BauSystem.getInstance(), () -> calcCursor(player), 1);
}
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
if (!Permission.BUILD.hasPermission(event.getPlayer())) return;
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
calcCursor(event.getPlayer());
}, 0);
scheduleCursorUpdate(event.getPlayer());
}
@EventHandler
public void onPlayerDropItem(PlayerDropItemEvent event) {
if (!Permission.BUILD.hasPermission(event.getPlayer())) return;
calcCursor(event.getPlayer());
scheduleCursorUpdate(event.getPlayer());
}
@EventHandler
public void onPlayerItemHeld(PlayerItemHeldEvent event) {
if (!Permission.BUILD.hasPermission(event.getPlayer())) return;
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
calcCursor(event.getPlayer());
}, 1);
scheduleCursorUpdate(event.getPlayer());
}
@EventHandler
public void onInventoryClick(InventoryClickEvent event) {
if (!(event.getWhoClicked() instanceof Player player)) return;
if (!Permission.BUILD.hasPermission(player)) return;
scheduleCursorUpdate(player);
}
@EventHandler
public void onInventoryDrag(InventoryDragEvent event) {
if (!(event.getWhoClicked() instanceof Player player)) return;
if (!Permission.BUILD.hasPermission(player)) return;
scheduleCursorUpdate(player);
}
@EventHandler
@@ -119,10 +129,7 @@ public class SimulatorCursor implements Listener {
@EventHandler
public void onPlayerQuit(PlayerQuitEvent event) {
cursorType.remove(event.getPlayer());
cursors.remove(event.getPlayer());
synchronized (calculating) {
calculating.remove(event.getPlayer());
}
removeCursor(event.getPlayer());
}
private static final Map<Player, Long> LAST_SNEAKS = new HashMap<>();
@@ -138,7 +145,7 @@ public class SimulatorCursor implements Listener {
public void onPlayerToggleSneak(PlayerToggleSneakEvent event) {
if (!event.isSneaking()) return;
Player player = event.getPlayer();
if (!isSimulatorItem(player.getInventory().getItemInMainHand()) && !isSimulatorItem(player.getInventory().getItemInOffHand())) {
if (!hasSimulatorItem(player)) {
return;
}
if (LAST_SNEAKS.containsKey(player)) {
@@ -164,17 +171,10 @@ public class SimulatorCursor implements Listener {
}
public static void calcCursor(Player player) {
synchronized (calculating) {
if (calculating.contains(player)) return;
calculating.add(player);
}
if (!Permission.BUILD.hasPermission(player) || (!isSimulatorItem(player.getInventory().getItemInMainHand()) && !isSimulatorItem(player.getInventory().getItemInOffHand()))) {
if (removeCursor(player) || SimulatorWatcher.show(null, player)) {
if (!Permission.BUILD.hasPermission(player) || !hasSimulatorItem(player)) {
if (removeCursor(player) | SimulatorWatcher.show(null, player)) {
SWUtils.sendToActionbar(player, "");
}
synchronized (calculating) {
calculating.remove(player);
}
return;
}
@@ -183,203 +183,98 @@ public class SimulatorCursor implements Listener {
removeCursor(player);
SimulatorWatcher.show(null, player);
SWUtils.sendToActionbar(player, "§cGenerating Stab");
synchronized (calculating) {
calculating.remove(player);
}
return;
}
SimulatorWatcher.show(simulator, player);
List<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;
Cursor cursor = getOrCreateCursor(player, simulator, cursorType.getOrDefault(player, CursorType.TNT));
cursor.renderDeduplicated();
}
private static Cursor getOrCreateCursor(Player player, Simulator simulator, CursorType type) {
REntityServer targetServer = simulator == null ? emptyTargetServers.computeIfAbsent(player, __ -> new REntityServer()) : SimulatorWatcher.getEntityServerOfSimulator(simulator);
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);
}
showCursor(player, rayTraceResult, simulator != null);
synchronized (calculating) {
calculating.remove(player);
}
return cursor;
}
private static synchronized boolean removeCursor(Player player) {
REntityServer entityServer = cursors.get(player);
boolean hadCursor = entityServer != null && !entityServer.getEntities().isEmpty();
if (entityServer != null) {
entityServer.getEntities().forEach(REntity::die);
Optional<Cursor> cursor = SWPlayer.of(player).getComponent(Cursor.class);
cursor.ifPresent(__ -> SWPlayer.of(player).removeComponent(Cursor.class));
REntityServer emptyTargetServer = emptyTargetServers.remove(player);
if (emptyTargetServer != null) {
emptyTargetServer.close();
}
return hadCursor;
return cursor.isPresent();
}
private static synchronized void showCursor(Player player, RayTraceUtils.RRayTraceResult rayTraceResult, boolean hasSimulatorSelected) {
REntityServer entityServer = cursors.computeIfAbsent(player, __ -> {
REntityServer rEntityServer = new REntityServer();
rEntityServer.addPlayer(player);
return rEntityServer;
});
CursorType type = cursorType.getOrDefault(player, CursorType.TNT);
REntity hitEntity = rayTraceResult.getHitEntity();
Location location = hitEntity != null ? new Vector(hitEntity.getX(), hitEntity.getY(), hitEntity.getZ()).toLocation(WORLD) :
type.position.apply(player, rayTraceResult).toLocation(WORLD);
Material material = hitEntity != null ? Material.GLASS : type.getMaterial();
List<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 {
private static void sendCursorActionbar(Player player, Simulator simulator, boolean hasCursorLocation, boolean hasHitEntity) {
if (!hasCursorLocation) {
SWUtils.sendToActionbar(player, simulator == null ? "§eSelect Simulator" : "§eOpen Simulator");
} else if (simulator == null) {
SWUtils.sendToActionbar(player, "§eCreate new Simulator");
}
}
public static Vector getPosFree(Player player, RayTraceUtils.RRayTraceResult result) {
Vector pos = result.getHitPosition();
BlockFace face = result.getHitBlockFace();
if (face != null) {
switch (face) {
case DOWN:
pos.setY(pos.getY() - 0.98);
break;
case EAST:
pos.setX(pos.getX() + 0.49);
break;
case WEST:
pos.setX(pos.getX() - 0.49);
break;
case NORTH:
pos.setZ(pos.getZ() - 0.49);
break;
case SOUTH:
pos.setZ(pos.getZ() + 0.49);
break;
default:
break;
}
if (face.getModY() == 0 && player.isSneaking()) {
pos.setY(pos.getY() - 0.49);
}
}
if (!player.isSneaking()) {
pos.setX(pos.getBlockX() + 0.5);
if (face == null || face.getModY() == 0) {
pos.setY(pos.getBlockY() + 0.0);
}
pos.setZ(pos.getBlockZ() + 0.5);
}
return pos;
}
private static Vector getPosBlockAligned(Player player, RayTraceUtils.RRayTraceResult result) {
Vector pos = result.getHitPosition();
BlockFace face = result.getHitBlockFace();
if (face != null) {
switch (face) {
case DOWN:
pos.setY(pos.getY() - 0.98);
break;
case EAST:
pos.setX(pos.getX() + 0.49);
break;
case WEST:
pos.setX(pos.getX() - 0.49);
break;
case NORTH:
pos.setZ(pos.getZ() - 0.49);
break;
case SOUTH:
pos.setZ(pos.getZ() + 0.49);
break;
default:
break;
}
}
pos.setX(pos.getBlockX() + 0.5);
if (pos.getY() - pos.getBlockY() != 0 && face == BlockFace.UP) {
pos.setY(pos.getBlockY() + 1.0);
} else if (hasHitEntity) {
SWUtils.sendToActionbar(player, "§eEdit Position");
} else {
pos.setY(pos.getBlockY());
SWUtils.sendToActionbar(player, "§eAdd new " + cursorType.getOrDefault(player, CursorType.TNT).name);
}
pos.setZ(pos.getBlockZ() + 0.5);
return pos;
}
@Getter
@AllArgsConstructor
public enum CursorType {
TNT(Material.TNT, Material.GUNPOWDER, SimulatorCursor::getPosFree, "TNT", vector -> new TNTElement(vector).add(new TNTPhase())),
REDSTONE_BLOCK(Material.REDSTONE_BLOCK, Material.REDSTONE, SimulatorCursor::getPosBlockAligned, "Redstone Block", vector -> new RedstoneElement(vector).add(new RedstonePhase())),
OBSERVER(Material.OBSERVER, Material.QUARTZ, SimulatorCursor::getPosBlockAligned, "Observer", vector -> new ObserverElement(vector).add(new ObserverPhase())),
TNT(Material.TNT, Material.GUNPOWDER, List.of(Cursor.CursorMode.FREE, Cursor.CursorMode.SURFACE_ALIGNED), "TNT", vector -> new TNTElement(vector).add(new TNTPhase())),
REDSTONE_BLOCK(Material.REDSTONE_BLOCK, Material.REDSTONE, List.of(Cursor.CursorMode.BLOCK_ALIGNED), "Redstone Block", vector -> new RedstoneElement(vector).add(new RedstonePhase())),
OBSERVER(Material.OBSERVER, Material.QUARTZ, List.of(Cursor.CursorMode.BLOCK_ALIGNED), "Observer", vector -> new ObserverElement(vector).add(new ObserverPhase())),
;
public final Material material;
public final Material nonSelectedMaterial;
public final BiFunction<Player, RayTraceUtils.RRayTraceResult, Vector> position;
public final List<Cursor.CursorMode> cursorModes;
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")) {
private static void handlePlayerClick(Player player, Location cursorLocation, Optional<REntity> hitEntity, Action action) {
if (!Permission.BUILD.hasPermission(player)) return;
if (!hasSimulatorItem(player)) {
return;
}
event.setCancelled(true);
Player player = event.getPlayer();
Simulator simulator = SimulatorStorage.getSimulator(player);
if (event.getAction() == Action.LEFT_CLICK_BLOCK || event.getAction() == Action.LEFT_CLICK_AIR) {
if (action == Action.LEFT_CLICK_BLOCK || action == Action.LEFT_CLICK_AIR) {
if (simulator == null) {
return;
}
SimulatorExecutor.run(event.getPlayer(), simulator, null);
SimulatorExecutor.run(player, simulator, null);
return;
}
if (event.getAction() != Action.RIGHT_CLICK_BLOCK && event.getAction() != Action.RIGHT_CLICK_AIR) {
if (action != Action.RIGHT_CLICK_BLOCK && action != Action.RIGHT_CLICK_AIR) {
return;
}
RayTraceUtils.RRayTraceResult rayTraceResult = RayTraceUtils.traceREntity(player, player.getLocation(), SimulatorWatcher.getEntitiesOfSimulator(simulator));
if (simulator == null) {
if (rayTraceResult == null) {
if (cursorLocation == null) {
SimulatorStorage.openSimulatorSelector(player);
} else {
SWAnvilInv anvilInv = new SWAnvilInv(player, "Name");
@@ -395,7 +290,7 @@ public class SimulatorCursor implements Listener {
}
sim = new Simulator(s);
SimulatorStorage.addSimulator(s, sim);
createElement(player, rayTraceResult, sim);
createElement(player, cursorLocation, sim);
SimulatorStorage.setSimulator(player, sim);
});
anvilInv.open();
@@ -403,57 +298,56 @@ public class SimulatorCursor implements Listener {
return;
}
if (rayTraceResult == null) {
if (cursorLocation == null) {
new SimulatorGui(player, simulator).open();
return;
}
if (rayTraceResult.getHitEntity() != null) {
REntity hitEntity = rayTraceResult.getHitEntity();
Vector vector = new Vector(hitEntity.getX(), hitEntity.getY(), hitEntity.getZ());
List<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;
}
if (hitEntity.isPresent()) {
openElement(player, simulator, hitEntity.get());
return;
}
// Add new Element to current simulator
createElement(player, rayTraceResult, simulator);
createElement(player, cursorLocation, simulator);
}
private void createElement(Player player, RayTraceUtils.RRayTraceResult rayTraceResult, Simulator simulator) {
private static void openElement(Player player, Simulator simulator, REntity hitEntity) {
Vector vector = new Vector(hitEntity.getX(), hitEntity.getY(), hitEntity.getZ());
List<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 = cursorType.getOrDefault(player, CursorType.TNT);
Vector vector = type.position.apply(player, rayTraceResult);
Vector vector = cursorLocation.toVector();
if (type == CursorType.REDSTONE_BLOCK) {
vector.subtract(new Vector(0.5, 0, 0.5));
}
@@ -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));
}
}
@@ -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());
}
}
@@ -20,7 +20,6 @@
package de.steamwar.bausystem.features.world;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.Permission;
import de.steamwar.bausystem.config.BauServer;
import de.steamwar.core.CRIUWakeupEvent;
import de.steamwar.linkage.Linked;
@@ -37,13 +36,12 @@ public class AntiBauAddMemberFix implements Listener {
@EventHandler(priority = EventPriority.LOW)
public void onPlayerJoin(PlayerJoinEvent event) {
if (BauSystem.DEV_SERVER) return;
if (Permission.SUPERVISOR.hasPermission(event.getPlayer())) {
if (event.getPlayer().getUniqueId().equals(BauServer.getInstance().getOwner())) {
return;
}
if (BauweltMember.getBauMember(BauServer.getInstance().getWorldID(), event.getPlayer().getUniqueId()) == null) {
if (BauweltMember.getBauMember(BauServer.getInstance().getOwner(), event.getPlayer().getUniqueId()) == null) {
event.getPlayer().kickPlayer("");
String owner = BauServer.getInstance().isTeamWorld() ? "team " + BauServer.getInstance().getTeamID() : SteamwarUser.byId(BauServer.getInstance().getOwnerID()).getUserName();
throw new SecurityException("The player " + event.getPlayer().getName() + " joined on the server of " + owner + " without being added!");
throw new SecurityException("The player " + event.getPlayer().getName() + " joined on the server of " + SteamwarUser.byId(BauServer.getInstance().getOwnerID()).getUserName() + " without being added!");
}
}
@@ -33,7 +33,7 @@ public class AxiomPermissionCheck implements Listener {
@EventHandler
public void onAxiomHandshake(AxiomHandshakeEvent event) {
if (Permission.SUPERVISOR.hasPermission(event.getPlayer()) || (BauServer.getInstance().hasOwner() && BauServer.getInstance().getOwner().equals(event.getPlayer().getUniqueId()))) {
if (Permission.SUPERVISOR.hasPermission(event.getPlayer()) || BauServer.getInstance().getOwner().equals(event.getPlayer().getUniqueId())) {
event.setMaxBufferSize(Short.MAX_VALUE);
return;
}
@@ -28,7 +28,7 @@ import de.steamwar.data.BauLockState;
import de.steamwar.linkage.Linked;
import de.steamwar.network.packets.PacketHandler;
import de.steamwar.network.packets.server.BaulockUpdatePacket;
import de.steamwar.sql.SteamwarWorld;
import de.steamwar.sql.UserConfig;
import lombok.Getter;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
@@ -37,13 +37,13 @@ import org.bukkit.event.Listener;
@Linked
public class BauLockStateScoreboard extends PacketHandler implements ScoreboardElement, Listener {
private static final String BAU_LOCK_CONFIG_NAME = "baulockstate";
@Getter
private BauLockState lockState = loadLockState();
private BauLockState loadLockState() {
if (BauServer.getInstance().isTeamWorld()) return BauLockState.OPEN;
SteamwarWorld world = SteamwarWorld.getWorld(BauServer.getInstance().getWorldID());
String state = world == null ? null : world.getLockState();
String state = UserConfig.getConfig(BauServer.getInstance().getOwner(), BAU_LOCK_CONFIG_NAME);
return state == null ? BauLockState.OPEN : BauLockState.valueOf(state);
}
@@ -69,7 +69,6 @@ public class BauLockStateScoreboard extends PacketHandler implements ScoreboardE
@Override
public String get(Region region, Player p) {
if (BauServer.getInstance().isTeamWorld()) return null;
if (!BauServer.getInstance().getOwner().equals(p.getUniqueId())) {
return null;
}
@@ -42,11 +42,7 @@ public class KickallCommand extends SWCommand {
if (!Permission.OWNER.hasPermission(player)) return;
Bukkit.getOnlinePlayers().forEach(p -> {
if (bauServer.isTeamWorld()) {
if (!Permission.SUPERVISOR.hasPermission(p)) p.kickPlayer("");
} else if (!bauServer.getOwner().equals(p.getUniqueId())) {
p.kickPlayer("");
}
if (!bauServer.getOwner().equals(p.getUniqueId())) p.kickPlayer("");
});
}
}
@@ -143,14 +143,17 @@ public class SpectatorListener implements Listener {
public void onPlayerJoin(PlayerJoinEvent event) {
enableOrDisableTechhider();
if (BauSystem.DEV_SERVER) return;
if (Permission.SUPERVISOR.hasPermission(event.getPlayer())) {
if (event.getPlayer().getUniqueId().equals(BauServer.getInstance().getOwner())) {
return;
}
BauweltMember bauweltMember = BauweltMember.getBauMember(BauServer.getInstance().getWorldID(), event.getPlayer().getUniqueId());
BauweltMember bauweltMember = BauweltMember.getBauMember(BauServer.getInstance().getOwner(), event.getPlayer().getUniqueId());
if (bauweltMember == null) {
event.getPlayer().kickPlayer("");
return;
}
if (Permission.SUPERVISOR.hasPermission(event.getPlayer())) {
return;
}
if (!anySupervisorOnline(null)) {
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
event.getPlayer().kickPlayer("");
+1 -11
View File
@@ -11,23 +11,13 @@ import de.steamwar.commands.profiler.ProfilerCommand
import de.steamwar.commands.user.UserCommand
import de.steamwar.commands.user.UserInfoCommand
import de.steamwar.commands.user.UserSearchCommand
import de.steamwar.commands.world.MigrationCommand
import de.steamwar.commands.world.SaveToStorageCommand
import de.steamwar.commands.world.TemplateCommand
import de.steamwar.commands.world.TemplateCreateCommand
import de.steamwar.commands.world.WorldCommand
fun main(args: Array<String>) =
SteamWar()
.subcommands(
DatabaseCommand().subcommands(InfoCommand(), ResetCommand()),
UserCommand().subcommands(UserInfoCommand(), UserSearchCommand()),
WorldCommand().subcommands(
MigrationCommand(),
SaveToStorageCommand(),
TemplateCommand().subcommands(TemplateCreateCommand())
),
DevCommand(),
ProfilerCommand()
)
.main(args)
.main(args)
-243
View File
@@ -1,243 +0,0 @@
package de.steamwar.commands.world
import com.github.ajalt.clikt.core.CliktCommand
import de.steamwar.db.Database
import de.steamwar.sql.SteamwarUser
import de.steamwar.sql.SteamwarWorld
import de.steamwar.sql.UserConfig
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
import java.io.File
import java.sql.SQLException
import java.util.UUID
class MigrationCommand : CliktCommand(name = "migrate") {
private val userWorldPattern = Regex("""userworlds(\d+)""")
private val builderWorldPattern = Regex("""builder(\d+)""")
private val trailingVersionPattern = Regex("""(\d+)$""")
override fun run() {
Database.ensureConnected()
val bau = migrateBauWorlds()
val builder = migrateBuilderWorlds()
val arena = migrateArenaWorlds()
migrateBauLockConfig()
migrateBauweltMembers()
echo("Imported ${bau.imported} build worlds, skipped ${bau.skipped}, missing users ${bau.missingOwner}.")
echo("Imported ${builder.imported} builder worlds, skipped ${builder.skipped}.")
echo("Imported ${arena.imported} arena worlds, skipped ${arena.skipped}.")
echo("World migration completed.")
}
private fun migrateBauWorlds(): MigrationStats {
val stats = MigrationStats()
val worldsRoot = File("/worlds")
worldsRoot.listFiles { file -> file.isDirectory && userWorldPattern.matches(file.name) }
?.forEach { versionFolder ->
val version = userWorldPattern.matchEntire(versionFolder.name)!!.groupValues[1].toInt()
versionFolder.listFiles { file -> file.isDirectory && isWorldDirectory(file) }
?.sortedBy { it.name }
?.forEach { legacyWorld ->
val owner = legacyWorld.ownerUser()
if (owner == null) {
stats.missingOwner++
echo("Skipped build world ${legacyWorld.path}: no matching user.")
return@forEach
}
val lockState = UserConfig.getConfig(owner.id.value, BAU_LOCK_CONFIG_NAME)
val world = SteamwarWorld.getOrCreateBauWorld(owner, owner.userName, version, legacyWorld)
if (world.lockState == null && lockState != null) {
world.changeLockState(lockState)
}
stats.imported++
}
} ?: run {
stats.skipped++
echo("Skipped build worlds: /worlds does not exist.")
}
return stats
}
private fun migrateBuilderWorlds(): MigrationStats {
val stats = MigrationStats()
val worldsRoot = File("/worlds")
worldsRoot.listFiles { file -> file.isDirectory && builderWorldPattern.matches(file.name) }
?.forEach { versionFolder ->
val version = builderWorldPattern.matchEntire(versionFolder.name)!!.groupValues[1].toInt()
versionFolder.listFiles { file -> file.isDirectory && isWorldDirectory(file) }
?.sortedBy { it.name }
?.forEach { legacyWorld ->
SteamwarWorld.getOrCreateBuilderWorld(legacyWorld.name, version, legacyWorld)
stats.imported++
}
} ?: run {
stats.skipped++
echo("Skipped builder worlds: /worlds does not exist.")
}
return stats
}
private fun migrateArenaWorlds(): MigrationStats {
val stats = MigrationStats()
val configs = parseGameModeConfigs(File("/configs/GameModes"))
if (configs.isEmpty()) {
stats.skipped++
echo("Skipped arena worlds: no game mode configs found in /configs/GameModes.")
return stats
}
for (config in configs) {
val version = trailingVersionPattern.find(config.serverFolder)?.value?.toIntOrNull()
if (version == null) {
stats.skipped++
echo("Skipped arena mode ${config.modeName}: server folder '${config.serverFolder}' has no version suffix.")
continue
}
val arenaRoot = File("/servers/${config.serverFolder}/arenas")
for (map in config.maps) {
val legacyWorld = File(arenaRoot, map)
if (!isWorldDirectory(legacyWorld)) {
stats.skipped++
continue
}
SteamwarWorld.getOrCreateArenaWorld(config.modeName, map, version, legacyWorld)
stats.imported++
}
}
return stats
}
private fun migrateBauLockConfig() {
transaction(Database.db) {
exec(
"""
UPDATE `world` w
JOIN `UserConfig` uc ON uc.`User` = w.`Owner` AND uc.`Config` = 'baulockstate'
SET w.`LockState` = uc.`Value`
WHERE w.`Owner` IS NOT NULL AND w.`LockState` IS NULL
""".trimIndent()
)
}
}
private fun migrateBauweltMembers() {
if (!columnExists("BauweltMember", "WorldID") || !columnExists("BauweltMember", "BauweltID")) {
echo("Skipped BauweltMember data migration: expected WorldID and BauweltID columns are not both present.")
return
}
execIgnore(
"""
INSERT IGNORE INTO `BauweltMember` (`WorldID`, `MemberID`, `Build`, `WorldEdit`, `World`)
SELECT w.`id`, bm.`MemberID`, bm.`Build`, bm.`WorldEdit`, bm.`World`
FROM `BauweltMember` bm
JOIN `world` w ON w.`Owner` = bm.`BauweltID` AND w.`Deleted` = 0
""".trimIndent(),
"Skipped BauweltMember data migration"
)
}
private fun parseGameModeConfigs(configRoot: File): List<ArenaMigrationConfig> {
if (!configRoot.isDirectory) return emptyList()
return configRoot.listFiles { file -> file.isFile && file.extension == "yml" && !file.name.endsWith(".kits.yml") }
?.mapNotNull { file ->
val server = parseServerBlock(file)
val folder = server.folder ?: return@mapNotNull null
ArenaMigrationConfig(file.nameWithoutExtension, folder, server.maps)
}
?: emptyList()
}
private fun parseServerBlock(file: File): ParsedServerBlock {
var inServer = false
var inMaps = false
var folder: String? = null
val maps = mutableListOf<String>()
file.readLines().forEach { line ->
val trimmed = line.trim()
if (trimmed.isEmpty() || trimmed.startsWith("#")) return@forEach
val indent = line.indexOfFirst { !it.isWhitespace() }.let { if (it == -1) 0 else it }
if (indent == 0) {
inServer = trimmed == "Server:"
inMaps = false
return@forEach
}
if (!inServer) return@forEach
when {
indent <= 2 && trimmed.startsWith("Folder:") -> folder = trimmed.substringAfter(":").cleanYamlValue()
indent <= 2 && trimmed.startsWith("Maps:") -> {
val value = trimmed.substringAfter(":").trim()
inMaps = true
if (value.startsWith("[") && value.endsWith("]")) {
maps += value.removePrefix("[").removeSuffix("]")
.split(",")
.map { it.cleanYamlValue() }
.filter { it.isNotEmpty() }
inMaps = false
}
}
inMaps && trimmed.startsWith("-") -> maps += trimmed.removePrefix("-").cleanYamlValue()
inMaps && indent <= 2 -> inMaps = false
}
}
return ParsedServerBlock(folder, maps)
}
private fun File.ownerUser(): SteamwarUser? {
name.toIntOrNull()?.let { return SteamwarUser.byId(it) }
return runCatching { SteamwarUser.get(UUID.fromString(name)) }.getOrNull()
}
private fun isWorldDirectory(file: File): Boolean =
file.isDirectory && (File(file, "level.dat").isFile || File(file, "region").isDirectory)
private fun String.cleanYamlValue(): String =
trim().trim('"').trim('\'')
private fun columnExists(table: String, column: String): Boolean =
transaction(Database.db) {
exec("SHOW COLUMNS FROM `$table` LIKE '$column'") { result -> result.next() } ?: false
}
private fun execIgnore(sql: String, prefix: String) {
try {
transaction(Database.db) {
exec(sql)
}
} catch (e: SQLException) {
echo("$prefix: ${e.message}")
}
}
private data class MigrationStats(
var imported: Int = 0,
var skipped: Int = 0,
var missingOwner: Int = 0,
)
private data class ArenaMigrationConfig(
val modeName: String,
val serverFolder: String,
val maps: List<String>,
)
private data class ParsedServerBlock(
val folder: String?,
val maps: List<String>,
)
private companion object {
private const val BAU_LOCK_CONFIG_NAME = "baulockstate"
}
}
@@ -1,30 +0,0 @@
package de.steamwar.commands.world
import com.github.ajalt.clikt.core.CliktCommand
import de.steamwar.db.Database
import de.steamwar.sql.SteamwarWorld
import java.time.LocalTime
class SaveToStorageCommand : CliktCommand(name = "saveToStorage") {
override fun run() {
Database.ensureConnected()
var archived = 0
for (world in SteamwarWorld.getWorldsToArchive()) {
if (!beforeCutoff()) {
echo("Stopping before 04:00. Archived $archived worlds.")
return
}
if (world.archiveIfNeeded()) {
archived++
echo("Archived ${world.uuid} (${world.type}/${world.name}).")
}
}
echo("Storage save completed. Archived $archived worlds.")
}
private fun beforeCutoff(): Boolean =
LocalTime.now().isBefore(LocalTime.of(4, 0))
}
-24
View File
@@ -1,24 +0,0 @@
package de.steamwar.commands.world
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.parameters.arguments.argument
import com.github.ajalt.clikt.parameters.types.int
import com.github.ajalt.clikt.parameters.types.path
import de.steamwar.db.Database
import de.steamwar.sql.SteamwarWorld
class TemplateCommand : CliktCommand(name = "template") {
override fun run() = Unit
}
class TemplateCreateCommand : CliktCommand(name = "create") {
private val name by argument()
private val version by argument().int()
private val source by argument().path(canBeFile = false, mustExist = true)
override fun run() {
Database.ensureConnected()
SteamwarWorld.getOrCreateTemplateWorld(name, version, source.toFile())
echo("Template world '$name' created.")
}
}
-7
View File
@@ -1,7 +0,0 @@
package de.steamwar.commands.world
import com.github.ajalt.clikt.core.CliktCommand
class WorldCommand : CliktCommand(name = "world") {
override fun run() = Unit
}
@@ -19,6 +19,7 @@
package de.steamwar.sql
import de.steamwar.sql.BauweltMemberTable.bauweltId
import de.steamwar.sql.internal.useDb
import org.jetbrains.exposed.v1.core.and
import org.jetbrains.exposed.v1.core.dao.id.CompositeID
@@ -31,37 +32,45 @@ import org.jetbrains.exposed.v1.jdbc.insertIgnore
import java.util.*
object BauweltMemberTable : CompositeIdTable("BauweltMember") {
val worldId = reference("WorldID", WorldTable).index()
val bauweltId = reference("BauweltID", SteamwarUserTable).index()
val memberId = reference("MemberID", SteamwarUserTable).index()
val build = bool("Build")
val worldEdit = bool("WorldEdit")
val world = bool("World")
override val primaryKey = PrimaryKey(worldId, memberId)
override val primaryKey = PrimaryKey(bauweltId, memberId)
init {
addIdColumn(worldId)
addIdColumn(bauweltId)
addIdColumn(memberId)
}
}
class BauweltMember(id: EntityID<CompositeID>) : CompositeEntity(id) {
companion object : CompositeEntityClass<BauweltMember>(BauweltMemberTable) {
private val cache = mutableMapOf<Pair<UUID, Int>, BauweltMember>()
private val cache = mutableMapOf<Int, BauweltMember>()
private fun cache(member: BauweltMember) =
cache.put(member.worldID to member.memberID, member)
cache.put(member.memberID, member)
@JvmStatic
fun clear() =
cache.clear()
@JvmStatic
fun addMember(worldId: UUID, newMemberId: Int) =
@Deprecated("Use addMember(ownerId: Int, newMemberId: Int)")
fun addMember(ownerId: UUID, newMemberId: UUID) =
addMember(SteamwarUser.get(ownerId)!!.id, SteamwarUser.get(newMemberId)!!.id)
@JvmStatic
fun addMember(ownerId: Int, newMemberId: Int) =
addMember(EntityID(ownerId, SteamwarUserTable), EntityID(newMemberId, SteamwarUserTable))
fun addMember(ownerId: EntityID<Int>, newMemberId: EntityID<Int>) =
useDb {
BauweltMemberTable.insertIgnore {
it[BauweltMemberTable.worldId] = EntityID(worldId, WorldTable)
it[memberId] = EntityID(newMemberId, SteamwarUserTable)
it[bauweltId] = ownerId
it[memberId] = newMemberId
it[build] = false
it[worldEdit] = false
it[world] = false
@@ -69,32 +78,31 @@ class BauweltMember(id: EntityID<CompositeID>) : CompositeEntity(id) {
}
@JvmStatic
fun addMember(worldId: UUID, newMemberId: UUID) =
addMember(worldId, SteamwarUser.get(newMemberId)!!.id.value)
@JvmStatic
fun getBauMember(world: UUID, member: Int) =
cache[world to member]
?: useDb {
find { (BauweltMemberTable.worldId eq world) and (BauweltMemberTable.memberId eq member) }.firstOrNull()?.also { cache(it) }
}
@JvmStatic
fun getBauMember(world: UUID, member: UUID) =
getBauMember(world, SteamwarUser.get(member)!!.id.value)
@JvmStatic
fun getWorldMembers(world: UUID) =
@Deprecated("Use getBauMember(bauwelt: Int, member: Int)")
fun getBauMember(bauwelt: UUID, member: UUID) =
useDb {
find { BauweltMemberTable.worldId eq world }.toList().also { it.forEach { cache(it) } }
find { (bauweltId eq SteamwarUser.get(bauwelt)!!.id) and (BauweltMemberTable.memberId eq SteamwarUser.get(member)!!.id) }.firstOrNull()?.also { cache(it) }
}
@JvmStatic
fun getMembers(world: UUID) =
getWorldMembers(world)
fun getBauMember(bauwelt: Int, member: Int) =
useDb {
find { (bauweltId eq bauwelt) and (BauweltMemberTable.memberId eq member) }.firstOrNull()?.also { cache(it) }
}
@JvmStatic
@Deprecated("Use getMembers(bauwelt: Int)")
fun getMembers(bauwelt: UUID) =
getMembers(SteamwarUser.get(bauwelt)!!.id.value)
@JvmStatic
fun getMembers(bauwelt: Int) =
useDb {
find { bauweltId eq bauwelt }.toList().also { it.forEach { cache(it) } }
}
}
val worldID by BauweltMemberTable.worldId.transform({ EntityID(it, WorldTable) }, { it.value })
val bauweltID by BauweltMemberTable.bauweltId.transform({ EntityID(it, SteamwarUserTable) }, { it.value })
val memberID by BauweltMemberTable.memberId.transform({ EntityID(it, SteamwarUserTable) }, { it.value })
private var worldEditInternal by BauweltMemberTable.worldEdit
var worldEdit: Boolean
@@ -136,4 +144,4 @@ class BauweltMember(id: EntityID<CompositeID>) : CompositeEntity(id) {
useDb {
delete()
}
}
}
@@ -50,8 +50,6 @@ enum class UserPerm {
PUNISHMENTS,
TICKET_LOG,
BUILD,
BUILD_EXTRA_WORLDS,
BUILD_UNLIMITED_WORLDS,
CHECK,
MODERATION,
ADMINISTRATION;
@@ -97,4 +95,4 @@ enum class UserPerm {
}
data class Prefix(val colorCode: String, val chatPrefix: String, val teamPrefix: Boolean = false)
}
}
-345
View File
@@ -1,345 +0,0 @@
/*
* 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.sql
import de.steamwar.sql.internal.useDb
import org.jetbrains.exposed.v1.core.and
import org.jetbrains.exposed.v1.core.dao.id.EntityID
import org.jetbrains.exposed.v1.core.dao.id.java.UUIDTable
import org.jetbrains.exposed.v1.core.eq
import org.jetbrains.exposed.v1.core.not
import org.jetbrains.exposed.v1.core.or
import org.jetbrains.exposed.v1.core.SortOrder
import org.jetbrains.exposed.v1.dao.Entity
import org.jetbrains.exposed.v1.dao.EntityClass
import org.jetbrains.exposed.v1.javatime.CurrentTimestamp
import org.jetbrains.exposed.v1.javatime.timestamp
import java.io.File
import java.time.Instant
import java.time.temporal.ChronoUnit
import java.util.UUID
object WorldTable : UUIDTable("world") {
val name = varchar("Name", 512)
val version = integer("Version")
val type = enumeration<WorldType>("Type")
val owner = reference("Owner", SteamwarUserTable).nullable()
val team = reference("Team", TeamTable).nullable()
val lockState = varchar("LockState", 32).nullable()
val deleted = bool("Deleted").default(false)
val created = timestamp("Created").defaultExpression(CurrentTimestamp)
val lastUsed = timestamp("LastUsed").defaultExpression(CurrentTimestamp)
}
enum class WorldType {
BAU,
BUILDER,
TEAM,
ARENA,
TEMPLATE,
}
class SteamwarWorld(id: EntityID<UUID>) : Entity<UUID>(id) {
var name by WorldTable.name
private set
var version by WorldTable.version
private set
var type by WorldTable.type
private set
var owner by WorldTable.owner
private set
var team by WorldTable.team
private set
var lockState by WorldTable.lockState
private set
var deleted by WorldTable.deleted
private set
val created by WorldTable.created
var lastUsed by WorldTable.lastUsed
private set
val archived: Boolean
get() = !storageDirectory.exists() && archiveFile.exists()
val shouldArchive: Boolean
get() = !archived && (deleted || lastUsed.plus(7, ChronoUnit.DAYS).isBefore(Instant.now()))
val uuid: UUID
get() = id.value
val storageDirectory: File
get() = File(WORLD_STORAGE, id.value.toString())
private val archiveFile: File
get() = File(ARCHIVE_WORLD_STORAGE, "${id.value}.zip")
@JvmOverloads
fun setupAndGetStoragePath(prototype: File? = null): String {
if (archived) {
loadFromArchive()
}
val needsInitialization = !storageDirectory.exists() || storageDirectory.list()?.isEmpty() == true
if (needsInitialization) {
if (prototype != null && prototype.exists()) {
prototype.copyRecursively(storageDirectory, overwrite = true)
} else {
storageDirectory.mkdirs()
}
}
useDb {
lastUsed = Instant.now()
}
return storageDirectory.path
}
fun markDeleted() = useDb {
deleted = true
}
fun rename(newName: String) = useDb {
name = newName
}
fun archiveIfNeeded(): Boolean {
if (!shouldArchive) return false
File(ARCHIVE_WORLD_STORAGE).mkdirs()
return archiveWorld() == 0
}
fun changeVersion(newVersion: Int) = useDb {
version = newVersion
}
fun changeLockState(newLockState: String?) = useDb {
lockState = newLockState
}
private fun archiveWorld() = ProcessBuilder("zip", "-u9oymrqq", "$ARCHIVE_WORLD_STORAGE/${id.value}.zip", id.value.toString())
.directory(File(WORLD_STORAGE))
.inheritIO()
.start()
.waitFor()
private fun loadFromArchive() = ProcessBuilder("unzip", "-qq", "-o", "$ARCHIVE_WORLD_STORAGE/${id.value}.zip", "-d", WORLD_STORAGE)
.inheritIO()
.start()
.waitFor()
companion object : EntityClass<UUID, SteamwarWorld>(WorldTable) {
const val WORLD_STORAGE = "/worlds/storage"
const val ARCHIVE_WORLD_STORAGE = "/mnt/storage/worlds/storage"
const val DEFAULT_BAU_WORLD_LIMIT = 3
const val EXTRA_BAU_WORLD_LIMIT = 10
const val TEAM_WORLD_LIMIT = 2
@JvmStatic
fun getWorld(uuid: UUID) = useDb {
findById(uuid)
}
@JvmStatic
fun getBauWorld(user: SteamwarUser, version: Int) = useDb {
find {
(WorldTable.owner eq user.id) and
(WorldTable.version eq version) and
(WorldTable.type eq WorldType.BAU) and
not(WorldTable.deleted)
}.firstOrNull()
}
@JvmStatic
fun getBauWorld(user: SteamwarUser, name: String, version: Int) = useDb {
find {
(WorldTable.owner eq user.id) and
(WorldTable.name eq name) and
(WorldTable.version eq version) and
(WorldTable.type eq WorldType.BAU) and
not(WorldTable.deleted)
}.firstOrNull()
}
@JvmStatic
fun getBauWorlds(user: SteamwarUser) = useDb {
find {
(WorldTable.owner eq user.id) and
(WorldTable.type eq WorldType.BAU) and
not(WorldTable.deleted)
}.orderBy(WorldTable.created to SortOrder.ASC).toList()
}
@JvmStatic
fun countBauWorlds(user: SteamwarUser) = useDb {
find {
(WorldTable.owner eq user.id) and
(WorldTable.type eq WorldType.BAU) and
not(WorldTable.deleted)
}.count()
}
@JvmStatic
@JvmOverloads
fun getOrCreateBauWorld(user: SteamwarUser, version: Int, prototype: File? = null): SteamwarWorld =
getBauWorld(user, version) ?: createWorld(user, user.userName, version, WorldType.BAU, prototype)
@JvmStatic
@JvmOverloads
fun getOrCreateBauWorld(user: SteamwarUser, name: String, version: Int, prototype: File? = null): SteamwarWorld =
getBauWorld(user, name, version) ?: createWorld(user, name, version, WorldType.BAU, prototype)
@JvmStatic
fun getBuilderWorld(name: String, version: Int) = useDb {
find {
(WorldTable.name eq name) and
(WorldTable.version eq version) and
(WorldTable.type eq WorldType.BUILDER) and
not(WorldTable.deleted)
}.firstOrNull()
}
@JvmStatic
fun getBuilderWorlds(version: Int) = useDb {
find {
(WorldTable.version eq version) and
(WorldTable.type eq WorldType.BUILDER) and
not(WorldTable.deleted)
}.toList()
}
@JvmStatic
@JvmOverloads
fun getOrCreateBuilderWorld(name: String, version: Int, prototype: File? = null): SteamwarWorld =
getBuilderWorld(name, version) ?: createWorld(null, name, version, WorldType.BUILDER, prototype)
@JvmStatic
fun getTeamWorld(team: Team, name: String, version: Int) = useDb {
find {
(WorldTable.team eq team.id) and
(WorldTable.name eq name) and
(WorldTable.version eq version) and
(WorldTable.type eq WorldType.TEAM) and
not(WorldTable.deleted)
}.firstOrNull()
}
@JvmStatic
fun getTeamWorlds(team: Team) = useDb {
find {
(WorldTable.team eq team.id) and
(WorldTable.type eq WorldType.TEAM) and
not(WorldTable.deleted)
}.orderBy(WorldTable.created to SortOrder.ASC).toList()
}
@JvmStatic
@JvmOverloads
fun getOrCreateTeamWorld(team: Team, name: String, version: Int, prototype: File? = null): SteamwarWorld =
getTeamWorld(team, name, version) ?: createWorld(null, name, version, WorldType.TEAM, prototype, team)
@JvmStatic
fun countTeamWorlds(team: Team) = useDb {
find {
(WorldTable.team eq team.id) and
(WorldTable.type eq WorldType.TEAM) and
not(WorldTable.deleted)
}.count()
}
@JvmStatic
fun getArenaWorld(mode: String, map: String, version: Int) = useDb {
find {
(WorldTable.name eq arenaWorldName(mode, map)) and
(WorldTable.version eq version) and
(WorldTable.type eq WorldType.ARENA) and
not(WorldTable.deleted)
}.firstOrNull()
}
@JvmStatic
@JvmOverloads
fun getOrCreateArenaWorld(mode: String, map: String, version: Int, prototype: File? = null): SteamwarWorld =
getArenaWorld(mode, map, version) ?: createWorld(null, arenaWorldName(mode, map), version, WorldType.ARENA, prototype)
@JvmStatic
fun getTemplateWorld(name: String, version: Int) = useDb {
find {
(WorldTable.name eq name) and
(WorldTable.version eq version) and
(WorldTable.type eq WorldType.TEMPLATE) and
not(WorldTable.deleted)
}.firstOrNull()
}
@JvmStatic
fun getTemplateWorlds() = useDb {
find {
(WorldTable.type eq WorldType.TEMPLATE) and
not(WorldTable.deleted)
}.orderBy(WorldTable.name to SortOrder.ASC).toList()
}
@JvmStatic
fun getTemplateWorlds(version: Int) = useDb {
find {
(WorldTable.version eq version) and
(WorldTable.type eq WorldType.TEMPLATE) and
not(WorldTable.deleted)
}.orderBy(WorldTable.name to SortOrder.ASC).toList()
}
@JvmStatic
@JvmOverloads
fun getOrCreateTemplateWorld(name: String, version: Int, prototype: File? = null): SteamwarWorld =
getTemplateWorld(name, version) ?: createWorld(null, name, version, WorldType.TEMPLATE, prototype)
@JvmStatic
fun getAllActiveWorlds() = useDb {
find { not(WorldTable.deleted) }.toList()
}
@JvmStatic
fun getWorldsToArchive() = useDb {
find {
not(WorldTable.deleted) or (WorldTable.deleted eq true)
}.toList().filter { it.shouldArchive }
}
private fun arenaWorldName(mode: String, map: String) = "$mode/$map"
@JvmStatic
@JvmOverloads
fun createWorld(user: SteamwarUser?, name: String, version: Int, type: WorldType, prototype: File? = null, team: Team? = null): SteamwarWorld {
val world = useDb { new {
this.name = name
this.version = version
this.type = type
this.owner = user?.id
this.team = team?.id
} }
world.setupAndGetStoragePath(prototype)
return world
}
}
}
@@ -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.
*
* 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;
@@ -20,54 +20,14 @@
package de.steamwar.providers;
import de.steamwar.sql.SteamwarUser;
import de.steamwar.sql.WorldType;
import org.bukkit.Bukkit;
import java.util.UUID;
public class BauServerInfo {
private static Integer bauOwner = null;
private static Integer bauTeam = null;
private static UUID bauWorld = null;
private static WorldType bauType = null;
static {
String ownerProperty = System.getProperty("bauOwner");
if (ownerProperty != null && !ownerProperty.isBlank()) {
try {
bauOwner = Integer.parseInt(ownerProperty);
} catch (NumberFormatException ignored) {
}
}
String teamProperty = System.getProperty("bauTeam");
if (teamProperty != null && !teamProperty.isBlank()) {
try {
bauTeam = Integer.parseInt(teamProperty);
} catch (NumberFormatException ignored) {
}
}
String worldProperty = System.getProperty("bauWorld");
if (worldProperty != null && !worldProperty.isBlank()) {
try {
bauWorld = UUID.fromString(worldProperty);
} catch (IllegalArgumentException ignored) {
}
}
String typeProperty = System.getProperty("bauType");
if (typeProperty != null && !typeProperty.isBlank()) {
try {
bauType = WorldType.valueOf(typeProperty);
} catch (IllegalArgumentException ignored) {
}
}
try {
if (bauOwner == null) {
bauOwner = Integer.parseInt(Bukkit.getWorlds().get(0).getName());
}
bauOwner = Integer.parseInt(Bukkit.getWorlds().get(0).getName());
} catch (NumberFormatException ignored) {
}
}
@@ -77,23 +37,11 @@ public class BauServerInfo {
}
public static boolean isBauServer() {
return bauOwner != null || bauWorld != null || bauTeam != null;
return bauOwner != null;
}
public static SteamwarUser getOwnerUser() {
if (bauOwner == null) return null;
return SteamwarUser.byId(bauOwner);
}
public static UUID getWorldId() {
return bauWorld;
}
public static Integer getTeamId() {
return bauTeam;
}
public static WorldType getWorldType() {
return bauType;
}
}
@@ -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
@@ -29,7 +29,6 @@ import java.util.function.Consumer;
@Getter
public class Bauserver extends Subserver {
private static final Map<UUID, Bauserver> servers = new HashMap<>();
private static final Map<UUID, Bauserver> serversByWorld = new HashMap<>();
public static Bauserver get(UUID owner) {
synchronized (servers) {
@@ -37,43 +36,25 @@ public class Bauserver extends Subserver {
}
}
public static Bauserver getByWorld(UUID world) {
synchronized (serversByWorld) {
return serversByWorld.get(world);
}
}
private final UUID owner;
private final UUID world;
public Bauserver(String serverName, UUID owner, int port, ProcessBuilder processBuilder, Runnable shutdownCallback) {
this(serverName, owner, owner, port, processBuilder, shutdownCallback, null);
this(serverName, owner, port, processBuilder, shutdownCallback, null);
}
public Bauserver(String serverName, UUID owner, int port, ProcessBuilder processBuilder, Runnable shutdownCallback, Consumer<Exception> failureCallback) {
this(serverName, owner, owner, port, processBuilder, shutdownCallback, failureCallback);
}
public Bauserver(String serverName, UUID owner, UUID world, int port, ProcessBuilder processBuilder, Runnable shutdownCallback, Consumer<Exception> failureCallback) {
super(serverName, port, processBuilder, shutdownCallback, failureCallback);
this.owner = owner;
this.world = world;
synchronized (servers) {
servers.putIfAbsent(owner, this);
}
synchronized (serversByWorld) {
serversByWorld.put(world, this);
servers.put(owner, this);
}
}
@Override
protected void unregister() {
synchronized (servers) {
servers.remove(owner, this);
}
synchronized (serversByWorld) {
serversByWorld.remove(world);
servers.remove(owner);
}
super.unregister();
}
+2 -1
View File
@@ -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
}
+3 -3
View File
@@ -37,8 +37,8 @@ if __name__ == "__main__":
with open(configfile, 'r') as file:
gamemode = yaml.load(file)
builderworld = sys.argv[4] if len(sys.argv) > 4 else path.expanduser(f'/worlds/builder{version}/{worldname}')
arenaworld = sys.argv[5] if len(sys.argv) > 5 else f'/servers/{gamemode["Server"]["Folder"]}/arenas/{worldname}'
builderworld = path.expanduser(f'/worlds/builder{version}/{worldname}')
arenaworld = f'/servers/{gamemode["Server"]["Folder"]}/arenas/{worldname}'
if path.exists(arenaworld):
backupworld = path.expanduser(f'/mnt/storage/backup/arenas/{datetime.datetime.now()}-{worldname}-{version}.tar.xz')
@@ -91,7 +91,7 @@ if __name__ == "__main__":
if path.exists(arenaworld):
shutil.rmtree(arenaworld)
os.makedirs(f'{arenaworld}/backup', exist_ok=True)
os.makedirs(f'{arenaworld}/backup')
shutil.copy2(f'{builderworld}/level.dat', f'{arenaworld}/backup/level.dat')
shutil.copytree(f'{builderworld}/region', f'{arenaworld}/backup/region')
@@ -219,22 +219,22 @@ IGNORE_ALREADY = §cYou are already ignoring this player.
IGNORE_MESSAGE = §7You are now ignoring §e{0}§8.
#BauCommand
BAU_ADDMEMBER_USAGE = §8/§7build addmember §8[§eworld/all§8] §8[§eplayer§8]
BAU_ADDMEMBER_USAGE = §8/§7build addmember §8[§eplayer§8]
BAU_ADDMEMBER_SELFADD = §cYou don't have to add yourself!
BAU_ADDMEMBER_ISADDED = §cThis player is already a member of your world.
BAU_ADDMEMBER_ADDED = §aThe player was added to your world.
BAU_ADDMEMBER_ADDED_TARGET = §aYou have been added to the world of §e{0}§a.
BAU_TP_USAGE = §8/§7build tp §8[§eplayer§8] §8<§eworld§8>
BAU_TP_USAGE = §8/§7build tp §8[§eplayer§8]
BAU_TP_NOALLOWED = §cYou are not allowed to teleport to this player's world.
BAU_LOCKED_NOALLOWED = §cThe build server is currently locked.
BAU_LOCK_BLOCKED = §cYour build lock has prevented §e{0} §cfrom joining.
BAU_LOCKED_OPTIONS = §8/§7build lock §8[§eworld§8] §8[§eoption§8] §7Options§8: §cnobody§8, §eserverteam§8, §eteam_and_serverteam§8, §eteam§8, §aopen
BAU_LOCKED_OPTIONS = §7Build server lock options§8: §cnobody§8, §eserverteam§8, §eteam_and_serverteam§8, §eteam§8, §aopen
BAU_LOCKED_NOBODY = §7You have locked your build server for all players.
BAU_LOCKED_SERVERTEAM = §7You have locked your build server for all players except added server team members.
BAU_LOCKED_TEAM_AND_SERVERTEAM = §7You have locked your build server for all players except added team members and server team members.
BAU_LOCKED_TEAM = §7You have locked your build server for all players except added team members.
BAU_LOCKED_OPEN = §7You have opened your build server for all added players.
BAU_DELMEMBER_USAGE = §8/§7build delmember §8[§eworld/all§8] §8[§eplayer§8]
BAU_DELMEMBER_USAGE = §8/§7build delmember §8[§eplayer§8]
BAU_DELMEMBER_SELFDEL = §cYou cannot remove yourself!
BAU_DELMEMBER_DELETED = §cPlayer was removed.
BAU_DELMEMBER_DELETED_TARGET = §cYou were removed from the world of §e{0}.
@@ -244,23 +244,9 @@ BAU_DELETE_DELETED = §aYour world is being reset.
BAU_DELETE_GUI_NAME = §eDo you really want to delete the world?
BAU_DELETE_GUI_CANCEL = §cCancel
BAU_DELETE_GUI_DELETE = §aDelete
BAU_WORLD_GUI_TITLE = §eYour worlds
BAU_WORLD_CREATE_USAGE = §8/§7build world create §8[§ename§8] §8[§eversion§8] §8[§etemplate§8]
BAU_WORLD_START_USAGE = §8/§7build world start §8[§eworld§8]
BAU_WORLD_RENAME_USAGE = §8/§7build world rename §8[§eworld§8] §8[§enew name§8]
BAU_WORLD_DELETE_USAGE = §8/§7build world delete §8[§eworld§8]
BAU_WORLD_UPGRADE_USAGE = §8/§7build world upgrade §8[§eworld§8] §8[§eversion§8]
BAU_WORLD_VERSION = §cUnknown version.
BAU_WORLD_UNKNOWN = §cUnknown world.
BAU_WORLD_EXISTS = §cA world with this name already exists for this version.
BAU_WORLD_LIMIT = §cYou have reached your build world limit of §e{0}§c. Delete one first.
BAU_WORLD_OVER_LIMIT_DELETE = §cYou currently have more than your allowed §e{0}§c build worlds. Delete one before creating, renaming, or upgrading worlds.
BAU_WORLD_CREATED = §aWorld created.
BAU_WORLD_RENAMED = §aWorld renamed.
BAU_WORLD_UPGRADED = §aWorld version updated.
BAU_START_ALREADY = §cThis server is already starting.
BAU_MEMBER_NOMEMBER = §cThis player is no member of your world!
BAU_MEMBER_SET_USAGE = §8/§7build {0} §8[§eworld/all§8] §8[§eplayer§8]
BAU_MEMBER_SET_USAGE = §8/§7build {0} §8[§eplayer§8]
BAU_MEMBER_SET_TARGET = §7You are now a §e{1}§7 on the world of §e{0}§7.
BAU_MEMBER_SET = §7The player is now a §e{0}§7.
BAU_MEMBER_SET_SPECTATOR = spectator
@@ -538,20 +524,6 @@ TEAM_EVENT_LEFT = §7Your team no longer takes part in this event
TEAM_EVENT_JOINED = §7Your team now takes part in the event §e{0}§7!
TEAM_EVENT_HOW_TO_LEAVE = §7To cancel the participation repeat the command.
#Team Worlds
TEAM_WORLD_GUI_TITLE = §eTeam worlds
TEAM_WORLD_CREATE_USAGE = §8/§7team world create §8[§ename§8] §8[§eversion§8] §8[§etemplate§8]
TEAM_WORLD_RENAME_USAGE = §8/§7team world rename §8[§eworld§8] §8[§enew name§8]
TEAM_WORLD_UPGRADE_USAGE = §8/§7team world upgrade §8[§eworld§8] §8[§eversion§8]
TEAM_WORLD_DELETE_USAGE = §8/§7team world delete §8[§eworld§8]
TEAM_WORLD_EXISTS = §cA team world with this name already exists for this version.
TEAM_WORLD_LIMIT = §cYour team already has the maximum of §e{0}§c worlds. Delete one first.
TEAM_WORLD_OVER_LIMIT_DELETE = §cYour team currently has more than the allowed §e{0}§c worlds. Delete one before creating, renaming, or upgrading worlds.
TEAM_WORLD_CREATED = §aTeam world created.
TEAM_WORLD_RENAMED = §aTeam world renamed.
TEAM_WORLD_UPGRADED = §aTeam world version updated.
TEAM_WORLD_DELETED = §aTeam world deleted.
#Team Color
TEAM_COLOR_TITLE = Choose color
@@ -710,7 +682,7 @@ LOCK_LOCALE_CHANGED = §aLanguage saved
#Builder Cloud
BUILDERCLOUD_USAGE = §8/§7buildercloud §8[§eversion§8] §8[§emap§8]
BUILDERCLOUD_CREATE_USAGE = §8/§7buildercloud create §8[§eversion§8] §8[§emap§8] §8[§etemplate§8]
BUILDERCLOUD_CREATE_USAGE = §8/§7buildercloud create §8[§eversion§8] §8[§emap§8] §8<§7generator§8>
BUILDERCLOUD_RENAME_USAGE = §8/§7buildercloud rename §8[§eversion§8] §8[§emap§8] §8[§enew name§8]
BUILDERCLOUD_DEPLOY_USAGE = §8/§7deployarena §8[§egamemode§8] §8[§eversion§8] §8[§emap§8]
BUILDERCLOUD_DEPLOY_FINISHED = §7Map deployment finished.
@@ -201,22 +201,22 @@ IGNORE_ALREADY = §cDu ignorierst diesen Spieler bereits.
IGNORE_MESSAGE = §7Du ignorierst nun §e{0}§8.
#BauCommand
BAU_ADDMEMBER_USAGE = §8/§7bau addmember §8[§eWelt/all§8] §8[§eSpieler§8]
BAU_ADDMEMBER_USAGE = §8/§7bau addmember §8[§eSpieler§8]
BAU_ADDMEMBER_SELFADD = §cDu brauchst dich nicht selbst hinzufügen!
BAU_ADDMEMBER_ISADDED = §cDieser Spieler ist bereits Mitglied auf deiner Welt.
BAU_ADDMEMBER_ADDED = §aDer Spieler wurde zu deiner Welt hinzugefügt.
BAU_ADDMEMBER_ADDED_TARGET = §aDu wurdest zu der Welt von §e{0} §ahinzugefügt.
BAU_TP_USAGE = §8/§7bau tp §8[§eSpieler§8] §8<§eWelt§8>
BAU_TP_USAGE = §8/§7bau tp §8[§eSpieler§8]
BAU_TP_NOALLOWED = §cDu darfst dich nicht auf diese Welt teleportieren.
BAU_LOCKED_NOALLOWED = §cDer Bauserver ist momentan gesperrt.
BAU_LOCK_BLOCKED = §cDeine Bausperre hat den Beitritt von §e{0} §cverhindert.
BAU_LOCKED_OPTIONS = §8/§7bau lock §8[§eWelt§8] §8[§eOption§8] §7Optionen§8: §cnobody§8, §eserverteam§8, §eteam_and_serverteam§8, §eteam§8, §aopen
BAU_LOCKED_OPTIONS = §7Bauserver-Sperroptionen§8: §cnobody§8, §eserverteam§8, §eteam_and_serverteam§8, §eteam§8, §aopen
BAU_LOCKED_NOBODY = §7Du hast deinen Bau für alle Spieler geschlossen.
BAU_LOCKED_SERVERTEAM = §7Du hast deinen Bau für alle außer hinzugefügte Serverteammitglieder gesperrt.
BAU_LOCKED_TEAM_AND_SERVERTEAM = §7Du hast deinen Bau für alle außer hinzugefügte Teammitglieder und Serverteammitglieder gesperrt.
BAU_LOCKED_TEAM = §7Du hast deinen Bau für alle außer hinzugefügte Teammitglieder gesperrt.
BAU_LOCKED_OPEN = §7Du hast deinen Bau für alle hinzugefügten Spieler geöffnet.
BAU_DELMEMBER_USAGE = §8/§7bau delmember §8[§eWelt/all§8] §8[§eSpieler§8]
BAU_DELMEMBER_USAGE = §8/§7bau delmember §8[§eSpieler§8]
BAU_DELMEMBER_SELFDEL = §cDu kannst dich nicht selbst entfernen!
BAU_DELMEMBER_DELETED = §cDer Spieler wurde entfernt.
BAU_DELMEMBER_DELETED_TARGET = §cDu wurdest von der Welt von §e{0} §centfernt.
@@ -226,23 +226,9 @@ BAU_DELETE_DELETED = §aDeine Welt wird zurückgesetzt.
BAU_DELETE_GUI_NAME = §eWirklich Welt löschen?
BAU_DELETE_GUI_CANCEL = §cAbbrechen
BAU_DELETE_GUI_DELETE = §aLöschen
BAU_WORLD_GUI_TITLE = §eDeine Welten
BAU_WORLD_CREATE_USAGE = §8/§7bau world create §8[§eName§8] §8[§eVersion§8] §8[§eTemplate§8]
BAU_WORLD_START_USAGE = §8/§7bau world start §8[§eWelt§8]
BAU_WORLD_RENAME_USAGE = §8/§7bau world rename §8[§eWelt§8] §8[§eNeuer Name§8]
BAU_WORLD_DELETE_USAGE = §8/§7bau world delete §8[§eWelt§8]
BAU_WORLD_UPGRADE_USAGE = §8/§7bau world upgrade §8[§eWelt§8] §8[§eVersion§8]
BAU_WORLD_VERSION = §cUnbekannte Version.
BAU_WORLD_UNKNOWN = §cUnbekannte Welt.
BAU_WORLD_EXISTS = §cEine Welt mit diesem Namen existiert bereits in dieser Version.
BAU_WORLD_LIMIT = §cDu hast dein Bauwelt-Limit von §e{0}§c erreicht. Lösche zuerst eine Welt.
BAU_WORLD_OVER_LIMIT_DELETE = §cDu hast aktuell mehr als die erlaubten §e{0}§c Bauwelten. Lösche eine Welt, bevor du Welten erstellst, umbenennst oder upgradest.
BAU_WORLD_CREATED = §aWelt erstellt.
BAU_WORLD_RENAMED = §aWelt umbenannt.
BAU_WORLD_UPGRADED = §aWelt-Version aktualisiert.
BAU_START_ALREADY = §cDer Server startet bereits.
BAU_MEMBER_NOMEMBER = §cDer Spieler ist kein Mitglied deiner Welt!
BAU_MEMBER_SET_USAGE = §8/§7bau {0} §8[§eWelt/all§8] §8[§eSpieler§8]
BAU_MEMBER_SET_USAGE = §8/§7bau {0} §8[§eSpieler§8]
BAU_MEMBER_SET_TARGET = §7Du bist nun ein §e{1}§7 auf der Welt von §e{0}§7.
BAU_MEMBER_SET = §7Der Spieler ist nun §e{0}§7.
BAU_MEMBER_SET_SPECTATOR = Zuschauer
@@ -505,20 +491,6 @@ TEAM_EVENT_LEFT = §7Dein Team nimmt nicht mehr am Event teil
TEAM_EVENT_JOINED = §7Dein Team nimmt nun am Event §e{0} §7 teil!
TEAM_EVENT_HOW_TO_LEAVE = §7Um die Teilnahme abzusagen, wiederhole den Befehl.
#Team Worlds
TEAM_WORLD_GUI_TITLE = §eTeamwelten
TEAM_WORLD_CREATE_USAGE = §8/§7team world create §8[§eName§8] §8[§eVersion§8] §8[§eTemplate§8]
TEAM_WORLD_RENAME_USAGE = §8/§7team world rename §8[§eWelt§8] §8[§eNeuer Name§8]
TEAM_WORLD_UPGRADE_USAGE = §8/§7team world upgrade §8[§eWelt§8] §8[§eVersion§8]
TEAM_WORLD_DELETE_USAGE = §8/§7team world delete §8[§eWelt§8]
TEAM_WORLD_EXISTS = §cEine Teamwelt mit diesem Namen existiert bereits in dieser Version.
TEAM_WORLD_LIMIT = §cDein Team hat bereits das Maximum von §e{0}§c Welten. Lösche zuerst eine Welt.
TEAM_WORLD_OVER_LIMIT_DELETE = §cDein Team hat aktuell mehr als die erlaubten §e{0}§c Welten. Lösche eine Welt, bevor du Welten erstellst, umbenennst oder upgradest.
TEAM_WORLD_CREATED = §aTeamwelt erstellt.
TEAM_WORLD_RENAMED = §aTeamwelt umbenannt.
TEAM_WORLD_UPGRADED = §aTeamwelt-Version aktualisiert.
TEAM_WORLD_DELETED = §aTeamwelt gelöscht.
#Team Color
TEAM_COLOR_TITLE = Farbe wählen
@@ -672,7 +644,7 @@ LOCK_LOCALE_CHANGED = §aSprache gespeichert
#Builder Cloud
BUILDERCLOUD_USAGE = §8/§7buildercloud §8[§eVersion§8] §8[§eWelt§8]
BUILDERCLOUD_CREATE_USAGE = §8/§7buildercloud create §8[§eVersion§8] §8[§eWelt§8] §8[§eTemplate§8]
BUILDERCLOUD_CREATE_USAGE = §8/§7buildercloud create §8[§eVersion§8] §8[§eWelt§8] §8<§7Generator§8>
BUILDERCLOUD_RENAME_USAGE = §8/§7buildercloud rename §8[§eVersion§8] §8[§eWElt§8] §8[§eNeuer Name§8]
BUILDERCLOUD_VERSION = §cUnbekannte Version.
BUILDERCLOUD_EXISTING_MAP = §cWelt existiert bereits.
@@ -51,9 +51,11 @@ public class ServerStarter {
private static final String EVENT_PATH = TMP_DATA + "event/";
public static final String TEMP_WORLD_PATH = TMP_DATA + "arenaserver/";
public static final String WORLDS_BASE_PATH = SteamwarWorld.WORLD_STORAGE + "/";
public static final String LEGACY_WORLDS_BASE_PATH = "/worlds/userworlds";
public static final String LEGACY_BUILDER_BASE_PATH = "/worlds/builder";
private static final String WORLDS_FOLDER = "/worlds";
public static final String WORLDS_BASE_PATH = WORLDS_FOLDER + "/userworlds";
public static final String BUILDER_BASE_PATH = WORLDS_FOLDER + "/builder";
private static final String WORLDS_STORAGE_BASE_PATH = "/mnt/storage/worlds/userworlds";
private File directory = null;
private String worldDir = null;
@@ -89,8 +91,7 @@ public class ServerStarter {
gameMode = mode.configFile.getName().replace(".yml", "");
directory = new File(SERVER_PATH, mode.Server.Folder);
arguments.put("config", mode.configFile.getName());
SteamwarWorld arenaWorld = SteamwarWorld.getOrCreateArenaWorld(gameMode, map, version.getVersionSuffix());
tempWorld(arenaWorld.setupAndGetStoragePath());
tempWorld(SERVER_PATH + mode.Server.Folder + "/arenas/" + map);
startCondition = () -> {
if (playersToSend.stream().anyMatch(player -> Subserver.isArena(Subserver.getSubserver(player)))) {
playersToSend.forEach(player -> Chatter.of(player).system("FIGHT_IN_ARENA"));
@@ -152,36 +153,30 @@ public class ServerStarter {
}
public ServerStarter build(ServerVersion version, UUID owner) {
this.version = version;
SteamwarUser user = SteamwarUser.get(owner);
return build(version, SteamwarWorld.getOrCreateBauWorld(user, version.getVersionSuffix()));
}
public ServerStarter build(ServerVersion version, SteamwarWorld world) {
this.version = version;
directory = version.getServerDirectory("Bau");
SteamwarUser user = world.getOwner() == null ? null : SteamwarUser.byId(world.getOwner().getValue());
worldDir = WORLDS_BASE_PATH;
worldName = world.getUuid().toString();
worldDir = version.getWorldFolder(WORLDS_BASE_PATH);
worldName = String.valueOf(SteamwarUser.get(owner).getId());
checkpoint = true;
if (world.getType() == WorldType.TEAM) {
build(world.getUuid());
Team team = Team.byId(world.getTeam().getValue());
serverNameProvider = port -> team.getTeamKuerzel() + "s Bau";
arguments.put("bauTeam", String.valueOf(world.getTeam().getValue()));
} else {
build(user.getUUID(), world.getUuid());
arguments.put("bauOwner", String.valueOf(user.getId()));
}
arguments.put("bauWorld", world.getUuid().toString());
arguments.put("bauType", world.getType().name());
build(owner);
worldSetup = () -> world.setupAndGetStoragePath(new File(directory, "Bauwelt"));
worldSetup = () -> {
File world = new File(worldDir, worldName);
if (!world.exists()) {
File storage = new File(version.getWorldFolder(WORLDS_STORAGE_BASE_PATH), worldName);
if (storage.exists()) {
node.execute("mv", storage.getPath(), world.getPath());
} else {
copyWorld(node, new File(directory, "Bauwelt").getPath(), world.getPath());
}
}
};
// Send players to existing server
startCondition = () -> {
Bauserver subserver = Bauserver.getByWorld(world.getUuid());
Bauserver subserver = Bauserver.get(owner);
if (subserver != null) {
for (Player p : playersToSend) {
SubserverSystem.sendPlayer(subserver, p);
@@ -189,13 +184,9 @@ public class ServerStarter {
return false;
}
boolean atLeastOneSupervisor = playersToSend.stream().anyMatch(player -> {
SteamwarUser playerUser = SteamwarUser.get(player.getUniqueId());
if (world.getType() == WorldType.TEAM) {
return playerUser.getTeam() == world.getTeam().getValue();
}
if (user != null && playerUser.getId() == user.getId()) return true;
BauweltMember bauweltMember = BauweltMember.getBauMember(world.getUuid(), playerUser.getId());
return bauweltMember != null && bauweltMember.isSupervisor();
if (player.getUniqueId().equals(owner)) return true;
BauweltMember bauweltMember = BauweltMember.getBauMember(owner, player.getUniqueId());
return bauweltMember.isSupervisor();
});
if (!atLeastOneSupervisor) {
for (Player p : playersToSend) {
@@ -229,20 +220,15 @@ public class ServerStarter {
}
private void build(UUID owner) {
build(owner, owner);
}
private void build(UUID owner, UUID world) {
constructor = (serverName, port, builder, shutdownCallback, failureCallback) -> new Bauserver(serverName, owner, world, port, builder, shutdownCallback, failureCallback);
constructor = (serverName, port, builder, shutdownCallback, failureCallback) -> new Bauserver(serverName, owner, port, builder, shutdownCallback, failureCallback);
serverNameProvider = port -> bauServerName(SteamwarUser.get(owner));
}
public ServerStarter builder(ServerVersion version, String map, File prototype) {
public ServerStarter builder(ServerVersion version, String map, File generator) {
this.version = version;
directory = version.getServerDirectory("Builder");
SteamwarWorld world = SteamwarWorld.getOrCreateBuilderWorld(map, version.getVersionSuffix());
worldDir = WORLDS_BASE_PATH;
worldName = world.getUuid().toString();
worldDir = version.getWorldFolder(BUILDER_BASE_PATH);
worldName = map;
serverNameProvider = port -> "*" + map;
checkpoint = true;
constructor = (serverName, port, builder, shutdownCallback, failureCallback) -> new Builderserver(serverName, worldName, port, builder, shutdownCallback, failureCallback);
@@ -259,7 +245,16 @@ public class ServerStarter {
return true;
};
worldSetup = () -> world.setupAndGetStoragePath(prototype != null ? prototype : legacyBuilderWorld(version, map));
if (generator != null) {
worldSetup = () -> {
File leveldat = new File(new File(worldDir, worldName), "level.dat");
try {
Files.copy(generator.toPath(), leveldat.toPath());
} catch (IOException e) {
throw new SecurityException(e);
}
};
}
return this;
}
@@ -363,22 +358,6 @@ public class ServerStarter {
node.execute("cp", "-r", template, target);
}
private static File legacyBauWorld(ServerVersion version, SteamwarUser user) {
File legacyIdWorld = new File(version.getWorldFolder(LEGACY_WORLDS_BASE_PATH), String.valueOf(user.getId()));
if(legacyIdWorld.exists())
return legacyIdWorld;
File legacyUuidWorld = new File(version.getWorldFolder(LEGACY_WORLDS_BASE_PATH), user.getUUID().toString());
if(legacyUuidWorld.exists())
return legacyUuidWorld;
return new File(version.getServerDirectory("Bau"), "Bauwelt");
}
public static File legacyBuilderWorld(ServerVersion version, String map) {
return new File(version.getWorldFolder(LEGACY_BUILDER_BASE_PATH), map);
}
private interface ServerConstructor {
Subserver construct(String serverName, int port, ProcessBuilder builder, Runnable shutdownCallback, Consumer<Exception> failureCallback);
}
@@ -417,4 +396,4 @@ public class ServerStarter {
}
}
}
}
@@ -35,19 +35,16 @@ import de.steamwar.persistent.Bauserver;
import de.steamwar.sql.BauweltMember;
import de.steamwar.sql.GameModeConfig;
import de.steamwar.sql.SteamwarUser;
import de.steamwar.sql.SteamwarWorld;
import de.steamwar.sql.UserPerm;
import de.steamwar.velocitycore.*;
import de.steamwar.velocitycore.ServerStarter;
import de.steamwar.velocitycore.ServerVersion;
import de.steamwar.velocitycore.SubserverSystem;
import de.steamwar.velocitycore.VelocityCore;
import de.steamwar.velocitycore.inventory.SWInventory;
import de.steamwar.velocitycore.inventory.SWItem;
import de.steamwar.velocitycore.inventory.SWListInv;
import de.steamwar.velocitycore.network.NetworkSender;
import de.steamwar.velocitycore.util.BauLock;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.function.Consumer;
@Linked
@@ -67,13 +64,13 @@ public class BauCommand extends SWCommand {
}
@Register
public void toBau(PlayerChatter sender, @Mapper("ownBauWorld") @OptionalValue(value = "", onlyUINIG = true) @AllowNull SteamwarWorld world) {
startPersonalWorld(sender, world);
public void toBau(PlayerChatter sender, @OptionalValue(value = "", onlyUINIG = true) ServerVersion version) {
new ServerStarter().build(version, sender.user().getUUID()).send(sender.getPlayer()).start();
}
@Register(value = "addmember", description = "BAU_ADDMEMBER_USAGE")
public void addmember(Chatter sender, @Mapper("bauWorldSelector") String world, @Validator("addMemberTarget") SteamwarUser target) {
if (!forEachSelectedWorld(sender, world, bauworld -> BauweltMember.addMember(bauworld.getUuid(), target.getId()))) return;
public void addmember(Chatter sender, @Validator("addMemberTarget") SteamwarUser target) {
BauweltMember.addMember(sender.user().getUUID(), target.getUUID());
sender.system("BAU_ADDMEMBER_ADDED");
Chatter.of(target.getUUID()).system("BAU_ADDMEMBER_ADDED_TARGET", sender);
}
@@ -89,31 +86,18 @@ public class BauCommand extends SWCommand {
messageSender.send("BAU_ADDMEMBER_SELFADD");
return false;
}
if (BauweltMember.getBauMember(sender.user().getId(), value.getId()) != null) {
messageSender.send("BAU_ADDMEMBER_ISADDED");
return false;
}
return true;
};
}
@Register(value = "tp", description = "BAU_TP_USAGE")
@Register("teleport")
public void teleport(PlayerChatter sender, @Validator("teleportTarget") SteamwarUser worldOwner, @Mapper("targetBauWorld") @OptionalValue(value = "", onlyUINIG = true) @AllowNull SteamwarWorld world) {
if (world == null) {
world = defaultPersonalWorld(worldOwner);
}
if (world == null) {
sender.system("BAU_WORLD_UNKNOWN");
return;
}
if (sender.user().getId() != worldOwner.getId() && BauLock.isLocked(world, sender.user())) {
sender.system("BAU_LOCKED_NOALLOWED");
Chatter.of(worldOwner.getUUID()).system("BAU_LOCK_BLOCKED", sender);
return;
}
if (sender.user().getId() != worldOwner.getId() && BauweltMember.getBauMember(world.getUuid(), sender.user().getId()) == null) {
SubserverSystem.sendDeniedMessage(sender, worldOwner.getUUID());
sender.system("BAU_TP_NOALLOWED");
return;
}
new ServerStarter().build(ServerVersion.get(world.getVersion()), world).send(sender.getPlayer()).start();
public void teleport(PlayerChatter sender, @Validator("teleportTarget") SteamwarUser worldOwner, @OptionalValue(value = "", onlyUINIG = true) ServerVersion version) {
new ServerStarter().build(version, worldOwner.getUUID()).send(sender.getPlayer()).start();
}
@Validator(value = "teleportTarget", local = true)
@@ -123,6 +107,17 @@ public class BauCommand extends SWCommand {
messageSender.send("UNKNOWN_PLAYER");
return false;
}
if (sender.user().getId() != owner.getId() && BauweltMember.getBauMember(owner.getId(), sender.user().getId()) == null) {
SubserverSystem.sendDeniedMessage(sender, owner.getUUID());
messageSender.send("BAU_TP_NOALLOWED");
return false;
}
if (BauLock.isLocked(owner, sender.user())) {
messageSender.send("BAU_LOCKED_NOALLOWED");
Chatter.of(owner.getUUID()).system("BAU_LOCK_BLOCKED", sender);
return false;
}
return true;
};
}
@@ -133,39 +128,39 @@ public class BauCommand extends SWCommand {
}
@Register("setspectator")
public void setSpectator(Chatter sender, @Mapper("bauWorldSelector") String world, @Mapper("addedUsers") @AllowNull @OptionalValue("") SteamwarUser user) {
setPerms(sender, world, user, "setspectator", "BAU_MEMBER_SET_SPECTATOR", member -> {
public void setSpectator(Chatter sender, @Mapper("addedUsers") @AllowNull @OptionalValue("") SteamwarUser user) {
setPerms(sender, user, "setspectator", "BAU_MEMBER_SET_SPECTATOR", member -> {
member.setBuild(false);
member.setSupervisor(false);
});
}
@Register("setbuilder")
public void setBuilder(Chatter sender, @Mapper("bauWorldSelector") String world, @Mapper("addedUsers") @AllowNull @OptionalValue("") SteamwarUser user) {
setPerms(sender, world, user, "setbuild", "BAU_MEMBER_SET_BUILDER", member -> {
public void setBuilder(Chatter sender, @Mapper("addedUsers") @AllowNull @OptionalValue("") SteamwarUser user) {
setPerms(sender, user, "setbuild", "BAU_MEMBER_SET_BUILDER", member -> {
member.setBuild(true);
member.setSupervisor(false);
});
}
@Register("setsupervisor")
public void setSupervisor(Chatter sender, @Mapper("bauWorldSelector") String world, @Mapper("addedUsers") @AllowNull @OptionalValue("") SteamwarUser user) {
setPerms(sender, world, user, "setsupervisor", "BAU_MEMBER_SET_SUPERVISOR", member -> {
public void setSupervisor(Chatter sender, @Mapper("addedUsers") @AllowNull @OptionalValue("") SteamwarUser user) {
setPerms(sender, user, "setsupervisor", "BAU_MEMBER_SET_SUPERVISOR", member -> {
member.setBuild(true);
member.setSupervisor(true);
});
}
private void setPerms(Chatter owner, String world, SteamwarUser user, String name, String permName, Consumer<BauweltMember> setter) {
private void setPerms(Chatter owner, SteamwarUser user, String name, String permName, Consumer<BauweltMember> setter) {
if (user == null) {
owner.system("BAU_MEMBER_SET_USAGE", name);
return;
}
withMembers(owner, world, user, target -> {
withMember(owner, user, target -> {
setter.accept(target);
Bauserver bauserver = Bauserver.getByWorld(target.getWorldID());
Bauserver bauserver = Bauserver.get(owner.user().getUUID());
if (bauserver != null) {
bauserver.getRegisteredServer().getPlayersConnected().stream().findAny().ifPresent(player -> NetworkSender.send(player, new BaumemberUpdatePacket()));
}
@@ -176,11 +171,11 @@ public class BauCommand extends SWCommand {
}
@Register(value = "delmember", description = "BAU_DELMEMBER_USAGE")
public void delmember(Chatter owner, @Mapper("bauWorldSelector") String world, @Mapper("addedUsers") SteamwarUser user) {
withMembers(owner, world, user, target -> {
public void delmember(Chatter owner, @Mapper("addedUsers") SteamwarUser user) {
withMember(owner, user, target -> {
target.remove();
Bauserver bauserver = Bauserver.getByWorld(target.getWorldID());
Bauserver bauserver = Bauserver.get(owner.user().getUUID());
Chatter member = Chatter.of(user.getUUID());
member.withPlayer(player -> {
if (bauserver != null && bauserver.getRegisteredServer().getPlayersConnected().contains(player)) {
@@ -197,29 +192,6 @@ public class BauCommand extends SWCommand {
});
}
@Mapper(value = "bauWorldSelector", local = true)
public TypeMapper<String> bauWorldSelector() {
return new TypeMapper<>() {
@Override
public String map(Chatter sender, PreviousArguments previousArguments, String s) {
if ("all".equalsIgnoreCase(s)) return "all";
return SteamwarWorld.getBauWorlds(sender.user()).stream()
.map(SteamwarWorld::getName)
.filter(name -> name.equalsIgnoreCase(s))
.findFirst()
.orElse(null);
}
@Override
public Collection<String> tabCompletes(Chatter sender, PreviousArguments previousArguments, String s) {
List<String> worlds = new ArrayList<>();
worlds.add("all");
SteamwarWorld.getBauWorlds(sender.user()).stream().map(SteamwarWorld::getName).forEach(worlds::add);
return worlds;
}
};
}
@Mapper(value = "addedUsers", local = true)
public TypeMapper<SteamwarUser> addedUsers() {
return new TypeMapper<SteamwarUser>() {
@@ -230,10 +202,8 @@ public class BauCommand extends SWCommand {
@Override
public Collection<String> tabCompletes(Chatter sender, PreviousArguments previousArguments, String s) {
return selectedWorlds(sender, previousArguments.userArgs.length == 0 ? "all" : previousArguments.userArgs[previousArguments.userArgs.length - 1]).stream()
.flatMap(world -> BauweltMember.getWorldMembers(world.getUuid()).stream())
return BauweltMember.getMembers(sender.user().getId()).stream()
.map(bauweltMember -> SteamwarUser.byId(bauweltMember.getMemberID()).getUserName())
.distinct()
.toList();
}
};
@@ -243,11 +213,9 @@ public class BauCommand extends SWCommand {
public void stop(PlayerChatter sender) {
VelocityCore.schedule(() -> {
sender.system("BAU_STOPPING");
for (SteamwarWorld world : SteamwarWorld.getBauWorlds(sender.user())) {
Bauserver subserver = Bauserver.getByWorld(world.getUuid());
if (subserver != null) {
subserver.stop();
}
Bauserver subserver = Bauserver.get(sender.user().getUUID());
if (subserver != null) {
subserver.stop();
}
sender.system("BAU_STOPPED");
@@ -256,50 +224,25 @@ public class BauCommand extends SWCommand {
@Register("resetall")
@Register("delete")
public void delete(PlayerChatter sender, @Mapper("ownBauWorld") SteamwarWorld world) {
openDeleteWorld(sender, world);
}
public void delete(PlayerChatter sender, ServerVersion version) {
SWInventory inventory = new SWInventory(sender, 9, new Message("BAU_DELETE_GUI_NAME"));
inventory.addItem(0, new SWItem("LIME_DYE", new Message("BAU_DELETE_GUI_DELETE")), click -> {
String world = version.getWorldFolder(ServerStarter.WORLDS_BASE_PATH) + sender.user().getId();
@Register("worlds")
@Register("world")
public void worlds(PlayerChatter sender) {
openWorldList(sender);
}
VelocityCore.schedule(() -> {
Bauserver subserver = Bauserver.get(sender.user().getUUID());
if (subserver != null) {
subserver.stop();
}
@Register(value = "world create", description = "BAU_WORLD_CREATE_USAGE")
public void createWorld(PlayerChatter sender, String name, @ErrorMessage("BAU_WORLD_VERSION") ServerVersion version, @Mapper("templateWorld") SteamwarWorld template) {
SteamwarWorld world = createPersonalWorld(sender, name, version, template);
if (world != null) {
sender.system("BAU_WORLD_CREATED");
}
}
SubserverSystem.deleteFolder(VelocityCore.local, world);
sender.system("BAU_DELETE_DELETED");
}).schedule();
@Register(value = "world start", description = "BAU_WORLD_START_USAGE")
public void startWorld(PlayerChatter sender, @Mapper("ownBauWorld") SteamwarWorld world) {
new ServerStarter().build(ServerVersion.get(world.getVersion()), world).send(sender.getPlayer()).start();
}
@Register(value = "world rename", description = "BAU_WORLD_RENAME_USAGE")
public void renameWorld(Chatter sender, @Mapper("ownBauWorld") SteamwarWorld world, String newName) {
if (hasTooManyPersonalWorlds(sender)) return;
if (SteamwarWorld.getBauWorld(sender.user(), newName, world.getVersion()) != null) {
sender.system("BAU_WORLD_EXISTS");
return;
}
world.rename(newName);
sender.system("BAU_WORLD_RENAMED");
}
@Register(value = "world delete", description = "BAU_WORLD_DELETE_USAGE")
public void deleteWorld(PlayerChatter sender, @Mapper("ownBauWorld") SteamwarWorld world) {
openDeleteWorld(sender, world);
}
@Register(value = "world upgrade", description = "BAU_WORLD_UPGRADE_USAGE")
public void upgradeWorld(Chatter sender, @Mapper("ownBauWorld") SteamwarWorld world, @ErrorMessage("BAU_WORLD_VERSION") ServerVersion version) {
if (hasTooManyPersonalWorlds(sender)) return;
world.changeVersion(version.getVersionSuffix());
sender.system("BAU_WORLD_UPGRADED");
inventory.close();
});
inventory.addItem(8, new SWItem("RED_DYE", new Message("BAU_DELETE_GUI_CANCEL")), click -> inventory.close());
inventory.open();
}
@Register("test")
@@ -311,244 +254,27 @@ public class BauCommand extends SWCommand {
}
@Register(value = "lock", description = "BAU_LOCKED_OPTIONS")
public void lock(Chatter sender, @Mapper("ownBauWorld") SteamwarWorld world, BauLockState bauLockState) {
BauLock.setLocked(sender, world, bauLockState);
public void lock(Chatter sender, BauLockState bauLockState) {
BauLock.setLocked(sender, bauLockState);
}
@Register("unlock")
public void unlock(Chatter sender, @Mapper("ownBauWorld") SteamwarWorld world) {
BauLock.setLocked(sender, world, BauLockState.OPEN);
public void unlock(Chatter sender) {
BauLock.setLocked(sender, BauLockState.OPEN);
}
private static void withMembers(Chatter owner, String worldSelector, SteamwarUser member, Consumer<BauweltMember> function) {
private static void withMember(Chatter owner, SteamwarUser member, Consumer<BauweltMember> function) {
if (member == null) {
owner.system("UNKNOWN_PLAYER");
return;
}
List<SteamwarWorld> worlds = selectedWorlds(owner, worldSelector);
if (worlds.isEmpty()) {
owner.system("BAU_WORLD_UNKNOWN");
return;
}
boolean found = false;
for (SteamwarWorld world : worlds) {
BauweltMember target = BauweltMember.getBauMember(world.getUuid(), member.getId());
if (target == null) continue;
found = true;
function.accept(target);
}
if (!found) {
BauweltMember target = BauweltMember.getBauMember(owner.user().getId(), member.getId());
if (target == null) {
owner.system("BAU_MEMBER_NOMEMBER");
}
}
@Mapper(value = "ownBauWorld", local = true)
private TypeMapper<SteamwarWorld> ownBauWorldMapper() {
return new TypeMapper<SteamwarWorld>() {
@Override
public SteamwarWorld map(Chatter sender, PreviousArguments previousArguments, String s) {
if (s == null || s.isEmpty()) return defaultPersonalWorld(sender);
return SteamwarWorld.getBauWorlds(sender.user()).stream()
.filter(world -> world.getName().equalsIgnoreCase(s) || world.getUuid().toString().equalsIgnoreCase(s))
.findFirst()
.orElse(null);
}
@Override
public Collection<String> tabCompletes(Chatter sender, PreviousArguments previousArguments, String s) {
return SteamwarWorld.getBauWorlds(sender.user()).stream().map(SteamwarWorld::getName).toList();
}
};
}
@Mapper(value = "targetBauWorld", local = true)
private TypeMapper<SteamwarWorld> targetBauWorldMapper() {
return new TypeMapper<>() {
@Override
public SteamwarWorld map(Chatter sender, PreviousArguments previousArguments, String s) {
SteamwarUser owner = previousArguments.getAll(SteamwarUser.class).stream().findFirst().orElse(null);
if (owner == null) return null;
if (s == null || s.isEmpty()) return defaultPersonalWorld(owner);
return SteamwarWorld.getBauWorlds(owner).stream()
.filter(world -> world.getName().equalsIgnoreCase(s) || world.getUuid().toString().equalsIgnoreCase(s))
.findFirst()
.orElse(null);
}
@Override
public Collection<String> tabCompletes(Chatter sender, PreviousArguments previousArguments, String s) {
SteamwarUser owner = previousArguments.getAll(SteamwarUser.class).stream().findFirst().orElse(null);
if (owner == null) return List.of();
return SteamwarWorld.getBauWorlds(owner).stream().map(SteamwarWorld::getName).toList();
}
};
}
@Mapper(value = "templateWorld", local = true)
private TypeMapper<SteamwarWorld> templateWorldMapper() {
return new TypeMapper<>() {
@Override
public SteamwarWorld map(Chatter sender, PreviousArguments previousArguments, String s) {
ServerVersion version = previousArguments.getAll(ServerVersion.class).stream().findFirst().orElse(null);
if (version == null) return null;
return SteamwarWorld.getTemplateWorld(s, version.getVersionSuffix());
}
@Override
public Collection<String> tabCompletes(Chatter sender, PreviousArguments previousArguments, String s) {
ServerVersion version = previousArguments.getAll(ServerVersion.class).stream().findFirst().orElse(null);
if (version == null) return List.of();
return SteamwarWorld.getTemplateWorlds(version.getVersionSuffix()).stream().map(SteamwarWorld::getName).toList();
}
};
}
private void openWorldList(PlayerChatter sender) {
List<SWListInv.SWListEntry<SteamwarWorld>> entries = new ArrayList<>();
for (SteamwarWorld world : SteamwarWorld.getBauWorlds(sender.user())) {
SWItem item = new SWItem("GRASS_BLOCK", new Message("PLAIN_STRING", "§e" + world.getName()))
.addLore(new Message("PLAIN_STRING", "§7Version: §e" + world.getVersion()))
.addLore(new Message("PLAIN_STRING", "§7Left: start, right: manage"));
entries.add(new SWListInv.SWListEntry<>(item, world));
}
SWListInv<SteamwarWorld> inventory = new SWListInv<>(sender, new Message("BAU_WORLD_GUI_TITLE"), entries, (click, world) -> {
if (click.isRightClick()) {
openWorldManagement(sender, world);
} else {
new ServerStarter().build(ServerVersion.get(world.getVersion()), world).send(sender.getPlayer()).start();
}
});
inventory.open();
}
private void openWorldManagement(PlayerChatter sender, SteamwarWorld world) {
SWInventory inventory = new SWInventory(sender, 9, new Message("PLAIN_STRING", "§e" + world.getName()));
inventory.addItem(0, new SWItem("ENDER_PEARL", new Message("PLAIN_STRING", "§aStart")), click -> {
inventory.close();
new ServerStarter().build(ServerVersion.get(world.getVersion()), world).send(sender.getPlayer()).start();
});
inventory.addItem(3, new SWItem("EXPERIENCE_BOTTLE", new Message("PLAIN_STRING", "§eUpgrade to latest")), click -> {
if (hasTooManyPersonalWorlds(sender)) {
inventory.close();
return;
}
world.changeVersion(ServerVersion.PAPER_21.getVersionSuffix());
sender.system("BAU_WORLD_UPGRADED");
inventory.close();
});
inventory.addItem(8, new SWItem("BARRIER", new Message("PLAIN_STRING", "§cDelete")), click -> openDeleteWorld(sender, world));
inventory.open();
}
private void openDeleteWorld(PlayerChatter sender, SteamwarWorld world) {
SWInventory inventory = new SWInventory(sender, 9, new Message("BAU_DELETE_GUI_NAME"));
inventory.addItem(0, new SWItem("LIME_DYE", new Message("BAU_DELETE_GUI_DELETE")), click -> {
VelocityCore.schedule(() -> {
Bauserver subserver = Bauserver.getByWorld(world.getUuid());
if (subserver != null) {
subserver.stop();
}
world.markDeleted();
SubserverSystem.deleteFolder(VelocityCore.local, world.getStorageDirectory().getPath());
sender.system("BAU_DELETE_DELETED");
}).schedule();
inventory.close();
});
inventory.addItem(8, new SWItem("RED_DYE", new Message("BAU_DELETE_GUI_CANCEL")), click -> inventory.close());
inventory.open();
}
private void startPersonalWorld(PlayerChatter sender, SteamwarWorld world) {
if (world == null) {
world = defaultPersonalWorld(sender);
}
if (world == null) {
sender.system("BAU_WORLD_UNKNOWN");
return;
}
new ServerStarter().build(ServerVersion.get(world.getVersion()), world).send(sender.getPlayer()).start();
}
private static SteamwarWorld defaultPersonalWorld(Chatter sender) {
return defaultPersonalWorld(sender.user());
}
private static SteamwarWorld defaultPersonalWorld(SteamwarUser user) {
return SteamwarWorld.getBauWorlds(user).stream().findFirst().orElse(null);
}
private static List<SteamwarWorld> selectedWorlds(Chatter sender, String selector) {
List<SteamwarWorld> worlds = SteamwarWorld.getBauWorlds(sender.user());
if ("all".equalsIgnoreCase(selector)) {
return worlds;
}
String normalized = selector.toLowerCase(Locale.ROOT);
return worlds.stream()
.filter(world -> world.getName().toLowerCase(Locale.ROOT).equals(normalized))
.toList();
}
private static boolean forEachSelectedWorld(Chatter sender, String selector, Consumer<SteamwarWorld> consumer) {
List<SteamwarWorld> worlds = selectedWorlds(sender, selector);
if (worlds.isEmpty()) {
sender.system("BAU_WORLD_UNKNOWN");
return false;
}
worlds.forEach(consumer);
return true;
}
private SteamwarWorld createPersonalWorld(Chatter sender, String name, ServerVersion version, SteamwarWorld template) {
if (template == null || template.getVersion() != version.getVersionSuffix()) {
sender.system("BAU_WORLD_UNKNOWN");
return null;
}
if (sender.user().hasPerm(UserPerm.BUILD_UNLIMITED_WORLDS)) {
if (SteamwarWorld.getBauWorld(sender.user(), name, version.getVersionSuffix()) != null) {
sender.system("BAU_WORLD_EXISTS");
return null;
}
return SteamwarWorld.getOrCreateBauWorld(sender.user(), name, version.getVersionSuffix(), template.getStorageDirectory());
}
long limit = personalWorldLimit(sender);
long count = SteamwarWorld.countBauWorlds(sender.user());
if (count > limit) {
sender.system("BAU_WORLD_OVER_LIMIT_DELETE", limit);
return null;
}
if (count >= limit) {
sender.system("BAU_WORLD_LIMIT", limit);
return null;
}
if (SteamwarWorld.getBauWorld(sender.user(), name, version.getVersionSuffix()) != null) {
sender.system("BAU_WORLD_EXISTS");
return null;
}
return SteamwarWorld.getOrCreateBauWorld(sender.user(), name, version.getVersionSuffix(), template.getStorageDirectory());
}
private boolean hasTooManyPersonalWorlds(Chatter sender) {
if (sender.user().hasPerm(UserPerm.BUILD_UNLIMITED_WORLDS)) {
return false;
}
long limit = personalWorldLimit(sender);
if (SteamwarWorld.countBauWorlds(sender.user()) <= limit) {
return false;
}
sender.system("BAU_WORLD_OVER_LIMIT_DELETE", limit);
return true;
}
private long personalWorldLimit(Chatter sender) {
return sender.user().hasPerm(UserPerm.BUILD_EXTRA_WORLDS) ? SteamwarWorld.EXTRA_BAU_WORLD_LIMIT : SteamwarWorld.DEFAULT_BAU_WORLD_LIMIT;
function.accept(target);
}
}
@@ -26,7 +26,6 @@ import de.steamwar.linkage.Linked;
import de.steamwar.messages.Chatter;
import de.steamwar.messages.PlayerChatter;
import de.steamwar.sql.GameModeConfig;
import de.steamwar.sql.SteamwarWorld;
import de.steamwar.sql.UserPerm;
import de.steamwar.velocitycore.ArenaMode;
import de.steamwar.velocitycore.ServerStarter;
@@ -34,11 +33,11 @@ import de.steamwar.velocitycore.ServerVersion;
import de.steamwar.velocitycore.VelocityCore;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
@Linked
public class BuilderCloudCommand extends SWCommand {
@@ -48,23 +47,14 @@ public class BuilderCloudCommand extends SWCommand {
}
@Register(value = "create", description = "BUILDERCLOUD_CREATE_USAGE")
public void create(Chatter sender, @ErrorMessage("BUILDERCLOUD_VERSION") ServerVersion version, @Mapper("map") String map, @Mapper("templateWorld") SteamwarWorld template) {
if(mapExists(version, map)) {
sender.system("BUILDERCLOUD_EXISTING_MAP");
return;
}
if (template == null || template.getVersion() != version.getVersionSuffix()) {
sender.system("BAU_WORLD_UNKNOWN");
return;
}
SteamwarWorld.getOrCreateBuilderWorld(map, version.getVersionSuffix(), template.getStorageDirectory());
sender.withPlayer(p -> new ServerStarter().builder(version, map, template.getStorageDirectory()).send(p).start());
public void create(Chatter sender, @ErrorMessage("BUILDERCLOUD_VERSION") ServerVersion version, @Mapper("map") String map, @OptionalValue("") @Mapper("generator") @AllowNull File generator) {
mapFile(version, map).mkdir();
sender.withPlayer(p -> new ServerStarter().builder(version, map, generator).send(p).start());
}
@Register(description = "BUILDERCLOUD_USAGE")
public void start(PlayerChatter sender, @ErrorMessage("BUILDERCLOUD_VERSION") ServerVersion version, @Mapper("map") String map) {
if (!mapExists(version, map)) {
if (!mapFile(version, map).exists()) {
sender.system("BUILDERCLOUD_UNKNOWN_MAP");
return;
}
@@ -74,34 +64,36 @@ public class BuilderCloudCommand extends SWCommand {
@Register(value = "rename", description = "BUILDERCLOUD_RENAME_USAGE")
public void rename(Chatter sender, @ErrorMessage("BUILDERCLOUD_VERSION") ServerVersion version, @Mapper("map") String oldName, String newName) {
SteamwarWorld oldMap = builderWorld(version, oldName);
if (oldMap == null) {
File oldMap = mapFile(version, oldName);
if (!oldMap.exists()) {
sender.system("BUILDERCLOUD_UNKNOWN_MAP");
return;
}
if (mapExists(version, newName)) {
File newMap = mapFile(version, newName);
if (newMap.exists()) {
sender.system("BUILDERCLOUD_EXISTING_MAP");
return;
}
oldMap.rename(newName);
try {
Files.move(oldMap.toPath(), newMap.toPath());
} catch (IOException e) {
throw new SecurityException(e);
}
sender.system("BUILDERCLOUD_RENAMED");
}
@Register(value = "deploy", description = "BUILDERCLOUD_DEPLOY_USAGE")
public void deploy(Chatter sender, @Mapper("nonHistoricArenaMode") GameModeConfig<String, String> arenaMode, @ErrorMessage("BUILDERCLOUD_VERSION") ServerVersion version, @Mapper("map") String map) {
SteamwarWorld builderWorld = builderWorld(version, map);
if (builderWorld == null) {
if (!mapFile(version, map).exists()) {
sender.system("BUILDERCLOUD_UNKNOWN_MAP");
return;
}
VelocityCore.schedule(() -> {
String modeName = arenaMode.configFile.getName().replace(".yml", "");
SteamwarWorld arenaWorld = SteamwarWorld.getOrCreateArenaWorld(modeName, map, version.getVersionSuffix());
VelocityCore.local.execute("deployarena.py", arenaMode.configFile.getName(), Integer.toString(version.getVersionSuffix()), map, builderWorld.setupAndGetStoragePath(), arenaWorld.setupAndGetStoragePath());
VelocityCore.local.execute("deployarena.py", arenaMode.configFile.getName(), Integer.toString(version.getVersionSuffix()), map);
ArenaMode.init();
sender.system("BUILDERCLOUD_DEPLOY_FINISHED");
}).schedule();
@@ -119,78 +111,60 @@ public class BuilderCloudCommand extends SWCommand {
@Override
public Collection<String> tabCompletes(Chatter sender, PreviousArguments previousArguments, String s) {
ServerVersion version = getVersion(previousArguments, 1);
if (version == null) {
File folder = getWorldFolder(previousArguments, 1);
String[] files;
if (folder == null || (files = folder.list()) == null) {
return Collections.emptyList();
}
Set<String> maps = new LinkedHashSet<>();
SteamwarWorld.getBuilderWorlds(version.getVersionSuffix()).stream()
.map(SteamwarWorld::getName)
.forEach(maps::add);
File legacyFolder = new File(version.getWorldFolder(ServerStarter.LEGACY_BUILDER_BASE_PATH));
String[] files = legacyFolder.list();
if(files != null)
Arrays.stream(files).filter(file -> new File(legacyFolder, file).isDirectory()).forEach(maps::add);
return maps.stream()
.filter(file -> s.startsWith(".") || !file.startsWith("."))
.toList();
return Arrays.stream(files).filter(file -> new File(folder, file).isDirectory()).filter(file -> s.startsWith(".") || !file.startsWith(".")).toList();
}
};
}
@Cached(global = true)
@Mapper(value = "templateWorld", local = true)
private TypeMapper<SteamwarWorld> templateWorldMapper() {
@Mapper(value = "generator", local = true)
private TypeMapper<File> generatorTypeMapper() {
return new TypeMapper<>() {
return new TypeMapper<File>() {
@Override
public SteamwarWorld map(Chatter sender, PreviousArguments previousArguments, String s) {
ServerVersion version = previousArguments.getAll(ServerVersion.class).stream().findFirst().orElse(null);
if (version == null) return null;
return SteamwarWorld.getTemplateWorld(s, version.getVersionSuffix());
public File map(Chatter sender, PreviousArguments previousArguments, String s) {
if (s.isEmpty()) return null;
File folder = getWorldFolder(previousArguments, 2);
if (folder == null) throw new SecurityException();
File generator = new File(folder, s + ".dat");
if (!generator.exists() || !generator.isFile()) {
throw new SecurityException();
}
return generator;
}
@Override
public Collection<String> tabCompletes(Chatter sender, PreviousArguments previousArguments, String s) {
ServerVersion version = previousArguments.getAll(ServerVersion.class).stream().findFirst().orElse(null);
if (version == null) {
File folder = getWorldFolder(previousArguments, 2);
String[] files;
if (folder == null || (files = folder.list()) == null) {
return Collections.emptyList();
}
return SteamwarWorld.getTemplateWorlds(version.getVersionSuffix()).stream()
.map(SteamwarWorld::getName)
.toList();
return Arrays.stream(files).filter(file -> new File(folder, file).isFile()).filter(file -> file.endsWith(".dat")).map(file -> file.substring(0, file.length() - 4)).toList();
}
};
}
private SteamwarWorld builderWorld(ServerVersion version, String map) {
SteamwarWorld world = SteamwarWorld.getBuilderWorld(map, version.getVersionSuffix());
if(world != null)
return world;
File legacyWorld = mapFile(version, map);
if(!legacyWorld.exists())
return null;
return SteamwarWorld.getOrCreateBuilderWorld(map, version.getVersionSuffix(), legacyWorld);
}
private boolean mapExists(ServerVersion version, String map) {
return SteamwarWorld.getBuilderWorld(map, version.getVersionSuffix()) != null || mapFile(version, map).exists();
}
private File mapFile(ServerVersion version, String map) {
return new File(version.getWorldFolder(ServerStarter.LEGACY_BUILDER_BASE_PATH), map);
return new File(version.getWorldFolder(ServerStarter.BUILDER_BASE_PATH), map);
}
private ServerVersion getVersion(PreviousArguments previousArguments, int offset) {
if (previousArguments.userArgs.length < offset) {
return null;
}
return ServerVersion.get(previousArguments.userArgs[previousArguments.userArgs.length - offset]);
private File getWorldFolder(PreviousArguments previousArguments, int offset) {
ServerVersion v = ServerVersion.get(previousArguments.userArgs[previousArguments.userArgs.length - offset]);
if (v == null) return null;
return new File(v.getWorldFolder(ServerStarter.BUILDER_BASE_PATH));
}
}
@@ -24,16 +24,11 @@ import de.steamwar.linkage.Linked;
import de.steamwar.messages.Chatter;
import de.steamwar.messages.PlayerChatter;
import de.steamwar.sql.SteamwarUser;
import de.steamwar.sql.SteamwarWorld;
import de.steamwar.sql.UserPerm;
import de.steamwar.sql.internal.Statement;
import de.steamwar.velocitycore.ServerStarter;
import de.steamwar.velocitycore.ServerVersion;
import de.steamwar.velocitycore.VelocityCore;
import java.io.*;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@@ -69,21 +64,12 @@ public class GDPRQuery extends SWCommand {
copy(getClass().getClassLoader().getResourceAsStream("GDPRQueryREADME.md"), out, "README.txt");
sender.system("GDPR_STATUS_WORLD");
Set<Integer> exportedVersions = new LinkedHashSet<>();
for(ServerVersion version : ServerVersion.values()) {
int versionSuffix = version.getVersionSuffix();
if(!exportedVersions.add(versionSuffix))
continue;
SteamwarWorld world = SteamwarWorld.getBauWorld(user, versionSuffix);
if(world != null)
copyBauwelt(user, out, world.setupAndGetStoragePath(), "BuildWorld" + versionSuffix);
else
copyLegacyBauwelt(user, out, version);
}
copyBauwelt(user, out, "/home/minecraft/userworlds/" + user.getUUID().toString(), "BuildWorld12");
copyBauwelt(user, out, "/home/minecraft/userworlds15/" + user.getId(), "BuildWorld15");
sender.system("GDPR_STATUS_INVENTORIES");
copyPlayerdata(user, out, SteamwarWorld.WORLD_STORAGE, "BuildInventories");
copyPlayerdata(user, out, "/home/minecraft/userworlds", "BuildInventories12");
copyPlayerdata(user, out, "/home/minecraft/userworlds15", "BuildInventories15");
sender.system("GDPR_STATUS_DATABASE");
sqlCSV(user, out, bannedIPs, "BannedIPs.csv");
@@ -113,8 +99,8 @@ public class GDPRQuery extends SWCommand {
}
private static final Statement bannedIPs = new Statement("SELECT Timestamp, IP FROM BannedUserIPs WHERE UserID = ?");
private static final Statement bauweltMember = new Statement("SELECT WorldID AS Bauwelt, WorldEdit, World FROM BauweltMember WHERE MemberID = ?");
private static final Statement bauweltMembers = new Statement("SELECT w.Name AS World, u.UserName AS 'User', m.WorldEdit AS WorldEdit, m.World AS World FROM BauweltMember m INNER JOIN UserData u ON m.MemberID = u.id INNER JOIN world w ON m.WorldID = w.id WHERE w.Owner = ?");
private static final Statement bauweltMember = new Statement("SELECT BauweltID AS Bauwelt, WorldEdit, World FROM BauweltMember WHERE MemberID = ?");
private static final Statement bauweltMembers = new Statement("SELECT u.UserName AS 'User', m.WorldEdit AS WorldEdit, m.World AS World FROM BauweltMember m INNER JOIN UserData u ON m.MemberID = u.id WHERE m.BauweltID = ?");
private static final Statement checkedSchems = new Statement("SELECT NodeName AS Schematic, StartTime, EndTime, DeclineReason AS Result FROM CheckedSchematic WHERE NodeOwner = ? ORDER BY StartTime ASC");
private static final Statement fights = new Statement("SELECT p.Team AS Team, p.Kit AS Kit, p.Kills AS Kills, p.IsOut AS Died, f.GameMode AS GameMode, f.Server AS Server, f.StartTime AS StartTime, f.Duration AS Duration, (f.BlueLeader = p.UserID) AS IsBlueLeader, (f.RedLeader = p.UserID) AS IsRedLeader, f.Win AS Winner, f.WinCondition AS WinCondition FROM Fight f INNER JOIN FightPlayer p ON f.FightID = p.FightID WHERE p.UserID = ? ORDER BY StartTime ASC");
private static final Statement ignoredPlayers = new Statement("SELECT u.UserName AS IgnoredPlayer FROM IgnoredPlayers i INNER JOIN UserData u ON i.Ignored = u.id WHERE Ignorer = ?");
@@ -244,18 +230,6 @@ public class GDPRQuery extends SWCommand {
}
}
private void copyLegacyBauwelt(SteamwarUser user, ZipOutputStream out, ServerVersion version) throws IOException {
File legacyIdWorld = new File(version.getWorldFolder(ServerStarter.LEGACY_WORLDS_BASE_PATH), String.valueOf(user.getId()));
if(legacyIdWorld.exists()) {
copyBauwelt(user, out, legacyIdWorld.getPath(), "BuildWorld" + version.getVersionSuffix());
return;
}
File legacyUuidWorld = new File(version.getWorldFolder(ServerStarter.LEGACY_WORLDS_BASE_PATH), user.getUUID().toString());
if(legacyUuidWorld.exists())
copyBauwelt(user, out, legacyUuidWorld.getPath(), "BuildWorld" + version.getVersionSuffix());
}
private void copyPlayerdata(SteamwarUser user, ZipOutputStream out, String inDir, String outDir) throws IOException {
File worlds = new File(inDir);
String path = "playerdata/" + user.getUUID().toString() + ".dat";
@@ -42,13 +42,6 @@ public class ReplayCommand extends SWCommand {
super("replay");
}
@Register
public void genericCommand(PlayerChatter sender) {
sender.system("REPLAY_UNAVAILABLE");
return;
}
/*
@Register
public void genericCommand(PlayerChatter sender, int replayId, @StaticValue(value = {"", "-a"}, allowISE = true) @OptionalValue("") boolean isAdmin, @OptionalValue("") String optionalMap) {
Fight fight = Fight.getById(replayId);
@@ -64,6 +57,9 @@ public class ReplayCommand extends SWCommand {
@Register
public void genericCommand(PlayerChatter sender, @OptionalValue("") String optionalMap) {
if (PunishmentCommand.isPunishedWithMessage(sender, Punishment.PunishmentType.NoFightServer)) return;
if (!sender.user().hasPerm(UserPerm.TEAM)) return;
new SWStreamInv<>(sender, new Message("REPLAY_TITLE"), (click, fight) -> {
startReplay(sender, click.isShiftClick(), optionalMap, fight);
@@ -72,6 +68,7 @@ public class ReplayCommand extends SWCommand {
private void startReplay(PlayerChatter sender, boolean isAdmin, String optionalMap, Fight fight) {
if (PunishmentCommand.isPunishedWithMessage(sender, Punishment.PunishmentType.NoFightServer)) return;
if (!sender.user().hasPerm(UserPerm.TEAM)) return;
GameModeConfig<String, String> mode = ArenaMode.getBySchemType(fight.getSchemType());
ServerStarter starter = new ServerStarter().replay(fight.getFightID()).blueLeader(sender.getPlayer());
@@ -116,5 +113,5 @@ public class ReplayCommand extends SWCommand {
private Message parseLeader(SteamwarUser leader, int players, boolean winner) {
return new Message("REPLAY_" + (players > 1 ? "" : "SOLO_") + (winner ? "WINNER" : "LOSER"), leader.getUserName(), players - 1);
}
*/
}
@@ -30,7 +30,6 @@ import de.steamwar.messages.PlayerChatter;
import de.steamwar.sql.*;
import de.steamwar.velocitycore.VelocityCore;
import de.steamwar.velocitycore.discord.DiscordBot;
import de.steamwar.velocitycore.inventory.SWInventory;
import de.steamwar.velocitycore.inventory.SWItem;
import de.steamwar.velocitycore.inventory.SWListInv;
import de.steamwar.velocitycore.util.SteamwarPrefix;
@@ -438,117 +437,6 @@ public class TeamCommand extends SWCommand {
}
}
@Register("worlds")
@Register("world")
public void worlds(@Validator("isInTeam") PlayerChatter sender) {
List<SWListInv.SWListEntry<SteamwarWorld>> entries = SteamwarWorld.getTeamWorlds(Team.byId(sender.user().getTeam())).stream()
.map(world -> new SWListInv.SWListEntry<>(
new SWItem("GRASS_BLOCK", new Message("PLAIN_STRING", "§e" + world.getName()))
.addLore(new Message("PLAIN_STRING", "§7Version: §e" + world.getVersion()))
.addLore(new Message("PLAIN_STRING", "§7Left: start, right: manage")),
world
))
.toList();
SWListInv<SteamwarWorld> inv = new SWListInv<>(sender, new Message("TEAM_WORLD_GUI_TITLE"), entries, (click, world) -> {
if (click.isRightClick() && sender.user().isLeader()) {
openTeamWorldManagement(sender, world);
} else {
startTeamWorld(sender, world);
}
});
inv.open();
}
@Register("world")
public void world(@Validator("isInTeam") PlayerChatter sender, @Mapper("teamWorld") SteamwarWorld world) {
startTeamWorld(sender, world);
}
@Register(value = "world create", description = "TEAM_WORLD_CREATE_USAGE")
public void createWorld(@Validator("isLeader") Chatter sender, String name, @ErrorMessage("BAU_WORLD_VERSION") ServerVersion version, @Mapper("templateWorld") SteamwarWorld template) {
Team team = Team.byId(sender.user().getTeam());
if (template == null || template.getVersion() != version.getVersionSuffix()) {
sender.system("BAU_WORLD_UNKNOWN");
return;
}
if (SteamwarWorld.getTeamWorld(team, name, version.getVersionSuffix()) != null) {
sender.system("TEAM_WORLD_EXISTS");
return;
}
long count = SteamwarWorld.countTeamWorlds(team);
if (count > SteamwarWorld.TEAM_WORLD_LIMIT) {
sender.system("TEAM_WORLD_OVER_LIMIT_DELETE", SteamwarWorld.TEAM_WORLD_LIMIT);
return;
}
if (count >= SteamwarWorld.TEAM_WORLD_LIMIT) {
sender.system("TEAM_WORLD_LIMIT", SteamwarWorld.TEAM_WORLD_LIMIT);
return;
}
SteamwarWorld.getOrCreateTeamWorld(team, name, version.getVersionSuffix(), template.getStorageDirectory());
sender.system("TEAM_WORLD_CREATED");
}
@Register(value = "world rename", description = "TEAM_WORLD_RENAME_USAGE")
public void renameWorld(@Validator("isLeader") Chatter sender, @Mapper("teamWorld") SteamwarWorld world, String newName) {
Team team = Team.byId(sender.user().getTeam());
if (hasTooManyTeamWorlds(sender, team)) return;
if (SteamwarWorld.getTeamWorld(team, newName, world.getVersion()) != null) {
sender.system("TEAM_WORLD_EXISTS");
return;
}
world.rename(newName);
sender.system("TEAM_WORLD_RENAMED");
}
@Register(value = "world upgrade", description = "TEAM_WORLD_UPGRADE_USAGE")
public void upgradeWorld(@Validator("isLeader") Chatter sender, @Mapper("teamWorld") SteamwarWorld world, @ErrorMessage("BAU_WORLD_VERSION") ServerVersion version) {
if (hasTooManyTeamWorlds(sender, Team.byId(sender.user().getTeam()))) return;
world.changeVersion(version.getVersionSuffix());
sender.system("TEAM_WORLD_UPGRADED");
}
@Register(value = "world delete", description = "TEAM_WORLD_DELETE_USAGE")
public void deleteWorld(@Validator("isLeader") PlayerChatter sender, @Mapper("teamWorld") SteamwarWorld world) {
SWInventory inventory = new SWInventory(sender, 9, new Message("BAU_DELETE_GUI_NAME"));
inventory.addItem(0, new SWItem("LIME_DYE", new Message("BAU_DELETE_GUI_DELETE")), click -> {
VelocityCore.schedule(() -> {
Bauserver subserver = Bauserver.getByWorld(world.getUuid());
if (subserver != null) {
subserver.stop();
}
world.markDeleted();
SubserverSystem.deleteFolder(VelocityCore.local, world.getStorageDirectory().getPath());
sender.system("TEAM_WORLD_DELETED");
}).schedule();
inventory.close();
});
inventory.addItem(8, new SWItem("RED_DYE", new Message("BAU_DELETE_GUI_CANCEL")), click -> inventory.close());
inventory.open();
}
private void startTeamWorld(PlayerChatter sender, SteamwarWorld world) {
new ServerStarter().build(ServerVersion.get(world.getVersion()), world).send(sender.getPlayer()).start();
}
private void openTeamWorldManagement(PlayerChatter sender, SteamwarWorld world) {
SWInventory inventory = new SWInventory(sender, 9, new Message("PLAIN_STRING", "§e" + world.getName()));
inventory.addItem(0, new SWItem("ENDER_PEARL", new Message("PLAIN_STRING", "§aStart")), click -> {
inventory.close();
startTeamWorld(sender, world);
});
inventory.addItem(3, new SWItem("EXPERIENCE_BOTTLE", new Message("PLAIN_STRING", "§eUpgrade to latest")), click -> {
if (hasTooManyTeamWorlds(sender, Team.byId(sender.user().getTeam()))) {
inventory.close();
return;
}
world.changeVersion(ServerVersion.PAPER_21.getVersionSuffix());
sender.system("TEAM_WORLD_UPGRADED");
inventory.close();
});
inventory.addItem(8, new SWItem("BARRIER", new Message("PLAIN_STRING", "§cDelete")), click -> deleteWorld(sender, world));
inventory.open();
}
@Register("event")
public void event(@Validator("isLeader") Chatter sender, Event event) {
Team team = Team.byId(sender.user().getTeam());
@@ -697,56 +585,6 @@ public class TeamCommand extends SWCommand {
};
}
@Mapper(value = "teamWorld", local = true)
public TypeMapper<SteamwarWorld> teamWorldMapper() {
return new TypeMapper<>() {
@Override
public SteamwarWorld map(Chatter sender, PreviousArguments previousArguments, String s) {
Team team = Team.byId(sender.user().getTeam());
return SteamwarWorld.getTeamWorlds(team).stream()
.filter(world -> world.getName().equalsIgnoreCase(s) || world.getUuid().toString().equalsIgnoreCase(s))
.findFirst()
.orElse(null);
}
@Override
public Collection<String> tabCompletes(Chatter sender, PreviousArguments previousArguments, String s) {
if (sender.user().getTeam() == 0) return List.of();
return SteamwarWorld.getTeamWorlds(Team.byId(sender.user().getTeam())).stream()
.map(SteamwarWorld::getName)
.toList();
}
};
}
@Mapper(value = "templateWorld", local = true)
public TypeMapper<SteamwarWorld> templateWorldMapper() {
return new TypeMapper<>() {
@Override
public SteamwarWorld map(Chatter sender, PreviousArguments previousArguments, String s) {
ServerVersion version = previousArguments.getAll(ServerVersion.class).stream().findFirst().orElse(null);
if (version == null) return null;
return SteamwarWorld.getTemplateWorld(s, version.getVersionSuffix());
}
@Override
public Collection<String> tabCompletes(Chatter sender, PreviousArguments previousArguments, String s) {
ServerVersion version = previousArguments.getAll(ServerVersion.class).stream().findFirst().orElse(null);
if (version == null) return List.of();
return SteamwarWorld.getTemplateWorlds(version.getVersionSuffix()).stream().map(SteamwarWorld::getName).toList();
}
};
}
private boolean hasTooManyTeamWorlds(Chatter sender, Team team) {
if (SteamwarWorld.countTeamWorlds(team) <= SteamwarWorld.TEAM_WORLD_LIMIT) {
return false;
}
sender.system("TEAM_WORLD_OVER_LIMIT_DELETE", SteamwarWorld.TEAM_WORLD_LIMIT);
return true;
}
private boolean checkTeamName(Chatter sender, Team team, String arg) {
Team t = Team.get(arg);
if (t != null && t.getTeamId() != team.getTeamId()) {
@@ -120,26 +120,17 @@ public class TpCommand extends SWCommand {
}
} else if (Subserver.isBuild(subserver)) {
Bauserver bauserver = (Bauserver) subserver;
SteamwarWorld world = SteamwarWorld.getWorld(bauserver.getWorld());
if (world != null && world.getType() == WorldType.TEAM) {
if (sender.user().getTeam() != world.getTeam().getValue()) {
sender.system("JOIN_PLAYER_BLOCK");
return;
}
SubserverSystem.sendPlayer(subserver, sender.getPlayer());
return;
}
Player checker = VelocityCore.getProxy().getPlayer(bauserver.getOwner()).orElse(null);
if (checker != null && CheckCommand.isChecking(checker)) {
if (!sender.user().hasPerm(UserPerm.CHECK) && CheckCommand.getCheckingSchem(checker).getOwner() != sender.user().getId()) {
sender.system("JOIN_PLAYER_BLOCK");
return;
}
} else if (world != null && BauLock.isLocked(world, sender.user())) {
} else if (BauLock.isLocked(SteamwarUser.get(bauserver.getOwner()), sender.user())) {
sender.system("BAU_LOCKED_NOALLOWED");
Chatter.of(bauserver.getOwner()).system("BAU_LOCK_BLOCKED", sender);
return;
} else if (!bauserver.getOwner().equals(sender.user().getUUID()) && BauweltMember.getBauMember(bauserver.getWorld(), sender.user().getUUID()) == null) {
} else if (!bauserver.getOwner().equals(sender.user().getUUID()) && BauweltMember.getBauMember(bauserver.getOwner(), sender.user().getUUID()) == null) {
SubserverSystem.sendDeniedMessage(sender, bauserver.getOwner());
sender.system("JOIN_PLAYER_BLOCK");
return;
@@ -25,39 +25,37 @@ import de.steamwar.network.packets.server.BaulockUpdatePacket;
import de.steamwar.persistent.Bauserver;
import de.steamwar.sql.BauweltMember;
import de.steamwar.sql.SteamwarUser;
import de.steamwar.sql.SteamwarWorld;
import de.steamwar.sql.UserConfig;
import de.steamwar.sql.UserPerm;
import de.steamwar.sql.WorldType;
import de.steamwar.velocitycore.network.NetworkSender;
import lombok.experimental.UtilityClass;
@UtilityClass
public class BauLock {
public static void setLocked(Chatter owner, SteamwarWorld world, BauLockState state) {
world.changeLockState(state == BauLockState.OPEN ? null : state.name());
private static final String BAU_LOCK_CONFIG_NAME = "baulockstate";
public static void setLocked(Chatter owner, BauLockState state) {
UserConfig.updatePlayerConfig(owner.user().getId(), BAU_LOCK_CONFIG_NAME, state == BauLockState.OPEN ? null : state.name());
owner.system("BAU_LOCKED_" + state.name());
Bauserver bauserver = Bauserver.getByWorld(world.getUuid());
Bauserver bauserver = Bauserver.get(owner.user().getUUID());
if (bauserver != null) {
bauserver.getRegisteredServer().getPlayersConnected().stream().findAny().ifPresent(player -> NetworkSender.send(player, new BaulockUpdatePacket()));
}
}
public static boolean isLocked(SteamwarWorld world, SteamwarUser target) {
if (world.getType() != WorldType.BAU || world.getOwner() == null) return false;
SteamwarUser owner = SteamwarUser.byId(world.getOwner().getValue());
if (owner == null || owner.getId() == target.getId()) return false;
public static boolean isLocked(SteamwarUser owner, SteamwarUser target) {
if (owner.getId() == target.getId()) return false;
boolean locked;
String state = world.getLockState();
String state = UserConfig.getConfig(owner.getId(), BAU_LOCK_CONFIG_NAME);
switch (state == null ? BauLockState.OPEN : BauLockState.valueOf(state)) {
case NOBODY:
locked = true;
break;
case SUPERVISOR:
BauweltMember member = BauweltMember.getBauMember(world.getUuid(), target.getId());
BauweltMember member = BauweltMember.getBauMember(owner.getId(), target.getId());
locked = member == null || !member.isSupervisor();
break;
case SERVERTEAM:
+23 -4
View File
@@ -28,7 +28,7 @@ class DevServer extends DefaultTask {
@Input
@Optional
String worldName = null
boolean debug = false
@Input
String template = null
@@ -84,10 +84,13 @@ class DevServer extends DefaultTask {
}
}
if (worldName == null) worldName = properties.get("worldName")
worldName = properties.get("worldName")
host = properties.get("host")
debugPort = new Random().nextInt(5001, 10000)
if (worldName == null) {
throw new GradleException("Please supply the 'worldName' in a 'steamwar.properties' files either in this project dir or any parent project!")
}
if (host == null) {
throw new GradleException("Please supply the 'host' in a 'steamwar.properties' files either in this project dir or any parent project!")
}
@@ -95,7 +98,7 @@ class DevServer extends DefaultTask {
doLast {
setupTemplate(template)
uploadDependencies()
startDebugPort()
if (debug) startDebugPort()
startDevServer()
}
finalizedBy(new Finalizer())
@@ -113,6 +116,9 @@ class DevServer extends DefaultTask {
@Internal
Boolean running = true
@Internal
String worldName = null
class Finalizer extends DefaultTask {
Finalizer() {
@@ -241,7 +247,7 @@ class DevServer extends DefaultTask {
devPy.append(" -D${dParam.key}=${dParam.value}")
}
devPy.append(" $template")
devPy.append(" -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:$debugPort")
if (debug) devPy.append(" -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:$debugPort")
if (jvmArgs != null) devPy.append(" $jvmArgs")
println("Starting $template with command ${devPy.toString()}")
@@ -278,6 +284,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