Compare commits

..

1 Commits

Author SHA1 Message Date
ec41111054 Start Logging
Some checks failed
SteamWarCI Build failed
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-01-28 20:44:05 +01:00
118 changed files with 735 additions and 6193 deletions

View File

@@ -39,7 +39,6 @@ import com.sk89q.worldedit.math.transform.AffineTransform;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.selector.CuboidRegionSelector;
import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.util.SideEffectSet;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockTypes;
@@ -115,9 +114,6 @@ public class FlatteningWrapper15 implements FlatteningWrapper {
@Override
public Clipboard loadSchematic(File file) {
if (file == null) {
return null;
}
Clipboard clipboard;
try (ClipboardReader reader = Objects.requireNonNull(ClipboardFormats.findByFile(file)).getReader(new FileInputStream(file))) {
clipboard = reader.read();
@@ -171,18 +167,13 @@ public class FlatteningWrapper15 implements FlatteningWrapper {
ClipboardHolder ch = new ClipboardHolder(clipboard);
BlockVector3 dimensions = clipboard.getDimensions();
BlockVector3 v;
BlockVector3 v = BlockVector3.at(pasteBuilder.getPastPoint().getX(), pasteBuilder.getPastPoint().getY(), pasteBuilder.getPastPoint().getZ());
BlockVector3 offset = clipboard.getRegion().getMinimumPoint().subtract(clipboard.getOrigin());
if (pasteBuilder.getPastPoint() != null) {
v = pasteBuilder.getPastPoint().toBlockVector3();
if (pasteBuilder.isRotate()) {
ch.setTransform(new AffineTransform().rotateY(180));
v = v.add(dimensions.getX() / 2, 0, dimensions.getZ() / 2).subtract(offset.multiply(-1, 1, -1)).subtract(0, 0, 1);
} else {
v = v.subtract(dimensions.getX() / 2, 0, dimensions.getZ() / 2).subtract(offset);
}
if (pasteBuilder.isRotate()) {
ch.setTransform(new AffineTransform().rotateY(180));
v = v.add(dimensions.getX() / 2, 0, dimensions.getZ() / 2).subtract(offset.multiply(-1, 1, -1)).subtract(0, 0, 1);
} else {
v = pasteBuilder.getMinPoint().toBlockVector3().subtract(offset);
v = v.subtract(dimensions.getX() / 2, 0, dimensions.getZ() / 2).subtract(offset);
}
pastePoint.set(v);
@@ -192,7 +183,6 @@ public class FlatteningWrapper15 implements FlatteningWrapper {
e.setBlocks(new CuboidRegion(pasteBuilder.getMinPoint().toBlockVector3(), pasteBuilder.getMaxPoint().toBlockVector3().withY(pasteBuilder.getWaterLevel())), Objects.requireNonNull(BlockTypes.WATER).getDefaultState().toBaseBlock());
}
}
e.setSideEffectApplier(SideEffectSet.none());
Operations.completeBlindly(ch.createPaste(e).to(v).ignoreAirBlocks(pasteBuilder.isIgnoreAir()).build());
return e;
} catch (WorldEditException e) {

View File

@@ -39,6 +39,7 @@ import de.steamwar.bausystem.utils.TickManager;
import de.steamwar.bausystem.worlddata.WorldData;
import de.steamwar.command.AbstractValidator;
import de.steamwar.command.SWCommandUtils;
import de.steamwar.core.CRIUSleepEvent;
import de.steamwar.core.Core;
import de.steamwar.core.WorldEditRendererCUIEditor;
import de.steamwar.core.WorldIdentifier;
@@ -51,6 +52,8 @@ import org.bukkit.Bukkit;
import org.bukkit.GameRule;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitRunnable;
@@ -63,7 +66,7 @@ import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.logging.Level;
public class BauSystem extends JavaPlugin {
public class BauSystem extends JavaPlugin implements Listener {
// This should be treated as final!
public static Message MESSAGE;
@@ -137,10 +140,16 @@ public class BauSystem extends JavaPlugin {
WorldIdentifier.set("bau/" + Core.getVersion() + "/" + identifier);
}
@EventHandler
public void onCRIUSleep(CRIUSleepEvent event) {
RegionSystem.INSTANCE.save();
}
@Override
public void onDisable() {
linker.unlink();
WorldData.write();
RegionSystem.INSTANCE.save();
Config.getInstance().saveAll();
}

View File

@@ -56,7 +56,6 @@ public class BauInfoBauGuiItem extends BauGuiItem {
Region region = Region.getRegion(player.getLocation());
List<String> stringList = new ArrayList<>();
for (Flag flag : Flag.getFlags()) {
if (flag == Flag.CHANGED) continue;
if (!region.getRegionData().has(flag).isApplicable()) continue;
FlagOptional<?> value = region.getRegionData().get(flag);
if (value.isPresent()) {

View File

@@ -31,7 +31,10 @@ import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.entity.Player;
import java.util.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class DesignEndStone {
@@ -53,10 +56,11 @@ public class DesignEndStone {
this.maxY = region.getBuildArea().getMaxPoint(false).getY();
this.maxZ = region.getBuildArea().getMaxPoint(false).getZ();
limited = Arrays.stream(Material.values())
.filter(Material::isBlock)
.filter(material -> !material.isLegacy())
.filter(material -> material.getBlastResistance() > region.getGameModeConfig().Schematic.MaxDesignBlastResistance)
limited = region.getGameModeConfig().Schematic.Limited
.entrySet()
.stream()
.filter(entry -> entry.getValue() == 0)
.flatMap(entry -> entry.getKey().stream())
.collect(Collectors.toSet());
calculateFromBottom = region.getGameModeConfig().Arena.NoFloor;

View File

@@ -63,11 +63,11 @@ public class ColorCommand extends SWCommand {
}
region.getRegionData().set(Flag.COLOR, color);
try {
PasteBuilder pasteBuilder = new PasteBuilder(PasteBuilder.ClipboardProvider.EMPTY)
PasteBuilder pasteBuilder = new PasteBuilder(new PasteBuilder.FileProvider(region.getArea().getResetFile()))
.ignoreAir(true)
.onlyColors(true)
.color(color);
region.getArea().reset(p.getLocation(), pasteBuilder, false);
region.getArea().reset(pasteBuilder, false);
RegionUtils.message(region, "REGION_REGION_COLORED");
RegionUtils.message(region, "REGION_REGION_COLORED_FAILED");
} catch (SecurityException e) {

View File

@@ -56,7 +56,6 @@ public class FireListener implements Listener, ScoreboardElement {
@Override
public String get(Region region, Player p) {
if (region.getRegionData().has(Flag.FIRE).notVisibleInScoreboard()) return null;
if (region.getRegionData().get(Flag.FIRE).isWithDefault(FireMode.DENY)) return null;
return "§e" + BauSystem.MESSAGE.parse(Flag.FIRE.getChatValue(), p) + "§8: " + BauSystem.MESSAGE.parse(region.getRegionData().get(Flag.FIRE).getWithDefault().getChatValue(), p);
}

View File

@@ -235,7 +235,6 @@ public class FreezeListener implements Listener, ScoreboardElement {
@Override
public String get(Region region, Player p) {
if (region.getRegionData().has(Flag.FREEZE).notVisibleInScoreboard()) return null;
if (region.getRegionData().get(Flag.FREEZE).isWithDefault(FreezeMode.INACTIVE)) return null;
return "§e" + BauSystem.MESSAGE.parse(Flag.FREEZE.getChatValue(), p) + "§8: " + BauSystem.MESSAGE.parse(region.getRegionData().get(Flag.FREEZE).getWithDefault().getChatValue(), p);
}

View File

@@ -54,7 +54,6 @@ public class ItemsListener implements Listener, ScoreboardElement {
@Override
public String get(Region region, Player p) {
if (region.getRegionData().has(Flag.ITEMS).notVisibleInScoreboard()) return null;
if (region.getRegionData().get(Flag.ITEMS).isWithDefault(ItemMode.INACTIVE)) return null;
return "§e" + BauSystem.MESSAGE.parse(Flag.ITEMS.getChatValue(), p) + "§8: " + BauSystem.MESSAGE.parse(region.getRegionData().get(Flag.ITEMS).getWithDefault().getChatValue(), p);
}

View File

@@ -54,7 +54,6 @@ public class NoGravityListener implements Listener, ScoreboardElement {
@Override
public String get(Region region, Player p) {
if (region.getRegionData().has(Flag.NO_GRAVITY).notVisibleInScoreboard()) return null;
if (region.getRegionData().get(Flag.NO_GRAVITY).isWithDefault(NoGravityMode.INACTIVE)) return null;
return "§e" + BauSystem.MESSAGE.parse(Flag.NO_GRAVITY.getChatValue(), p) + "§8: " + BauSystem.MESSAGE.parse(region.getRegionData().get(Flag.NO_GRAVITY).getWithDefault().getChatValue(), p);
}

View File

@@ -70,7 +70,6 @@ public class ProtectListener implements Listener, ScoreboardElement {
@Override
public String get(Region region, Player p) {
if (region.getRegionData().has(Flag.PROTECT).notVisibleInScoreboard()) return null;
if (region.getRegionData().get(Flag.PROTECT).isWithDefault(ProtectMode.INACTIVE)) return null;
return "§e" + BauSystem.MESSAGE.parse(Flag.PROTECT.getChatValue(), p) + "§8: " + BauSystem.MESSAGE.parse(region.getRegionData().get(Flag.PROTECT).getWithDefault().getChatValue(), p);
}

View File

@@ -30,7 +30,6 @@ import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.features.util.SelectCommand;
import de.steamwar.bausystem.region.Point;
import de.steamwar.bausystem.region.Region;
import de.steamwar.bausystem.region.RegionHistory;
import de.steamwar.bausystem.region.RegionUtils;
import de.steamwar.bausystem.region.flags.Flag;
import de.steamwar.bausystem.shared.Pair;
@@ -72,10 +71,7 @@ public class RegionCommand extends SWCommand {
@Register(value = "undo", description = "REGION_REGION_HELP_UNDO")
public void undoCommand(@Validator Player p) {
Region region = Region.getRegion(p.getLocation());
if (region.getHistory() == RegionHistory.EMPTY) {
BauSystem.MESSAGE.send("REGION_REGION_NO_REGION", p);
return;
}
if (checkGlobalRegion(region, p)) return;
if (region.getHistory().undo()) {
RegionUtils.message(region, "REGION_REGION_UNDID");
@@ -87,8 +83,7 @@ public class RegionCommand extends SWCommand {
@Register(value = "redo", description = "REGION_REGION_HELP_REDO")
public void redoCommand(@Validator Player p) {
Region region = Region.getRegion(p.getLocation());
if (region.getHistory() == RegionHistory.EMPTY) {
BauSystem.MESSAGE.send("REGION_REGION_NO_REGION", p);
if (checkGlobalRegion(region, p)) {
return;
}
@@ -105,10 +100,32 @@ public class RegionCommand extends SWCommand {
if(checkGlobalRegion(region, p)) return;
try {
PasteBuilder pasteBuilder = new PasteBuilder(PasteBuilder.ClipboardProvider.EMPTY)
PasteBuilder pasteBuilder = new PasteBuilder(new PasteBuilder.FileProvider(region.getArea().getResetFile()))
.ignoreAir(true)
.color(region.getRegionData().get(Flag.COLOR).getWithDefault());
region.getArea().reset(p.getLocation(), pasteBuilder, false);
region.getArea().reset(pasteBuilder, false);
RegionUtils.message(region, "REGION_REGION_RESTORED");
} catch (SecurityException e) {
BauSystem.MESSAGE.send("REGION_REGION_FAILED_RESTORE", p);
Bukkit.getLogger().log(Level.WARNING, "Failed restore", e);
}
}
@Register(value = "restore", description = "REGION_REGION_HELP_RESTORE_SCHEMATIC")
public void schematicRestoreCommand(@Validator Player p, SchematicNode node) {
Region region = Region.getRegion(p.getLocation());
if (checkGlobalRegion(region, p)) return;
if(node.isDir()) {
BauSystem.MESSAGE.send("ONLY_SCHEMS", p);
return;
}
try {
PasteBuilder pasteBuilder = new PasteBuilder(new PasteBuilder.SchematicProvider(node))
.ignoreAir(true)
.color(region.getRegionData().get(Flag.COLOR).getWithDefault());
region.getArea().reset(pasteBuilder, false);
RegionUtils.message(region, "REGION_REGION_RESTORED");
} catch (SecurityException e) {
BauSystem.MESSAGE.send("REGION_REGION_FAILED_RESTORE", p);

View File

@@ -29,6 +29,9 @@ import de.steamwar.bausystem.utils.PasteBuilder;
import de.steamwar.command.SWCommand;
import de.steamwar.linkage.Linked;
import de.steamwar.linkage.LinkedInstance;
import de.steamwar.sql.Punishment;
import de.steamwar.sql.SchematicNode;
import de.steamwar.sql.SteamwarUser;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
@@ -49,9 +52,9 @@ public class ResetCommand extends SWCommand {
Region region = regionCheck(p);
if (region == null) return;
try {
PasteBuilder pasteBuilder = new PasteBuilder(PasteBuilder.ClipboardProvider.EMPTY)
PasteBuilder pasteBuilder = new PasteBuilder(new PasteBuilder.FileProvider(region.getArea().getResetFile()))
.color(region.getRegionData().get(Flag.COLOR).getWithDefault());
region.getArea().reset(p.getLocation(), pasteBuilder, false);
region.getArea().reset(pasteBuilder, false);
region.getRegionData().clear();
RegionUtils.message(region, "REGION_RESET_RESETED");
} catch (SecurityException e) {
@@ -60,6 +63,35 @@ public class ResetCommand extends SWCommand {
}
}
@Register(description = "REGION_RESET_HELP_SCHEMATIC")
public void schematicResetCommand(@Validator Player p, SchematicNode node) {
Region region = regionCheck(p);
if (region == null) return;
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;
}
if (Punishment.isPunished(SteamwarUser.get(p.getUniqueId()), Punishment.PunishmentType.NoSchemSharing, punishment -> BauSystem.MESSAGE.send("REGION_TB_NO_SCHEMSHARING", p, punishment.getEndTime()))) {
return;
}
}
if (node.isDir()) {
BauSystem.MESSAGE.send("ONLY_SCHEMS", p);
return;
}
try {
PasteBuilder pasteBuilder = new PasteBuilder(new PasteBuilder.SchematicProvider(node))
.color(region.getRegionData().get(Flag.COLOR).getWithDefault());
region.getArea().reset(pasteBuilder, true);
RegionUtils.message(region, "REGION_RESET_RESETED");
} catch (SecurityException e) {
BauSystem.MESSAGE.send("REGION_RESET_ERROR", p);
Bukkit.getLogger().log(Level.WARNING, "Failed reset", e);
}
}
private Region regionCheck(Player player) {
Region region = Region.getRegion(player.getLocation());
if (region == RegionSystem.INSTANCE.getGlobalRegion()) {

View File

@@ -21,16 +21,13 @@ package de.steamwar.bausystem.features.region;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.region.Region;
import de.steamwar.bausystem.region.RegionSystem;
import de.steamwar.bausystem.region.RegionUtils;
import de.steamwar.bausystem.region.flags.Flag;
import de.steamwar.bausystem.region.flags.TNTMode;
import de.steamwar.bausystem.utils.ScoreboardElement;
import de.steamwar.linkage.Linked;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.entity.TNTPrimed;
import org.bukkit.event.EventHandler;
@@ -38,26 +35,12 @@ import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockExplodeEvent;
import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.event.entity.EntitySpawnEvent;
import org.bukkit.persistence.PersistentDataType;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
@Linked
public class TNTListener implements Listener, ScoreboardElement {
private static final NamespacedKey SPAWN_REGION_ID = Objects.requireNonNull(NamespacedKey.fromString("spawn_region_id", BauSystem.getInstance()));
@EventHandler
public void onEntitySpawn(EntitySpawnEvent event) {
Entity entity = event.getEntity();
if (!(entity instanceof TNTPrimed)) return;
Region region = Region.getRegion(entity.getLocation());
entity.getPersistentDataContainer().set(SPAWN_REGION_ID, PersistentDataType.STRING, region.getID().toString());
}
private void explode(List<Block> blockList, boolean destroy) {
blockList.removeIf(block -> {
Region region = Region.getRegion(block.getLocation());
@@ -88,25 +71,6 @@ public class TNTListener implements Listener, ScoreboardElement {
event.blockList().clear();
return;
}
Entity entity = event.getEntity();
Region currentRegion = Region.getRegion(entity.getLocation());
String spawningRegionIdString = entity.getPersistentDataContainer().get(SPAWN_REGION_ID, PersistentDataType.STRING);
UUID spawningRegionId = spawningRegionIdString == null ? null : UUID.fromString(spawningRegionIdString);
// TNT Only created block damage in the Region it spawned in or in a connected special region!
if (!currentRegion.getID().equals(spawningRegionId)) {
if (!currentRegion.getType().isSpecial()) {
event.blockList().clear();
return;
}
if (RegionSystem.INSTANCE.getConnectedRegions(currentRegion)
.noneMatch(region -> region.getID().equals(spawningRegionId))) {
event.blockList().clear();
return;
}
}
explode(event.blockList(), true);
}
@@ -122,7 +86,6 @@ public class TNTListener implements Listener, ScoreboardElement {
@Override
public String get(Region region, Player p) {
if (region.getRegionData().has(Flag.TNT).notVisibleInScoreboard()) return null;
if (region.getRegionData().get(Flag.TNT).isWithDefault(TNTMode.ALLOW)) return null;
return "§e" + BauSystem.MESSAGE.parse(Flag.TNT.getChatValue(), p) + "§8: " + BauSystem.MESSAGE.parse(region.getRegionData().get(Flag.TNT).getWithDefault().getChatValue(), p);
}

View File

@@ -21,11 +21,9 @@ package de.steamwar.bausystem.features.region;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.config.BauServer;
import de.steamwar.bausystem.region.Point;
import de.steamwar.bausystem.region.Region;
import de.steamwar.bausystem.region.RegionUtils;
import de.steamwar.bausystem.region.flags.Flag;
import de.steamwar.bausystem.region.flags.TestblockMode;
import de.steamwar.bausystem.region.utils.RegionExtensionType;
import de.steamwar.bausystem.utils.PasteBuilder;
import de.steamwar.command.PreviousArguments;
@@ -108,7 +106,13 @@ public class TestblockCommand extends SWCommand {
region.getRegionData().setTestblockSchematic(node);
}
PasteBuilder.ClipboardProvider clipboardProvider = node == null ? PasteBuilder.ClipboardProvider.EMPTY : PasteBuilder.ClipboardProvider.schematic(node);
PasteBuilder.ClipboardProvider clipboardProvider;
if (node == null) {
clipboardProvider = new PasteBuilder.FileProvider(region.getTestblockArea().getResetFile());
} else {
clipboardProvider = new PasteBuilder.SchematicProvider(node);
}
try {
PasteBuilder pasteBuilder = new PasteBuilder(clipboardProvider)
.ignoreAir(ignoreAir)
@@ -116,7 +120,7 @@ public class TestblockCommand extends SWCommand {
.removeTNT(removeTNT)
.removeWater(removeWater)
.color(region.getRegionData().get(Flag.COLOR).getWithDefault());
region.getTestblockArea().reset(p.getLocation(), pasteBuilder, regionExtensionType == RegionExtensionType.EXTENSION);
region.getTestblockArea().reset(pasteBuilder, regionExtensionType == RegionExtensionType.EXTENSION);
RegionUtils.message(region, "REGION_TB_DONE");
} catch (SecurityException e) {
BauSystem.MESSAGE.send("REGION_TB_ERROR", p);
@@ -169,17 +173,6 @@ public class TestblockCommand extends SWCommand {
private Region regionCheck(Player player) {
Region region = Region.getRegion(player.getLocation());
if (region.getRegionData().has(Flag.TESTBLOCK).isWritable() && region.getRegionData().get(Flag.TESTBLOCK).isWithDefault(TestblockMode.NO_VALUE)) {
Point minPoint = region.getArea().getMinPoint(false);
Point maxPoint = region.getArea().getMaxPoint(false);
// TODO: Check if empty!
int half = minPoint.getZ() + (maxPoint.getZ() - minPoint.getZ()) / 2;
if (player.getLocation().getBlockZ() <= half) {
region.getRegionData().set(Flag.TESTBLOCK, TestblockMode.SOUTH);
} else {
region.getRegionData().set(Flag.TESTBLOCK, TestblockMode.NORTH);
}
}
if (region.getTestblockArea().isEmpty()) {
BauSystem.MESSAGE.send("REGION_TB_NO_REGION", player);
return null;

View File

@@ -1,49 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.bausystem.features.region;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.region.Region;
import de.steamwar.bausystem.region.flags.Flag;
import de.steamwar.bausystem.region.flags.TestblockMode;
import de.steamwar.bausystem.utils.ScoreboardElement;
import de.steamwar.linkage.Linked;
import org.bukkit.entity.Player;
@Linked
public class TestblockScoreboardElement implements ScoreboardElement {
@Override
public ScoreboardGroup getGroup() {
return ScoreboardGroup.REGION;
}
@Override
public int order() {
return 6;
}
@Override
public String get(Region region, Player p) {
if (region.getRegionData().has(Flag.TESTBLOCK).notVisibleInScoreboard()) return null;
if (region.getRegionData().get(Flag.TESTBLOCK).is(TestblockMode.NO_VALUE)) return null;
return "§e" + BauSystem.MESSAGE.parse(Flag.TESTBLOCK.getChatValue(), p) + "§8: " + BauSystem.MESSAGE.parse(region.getRegionData().get(Flag.TESTBLOCK).getWithDefault().getChatValue(), p);
}
}

View File

@@ -51,7 +51,6 @@ public class WaterDestroyListener implements Listener, ScoreboardElement {
@Override
public String get(Region region, Player p) {
if (region.getRegionData().has(Flag.WATER_DESTROY).notVisibleInScoreboard()) return null;
if (region.getRegionData().get(Flag.WATER_DESTROY).isWithDefault(WaterDestroyMode.ALLOW)) return null;
return "§e" + BauSystem.MESSAGE.parse(Flag.WATER_DESTROY.getChatValue(), p) + "§8: " + BauSystem.MESSAGE.parse(region.getRegionData().get(Flag.WATER_DESTROY).getWithDefault().getChatValue(), p);
}

View File

@@ -50,8 +50,16 @@ public class ResetBauGuiItem extends BauGuiItem {
@Override
public boolean click(ClickType click, Player p) {
p.closeInventory();
resetCommand.genericResetCommand(p);
if (click == ClickType.LEFT) {
p.closeInventory();
resetCommand.genericResetCommand(p);
} else {
SchematicSelector selector = new SchematicSelector(p, SchematicSelector.selectSchematic(), node -> {
p.closeInventory();
resetCommand.schematicResetCommand(p, node);
});
selector.open();
}
return false;
}

View File

@@ -33,12 +33,12 @@ public class StabFinalizer extends StabStep {
@Override
protected void start() {
try {
PasteBuilder.ClipboardProvider clipboardProvider = PasteBuilder.ClipboardProvider.clipboard(data.clipboard);
PasteBuilder.ClipboardProvider clipboardProvider = new PasteBuilder.ClipboardProviderImpl(data.clipboard);
PasteBuilder pasteBuilder = new PasteBuilder(clipboardProvider);
if (data.region.getRegionData().has(Flag.COLOR).isReadable()) {
pasteBuilder.color(data.region.getRegionData().get(Flag.COLOR).getWithDefault());
}
data.region.getTestblockArea().reset(null, pasteBuilder, true);
data.region.getTestblockArea().reset(pasteBuilder, true);
} catch (SecurityException e) {
stop();
throw e;

View File

@@ -71,12 +71,12 @@ public class StabGenerator extends StabStep implements Listener {
@Override
protected void start() {
try {
PasteBuilder.ClipboardProvider clipboardProvider = PasteBuilder.ClipboardProvider.clipboard(data.clipboard);
PasteBuilder.ClipboardProvider clipboardProvider = new PasteBuilder.ClipboardProviderImpl(data.clipboard);
PasteBuilder pasteBuilder = new PasteBuilder(clipboardProvider);
if (data.region.getRegionData().has(Flag.COLOR).isReadable()) {
pasteBuilder.color(data.region.getRegionData().get(Flag.COLOR).getWithDefault());
}
data.region.getTestblockArea().reset(null, pasteBuilder, true);
data.region.getTestblockArea().reset(pasteBuilder, true);
} catch (SecurityException e) {
stop();
throw e;

View File

@@ -22,7 +22,6 @@ package de.steamwar.bausystem.features.warp;
import de.steamwar.bausystem.region.RegionSystem;
import de.steamwar.bausystem.worlddata.WorldData;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.*;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerTeleportEvent;
@@ -36,20 +35,14 @@ public class Warp {
private static Map<String, Warp> warpMap = new HashMap<>();
public static void enable() {
Warp worldSpawn = new Warp("WorldSpawn") {
@Override
public Location getLocation() {
return RegionSystem.INSTANCE.getWorldSpawn();
}
};
Warp worldSpawn = new Warp("WorldSpawn");
worldSpawn.setLocation(RegionSystem.INSTANCE.getWorldSpawn());
worldSpawn.setMat(Material.NETHER_STAR);
warpMap.put("WorldSpawn", worldSpawn);
}
private String name;
@Setter
private Location location;
@Setter
private Material mat;
private Warp(String name) {
@@ -98,13 +91,21 @@ public class Warp {
return warpMap.get(name);
}
public void setMat(Material mat) {
this.mat = mat;
}
public void setLocation(Location location) {
this.location = location;
}
public void delete() {
warpMap.remove(name);
WorldData.getWarpData().remove(name);
}
public void teleport(Player player) {
player.teleport(getLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN);
player.playSound(getLocation(), Sound.ENTITY_ENDERMAN_TELEPORT, SoundCategory.PLAYERS, 1, 1);
player.teleport(location, PlayerTeleportEvent.TeleportCause.PLUGIN);
player.playSound(location, Sound.ENTITY_ENDERMAN_TELEPORT, SoundCategory.PLAYERS, 1, 1);
}
}

View File

@@ -119,8 +119,11 @@ public class BauScoreboard implements Listener {
@Override
public String getTitle() {
Region region = Region.getRegion(player.getLocation());
if (region.getRegionData().has(Flag.COLOR).notVisibleInScoreboard()) return "§eSteam§8War";
String colorCode = "§" + region.getRegionData().get(Flag.COLOR).getWithDefault().getColorCode();
if (region.getType().isGlobal()) return "§eSteam§8War";
String colorCode = "§e";
if (region.getRegionData().has(Flag.COLOR).isReadable()) {
colorCode = "§" + region.getRegionData().get(Flag.COLOR).orElse(ColorMode.PINK).getColorCode();
}
return colorCode + "■ §eSteam§8War " + colorCode + ""; // ■
}
});

View File

@@ -28,7 +28,6 @@ import org.bukkit.scheduler.BukkitRunnable;
import java.util.Iterator;
import java.util.Optional;
import java.util.stream.Collectors;
@Linked
public class BackupScheduler implements Enable {
@@ -44,7 +43,6 @@ public class BackupScheduler implements Enable {
Iterator<Region> regionsToBackup = RegionSystem.INSTANCE.getRegions()
.filter(region -> region.getRegionData().has(Flag.CHANGED).isReadable())
.filter(region -> region.getRegionData().get(Flag.CHANGED).isWithDefault(ChangedMode.HAS_CHANGE))
.collect(Collectors.toList())
.iterator();
if (!regionsToBackup.hasNext()) return;
doBackup(regionsToBackup);

View File

@@ -48,26 +48,14 @@ public class Point {
return new Point(blockVector3.getBlockX(), blockVector3.getBlockY(), blockVector3.getBlockZ());
}
public Point setY(int y) {
return new Point(this.x, y, this.z);
}
public Point add(int x, int y, int z) {
return new Point(this.x + x, this.y + y, this.z + z);
}
public Point add(Point point) {
return add(point.x, point.y, point.z);
}
public Point subtract(int x, int y, int z) {
return new Point(this.x - x, this.y - y, this.z - z);
}
public Point subtract(Point point) {
return subtract(point.x, point.y, point.z);
}
public Point divide(int factor) {
return new Point(x / factor, y / factor, z / factor);
}

View File

@@ -24,16 +24,16 @@ import de.steamwar.bausystem.utils.FlatteningWrapper;
import de.steamwar.bausystem.utils.PasteBuilder;
import de.steamwar.sql.GameModeConfig;
import lombok.NonNull;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import javax.annotation.Nullable;
import java.io.File;
import java.util.UUID;
import java.util.function.BiConsumer;
import java.util.stream.Stream;
public interface Region extends RegionDataStore {
public interface Region {
static Stream<Region> getRegions() {
return RegionSystem.INSTANCE.getRegions();
@@ -76,9 +76,6 @@ public interface Region extends RegionDataStore {
interface Area {
int WORLD_MIN_Y = Bukkit.getWorlds().get(0).getMinHeight();
int WORLD_MAX_Y = Bukkit.getWorlds().get(0).getMaxHeight();
Area EMPTY = new Area() {
@Override
public boolean isEmpty() {
@@ -111,7 +108,12 @@ public interface Region extends RegionDataStore {
}
@Override
public void place(Location location, PasteBuilder pasteBuilder, boolean extension) {
public File getResetFile() {
return null;
}
@Override
public void reset(PasteBuilder pasteBuilder, boolean extension) {
}
@Override
@@ -146,24 +148,15 @@ public interface Region extends RegionDataStore {
return true;
}
default boolean inRegion(int x, int z, boolean extension) {
Point minPoint = getMinPoint(extension);
Point maxPoint = getMaxPoint(extension);
if (x < minPoint.getX() || x > maxPoint.getX()) return false;
if (z < minPoint.getZ() || z > maxPoint.getZ()) return false;
return true;
}
@Nullable
default Clipboard copy(boolean extension) {
return FlatteningWrapper.impl.copy(getMinPoint(extension), getMaxPoint(extension), getCopyPoint());
}
default void reset(Location location, PasteBuilder pasteBuilder, boolean extension) {
place(location, pasteBuilder, extension);
}
@Nullable
File getResetFile();
void place(Location location, PasteBuilder pasteBuilder, boolean extension);
void reset(PasteBuilder pasteBuilder, boolean extension);
default void forEachChunk(BiConsumer<Integer, Integer> executor) {
Point minPoint = getMinPoint(false);

View File

@@ -22,19 +22,14 @@ package de.steamwar.bausystem.region;
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.bukkit.Location;
import javax.annotation.CheckReturnValue;
import javax.annotation.Nullable;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
public interface RegionBackups {
DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy.MM.dd' 'HH:mm:ss");
@RequiredArgsConstructor
enum BackupType {
MANUAL(5),
@@ -44,44 +39,22 @@ public interface RegionBackups {
public final int maxBackups;
}
@RequiredArgsConstructor
@Getter
abstract class Backup implements RegionDataStore, Comparable<Backup> {
abstract class Backup {
@NonNull
protected final BackupType type;
private final BackupType type;
@NonNull
protected final String name;
private final String name;
@NonNull
protected final RegionData regionData;
protected Backup(@NonNull BackupType type, @NonNull String name, @NonNull Function<Backup, RegionData> regionDataConstructor) {
this.type = type;
this.name = name;
regionData = regionDataConstructor.apply(this);
}
private final RegionData data;
@CheckReturnValue
public abstract boolean load();
@Override
public final void save() {
}
public abstract long getCreationTime();
@Override
public int compareTo(Backup o) {
return Long.compare(getCreationTime(), o.getCreationTime());
}
@Override
public final void load(RegionData regionData) {
}
@SuppressWarnings("java:S3038") // This forces everybody to implement 'delete' for Backups!
@Override
public abstract void delete(Location location);
public abstract void delete();
}
@CheckReturnValue
@@ -91,7 +64,7 @@ public interface RegionBackups {
List<Backup> list();
@Nullable
Backup get(@NonNull String name);
Backup get(String name);
RegionBackups EMPTY = new RegionBackups() {
@Override
@@ -106,7 +79,7 @@ public interface RegionBackups {
@Nullable
@Override
public Backup get(@NonNull String name) {
public Backup get(String name) {
return null;
}
};

View File

@@ -22,42 +22,81 @@ package de.steamwar.bausystem.region;
import de.steamwar.bausystem.region.flags.Flag;
import de.steamwar.sql.SchematicNode;
import lombok.NonNull;
import yapion.hierarchy.types.YAPIONObject;
import java.util.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Stream;
public abstract class RegionData {
protected RegionDataStore store;
private final List<Property<?, ?>> properties = new ArrayList<>();
protected final YAPIONObject data;
protected final YAPIONObject flagData;
protected final Runnable onChange;
protected final Map<Flag<?>, Flag.Value<?>> flagMap = new HashMap<>();
// TODO: These should be turned into an ECS like System because not every Region has them or should have them!
protected final Property<SchematicNode, Integer> testblockSchematic = new Property<>("testblockSchematic", SchematicNode::byId, SchematicNode::getId);
protected RegionData(RegionDataStore store) {
this.store = store;
initialize();
store.load(this);
private final class Property<T, K> {
private final String field;
private final Function<K, T> loader;
private final Function<T, K> writer;
private T value;
public Property(String field, Function<K, T> loader, Function<T, K> writer) {
this.field = field;
this.loader = loader;
this.writer = writer;
properties.add(this);
}
public void load() {
if (flagData.containsKey(field)) {
value = loader.apply(flagData.getPlainValue(field));
} else {
value = null;
}
}
public T get() {
return value;
}
public void set(T value) {
this.value = value;
if (value == null) {
flagData.remove(field);
} else {
flagData.put(field, writer.apply(value));
}
}
}
public final void setStore(RegionDataStore store) {
this.store = store;
store.save();
private Property<SchematicNode, Integer> testblockSchematic = new Property<>("testblockSchematic", SchematicNode::byId, SchematicNode::getId);
protected RegionData(YAPIONObject data, Runnable onChange) {
this.data = data;
this.flagData = data.getObjectOrSetDefault("flagStorage", new YAPIONObject());
this.onChange = onChange;
initialize();
for (final Flag flag : Flag.getFlags()) {
if (!has(flag).isWritable()) continue;
try {
String s = flagData.getPlainValue(flag.name());
flagMap.put(flag, flag.valueOfValue(s));
} catch (Exception e) {
flagMap.put(flag, (Flag.Value<?>) flag.getDefaultValue());
}
}
properties.forEach(Property::load);
}
protected void initialize() {
}
/**
* All connected Regions are required to have the same type as their RegionData.
*/
protected Stream<Region> connectedRegions() {
return Stream.empty();
}
@NonNull
public abstract <T extends Enum<T> & Flag.Value<T>> RegionFlagPolicy has(@NonNull Flag<T> flag);
@@ -66,15 +105,11 @@ public abstract class RegionData {
*/
public final <T extends Enum<T> & Flag.Value<T>> boolean set(@NonNull Flag<T> flag, @NonNull T value) {
if (has(flag).isWritable()) {
boolean needsSave = flagMap.put(flag, value) != value;
if (needsSave) store.save();
connectedRegions().forEach(region -> {
if (region.getRegionData().flagMap.put(flag, value) != value) {
region.save();
}
});
return needsSave;
if (flagMap.put(flag, value) != value) {
flagData.put(flag.name(), value.name());
onChange.run();
return true;
}
}
return false;
}
@@ -85,94 +120,33 @@ public abstract class RegionData {
}
public final void clear() {
Set<Flag> remove = new HashSet<>();
for (Flag flag : Flag.getFlags()) {
if (has(flag).isWritable()) {
flagMap.remove(flag);
remove.add(flag);
flagData.remove(flag.name());
}
}
initialize();
properties.forEach(property -> property.set(null));
store.save();
connectedRegions().forEach(region -> {
region.getRegionData().flagMap.keySet().removeAll(remove);
region.getRegionData().initialize();
region.getRegionData().properties.forEach(property -> property.set(null));
region.save();
});
}
/**
* This method only copies all flags and properties from this into other without saving other afterward.
* TODO: If {@link #connectedRegions()} is overridden this method will not work correctly!
*/
public final void copyInto(RegionData other) {
if (this == other) return;
other.flagMap.clear();
other.flagMap.putAll(flagMap);
// TODO: This might not be correct, needs to be investigated
other.properties.clear();
other.properties.addAll(properties);
onChange.run();
}
public final Map<Flag<?>, Flag.Value<?>> getBackedMap() {
return flagMap;
}
public final List<Property<Object, Object>> getBackedProperties() {
return (List) properties;
}
public final SchematicNode getTestblockSchematic() {
public SchematicNode getTestblockSchematic() {
return testblockSchematic.get();
}
public final void setTestblockSchematic(SchematicNode schematic) {
public void setTestblockSchematic(SchematicNode schematic) {
testblockSchematic.set(schematic);
store.save();
onChange.run();
}
@Override
public final String toString() {
StringBuilder st = new StringBuilder();
st.append(getClass().getSimpleName()).append("{");
st.append("flagMap=").append(flagMap);
for (Property<?, ?> p : properties) {
st.append(p);
}
st.append("}");
return st.toString();
}
public final class Property<T, K> {
public final String field;
public final Function<K, T> loader;
public final Function<T, K> writer;
private T value;
private Property(String field, Function<K, T> loader, Function<T, K> writer) {
this.field = field;
this.loader = loader;
this.writer = writer;
properties.add(this);
}
public T get() {
return value;
}
public void set(T value) {
this.value = value;
}
@Override
public String toString() {
Object value = this.value;
if (value != null) value = writer.apply((T) value);
return ", " + field + "=" + value;
}
return getClass().getSimpleName() + "{" +
"flagMap=" + flagMap +
'}';
}
}

View File

@@ -35,8 +35,4 @@ public enum RegionFlagPolicy {
public boolean isApplicable() {
return readable || writable;
}
public boolean notVisibleInScoreboard() {
return !writable;
}
}

View File

@@ -19,8 +19,6 @@
package de.steamwar.bausystem.region;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.core.Core;
import lombok.NonNull;
import org.bukkit.Location;
@@ -28,15 +26,10 @@ import javax.annotation.CheckReturnValue;
import java.lang.reflect.InvocationTargetException;
import java.util.Optional;
import java.util.UUID;
import java.util.logging.Logger;
import java.util.stream.Stream;
public interface RegionSystem {
Logger LOGGER = BauSystem.getInstance().getLogger();
UUID GLOBAL_REGION_ID = new UUID(0, 0);
RegionSystem INSTANCE = init();
/**
@@ -44,6 +37,11 @@ public interface RegionSystem {
*/
void load();
/**
* Saves anything that should be written to file on either CRIUSleepEvent or plugin disable.
*/
void save();
/**
* Returns the Location to teleport players to when they first join or Warp to "WorldSpawn"
*/
@@ -76,63 +74,47 @@ public interface RegionSystem {
@NonNull
Stream<Region> getRegions();
/**
* Only contains Regions of the same Type as the one you inputted.
*/
@NonNull
Stream<Region> getConnectedRegions(Region region);
private static RegionSystem init() {
if (Core.getVersion() >= 21) {
// TODO: Add some kind of detection if the DynamicRegionSystem should be used!
try {
return (RegionSystem) Class.forName("de.steamwar.bausystem.region.DynamicRegionSystem").getConstructor().newInstance();
} catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException |
InvocationTargetException e) {
// Ignore
}
}
try {
return (RegionSystem) Class.forName("de.steamwar.bausystem.region.FixedRegionSystem").getConstructor().newInstance();
} catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException |
InvocationTargetException e) {
// Ignore
return new RegionSystem() {
@Override
public void load() {
throw new UnsupportedOperationException();
}
@Override
public void save() {
throw new UnsupportedOperationException();
}
@Override
public @NonNull Location getWorldSpawn() {
throw new UnsupportedOperationException();
}
@Override
public Region getGlobalRegion() {
throw new UnsupportedOperationException();
}
@Override
public Region get(Location location) {
throw new UnsupportedOperationException();
}
@Override
public Optional<Region> getRegion(UUID id) {
throw new UnsupportedOperationException();
}
@Override
public Stream<Region> getRegions() {
throw new UnsupportedOperationException();
}
};
}
return new RegionSystem() {
@Override
public void load() {
throw new UnsupportedOperationException();
}
@Override
public @NonNull Location getWorldSpawn() {
throw new UnsupportedOperationException();
}
@Override
public Region getGlobalRegion() {
throw new UnsupportedOperationException();
}
@Override
public Region get(Location location) {
throw new UnsupportedOperationException();
}
@Override
public Optional<Region> getRegion(UUID id) {
throw new UnsupportedOperationException();
}
@Override
public Stream<Region> getRegions() {
throw new UnsupportedOperationException();
}
@Override
public @NonNull Stream<Region> getConnectedRegions(Region region) {
throw new UnsupportedOperationException();
}
};
}
}

View File

@@ -26,50 +26,9 @@ import lombok.RequiredArgsConstructor;
@Getter
public enum RegionType {
GLOBAL(ConnectionType.Global),
/**
* This should not be used by the DynamicRegionSystem
*/
NORMAL(ConnectionType.Closed),
SPAWN(ConnectionType.Closed),
SPAWN_PATH(ConnectionType.Path),
SPAWN_EXTENSION(ConnectionType.Closed),
PATH(ConnectionType.Path),
DRY(ConnectionType.Closed),
DRY_SPECIAL(ConnectionType.Closed),
WET(ConnectionType.Water),
WET_SPECIAL(ConnectionType.Water),
GLOBAL(true),
NORMAL(false),
;
private final ConnectionType connectionType;
public boolean isGlobal() {
return this == GLOBAL;
}
public boolean isDeletable() {
return this == NORMAL || this == SPAWN_EXTENSION || this == PATH || this == DRY || this == DRY_SPECIAL || this == WET || this == WET_SPECIAL;
}
public boolean isSpecial() {
return this == DRY_SPECIAL || this == WET_SPECIAL;
}
public boolean isSpawn() {
return this == SPAWN || this == SPAWN_PATH || this == SPAWN_EXTENSION;
}
public boolean isPath() {
return this == PATH || this == SPAWN_PATH;
}
public enum ConnectionType {
Closed,
Path,
Water,
Global,
Garden,
}
private final boolean global;
}

View File

@@ -28,7 +28,6 @@ import org.bukkit.entity.Player;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Stream;
@UtilityClass
public class RegionUtils {
@@ -59,12 +58,10 @@ public class RegionUtils {
}
public void forEachInRegion(Region region, Consumer<Player> consumer) {
Stream<? extends Player> players = Bukkit.getOnlinePlayers().stream();
if (region.getType().isGlobal()) {
players = players.filter(player -> Region.getRegion(player.getLocation()).getType().isGlobal());
} else {
players = players.filter(player -> region.getArea().inRegion(player.getLocation(), false));
}
players.forEach(consumer);
Bukkit.getOnlinePlayers()
.stream()
.filter(player -> region.getArea().inRegion(player.getLocation(), false))
.filter(player -> !region.getType().isGlobal() || Region.getRegion(player.getLocation()).getType().isGlobal())
.forEach(consumer);
}
}

View File

@@ -42,7 +42,7 @@ import java.util.function.BiPredicate;
@Getter
public class PasteBuilder {
private ClipboardProvider clipboardProvider;
private final ClipboardProvider clipboardProvider;
private Point pastPoint;
private boolean rotate;
private boolean ignoreAir;
@@ -53,20 +53,10 @@ public class PasteBuilder {
private List<BiPredicate<BaseBlock, String>> predicates = new ArrayList<>();
private List<BiConsumer<Clipboard, BlockVector3>> mappers = new ArrayList<>();
public PasteBuilder() {
clipboardProvider = ClipboardProvider.EMPTY;
}
public PasteBuilder(@NonNull ClipboardProvider clipboardProvider) {
this.clipboardProvider = clipboardProvider;
}
public PasteBuilder with(@NonNull ClipboardProvider clipboardProvider) {
if (this.clipboardProvider != ClipboardProvider.EMPTY) return this;
this.clipboardProvider = clipboardProvider;
return this;
}
public PasteBuilder pastePoint(Point point) {
this.pastPoint = point;
return this;
@@ -102,16 +92,6 @@ public class PasteBuilder {
return this;
}
private PasteBuilder predicates(List<BiPredicate<BaseBlock, String>> predicates) {
this.predicates = predicates;
return this;
}
public PasteBuilder mappers(List<BiConsumer<Clipboard, BlockVector3>> mappers) {
this.mappers = mappers;
return this;
}
public PasteBuilder only(BiPredicate<BaseBlock, String> predicate) {
predicates.add(predicate);
return this;
@@ -202,27 +182,13 @@ public class PasteBuilder {
}
public EditSession run() {
if (pastPoint != null || minPoint != null) {
return FlatteningWrapper.impl.paste(this);
if (pastPoint == null) {
throw new IllegalStateException("pastePoint is null");
}
throw new IllegalStateException("pastePoint is null");
return FlatteningWrapper.impl.paste(this);
}
public interface ClipboardProvider {
ClipboardProvider EMPTY = new EmptyProvider();
static ClipboardProvider file(File file) {
return new FileProvider(file);
}
static ClipboardProvider schematic(SchematicNode schematic) {
return new SchematicProvider(schematic);
}
static ClipboardProvider clipboard(Clipboard clipboard) {
return new ClipboardProviderImpl(clipboard);
}
Clipboard getClipboard();
default <T extends ClipboardProvider> boolean is(Class<T> clazz) {
@@ -234,22 +200,12 @@ public class PasteBuilder {
}
}
private static class EmptyProvider implements ClipboardProvider {
private EmptyProvider() {
}
@Override
public Clipboard getClipboard() {
return null;
}
}
@Getter
public static class FileProvider implements ClipboardProvider {
private final File file;
private final Clipboard clipboard;
private FileProvider(File file) {
public FileProvider(File file) {
this.file = file;
this.clipboard = FlatteningWrapper.impl.loadSchematic(file);
}
@@ -265,7 +221,7 @@ public class PasteBuilder {
private final SchematicNode schematic;
private final Clipboard clipboard;
private SchematicProvider(SchematicNode schematic) {
public SchematicProvider(SchematicNode schematic) {
this.schematic = schematic;
try {
this.clipboard = new SchematicData(schematic).load();
@@ -284,7 +240,7 @@ public class PasteBuilder {
public static class ClipboardProviderImpl implements ClipboardProvider {
private final Clipboard clipboard;
private ClipboardProviderImpl(Clipboard clipboard) {
public ClipboardProviderImpl(Clipboard clipboard) {
this.clipboard = clipboard;
}

View File

@@ -1,49 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2024 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/>.
*/
plugins {
steamwar.java
}
tasks.compileJava {
options.isWarnings = false
}
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
dependencies {
compileOnly(libs.classindex)
annotationProcessor(libs.classindex)
compileOnly(project(":BauSystem:BauSystem_Main", "default"))
compileOnly(project(":SpigotCore", "default"))
compileOnly(libs.spigotapi)
compileOnly(libs.axiom)
compileOnly(libs.authlib)
compileOnly(libs.viaapi)
compileOnly(libs.nms20)
compileOnly(libs.fawe18)
implementation(libs.luaj)
}

View File

@@ -1,77 +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.bausystem.features.region;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.region.Point;
import de.steamwar.bausystem.region.Region;
import de.steamwar.bausystem.region.RegionUtils;
import de.steamwar.bausystem.region.flags.Flag;
import de.steamwar.bausystem.region.flags.TestblockMode;
import de.steamwar.bausystem.utils.PasteBuilder;
import de.steamwar.command.SWCommand;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.util.logging.Level;
public class WireframeCommand extends SWCommand {
public WireframeCommand() {
super("wireframe");
}
@Register
public void wireframeCommand(@Validator Player p) {
Region region = regionCheck(p);
if (region == null) return;
try {
PasteBuilder pasteBuilder = new PasteBuilder(PasteBuilder.ClipboardProvider.EMPTY)
.ignoreAir(true)
.color(region.getRegionData().get(Flag.COLOR).getWithDefault());
region.getBuildArea().reset(p.getLocation(), pasteBuilder, false);
RegionUtils.message(region, "REGION_TB_DONE");
} catch (SecurityException e) {
BauSystem.MESSAGE.send("REGION_TB_ERROR", p);
Bukkit.getLogger().log(Level.WARNING, "Failed testblock", e);
}
}
private Region regionCheck(Player player) {
Region region = Region.getRegion(player.getLocation());
if (region.getRegionData().has(Flag.TESTBLOCK).isWritable() && region.getRegionData().get(Flag.TESTBLOCK).isWithDefault(TestblockMode.NO_VALUE)) {
Point minPoint = region.getArea().getMinPoint(false);
Point maxPoint = region.getArea().getMaxPoint(false);
// TODO: Check if empty!
int half = minPoint.getZ() + (maxPoint.getZ() - minPoint.getZ()) / 2;
if (player.getLocation().getBlockZ() <= half) {
region.getRegionData().set(Flag.TESTBLOCK, TestblockMode.SOUTH);
} else {
region.getRegionData().set(Flag.TESTBLOCK, TestblockMode.NORTH);
}
}
if (region.getTestblockArea().isEmpty()) {
BauSystem.MESSAGE.send("REGION_TB_NO_REGION", player);
return null;
}
return region;
}
}

View File

@@ -1,9 +0,0 @@
{
"flags": {
"TNT": "DENY",
"FREEZE": "INACTIVE"
},
"properties": {
"testblockSchematic": 0
}
}

View File

@@ -1,6 +0,0 @@
{
"region_identifier": "SpawnRegion",
"tiles": [
{ "tile_x": 0, "tile_z": 0 }
]
}

View File

@@ -1,50 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2020 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.region;
import de.steamwar.bausystem.Permission;
import de.steamwar.bausystem.features.region.RegionCommand;
import de.steamwar.bausystem.region.dynamic.Tile;
import de.steamwar.command.AbstractSWCommand;
import de.steamwar.command.SWCommand;
import de.steamwar.core.SWPlayer;
import org.bukkit.entity.Player;
@AbstractSWCommand.PartOf(RegionCommand.class)
public class DynamicRegionCommand extends SWCommand {
public DynamicRegionCommand() {
super("");
}
@Register({"dynamic"})
public void visualizeRegions(Player player) {
Tile tile = Tile.fromLocation(player.getLocation()).orElse(null);
if (tile == null) return;
SWPlayer swPlayer = SWPlayer.of(player);
if (swPlayer.hasComponent(DynamicRegionVisualizer.class)) {
swPlayer.removeComponent(DynamicRegionVisualizer.class);
} else if (Permission.SUPERVISOR.hasPermission(player)) {
swPlayer.setComponent(new DynamicRegionVisualizer());
} else {
// TODO: Add Message
}
}
}

View File

@@ -1,249 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2020 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.region;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.features.region.WireframeCommand;
import de.steamwar.bausystem.region.dynamic.*;
import de.steamwar.bausystem.region.dynamic.global.GlobalRegion;
import de.steamwar.bausystem.shared.Pair;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nullable;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class DynamicRegionSystem implements RegionSystem {
private static final int TILE_SIZE_ADJUSTED = Tile.tileSize - 1;
public static DynamicRegionSystem INSTANCE;
private static final Map<Long, Region> regionCache = new LinkedHashMap<>(16, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<Long, Region> eldest) {
return size() > 8192; // Tweak this number if needed!
}
}; // Will be cleared on region add/delete/remove!
private static final Map<UUID, Region> regionMap = new HashMap<>();
private static final Map<RegionType, Set<Region>> regionTypeMap = new EnumMap<>(RegionType.class);
public void add(DynamicRegion region) {
regionCache.clear();
regionMap.put(region.getID(), region);
regionTypeMap.computeIfAbsent(region.getType(), __ -> new HashSet<>()).add(region);
}
public void remove(DynamicRegion region) {
regionCache.clear();
regionMap.remove(region.getID());
regionTypeMap.getOrDefault(region.getType(), Collections.emptySet()).remove(region);
}
public static Map<Class<? extends DynamicRegion>, RegionConstructorData> constructorDataMap = new HashMap<>();
public static Map<String, Class<? extends DynamicRegion>> identifierDataMap = new HashMap<>();
@Override
public void load() {
INSTANCE = this;
// Loading all Region Constructor Data that are defined inside the Code
constructorDataMap = new BufferedReader(new InputStreamReader(BauSystem.getInstance().getClass().getResourceAsStream("/META-INF/annotations/de.steamwar.bausystem.region.dynamic.RegionConstructorData")))
.lines()
.map(s -> {
try {
return Class.forName(s, false, BauSystem.getInstance().getClass().getClassLoader());
} catch (ClassNotFoundException | NoClassDefFoundError e) {
throw new SecurityException(e.getMessage(), e);
}
})
.filter(DynamicRegion.class::isAssignableFrom)
.map(clazz -> (Class<? extends DynamicRegion>) clazz)
.collect(Collectors.toUnmodifiableMap(Function.identity(), clazz -> clazz.getAnnotation(RegionConstructorData.class)));
identifierDataMap = constructorDataMap.entrySet()
.stream()
.collect(Collectors.toUnmodifiableMap(entry -> entry.getValue().identifier(), Map.Entry::getKey));
DynamicRegionRepository.loadRegions();
new DynamicRegionCommand();
new WireframeCommand();
}
@Override
public @NonNull Location getWorldSpawn() {
return Bukkit.getWorlds().get(0).getSpawnLocation(); // TODO: Temporary
}
@Override
public @NonNull Region getGlobalRegion() {
return GlobalRegion.INSTANCE;
}
public Set<Tile> getTilesOfRegion(@NonNull Region region) {
Point minPoint = region.getArea().getMinPoint(false);
Point maxPoint = region.getArea().getMaxPoint(false);
Set<Tile> tiles = new HashSet<>();
for (int x = minPoint.getX(); x < maxPoint.getX(); x += Tile.tileSize) {
for (int z = minPoint.getZ(); z < maxPoint.getZ(); z += Tile.tileSize) {
tiles.add(Tile.fromXZ(x, z).orElse(null));
}
}
tiles.remove(null);
return Collections.unmodifiableSet(tiles);
}
public @NonNull Region get(@Nullable Tile tile) {
if (tile == null) return getGlobalRegion();
return get(tile.getCenterLocation().getBlockX(), tile.getCenterLocation().getBlockZ(), true, regionMap.values());
}
private Region get(int x, int z, boolean fastCache, Collection<Region> regions) {
Tile tile = Tile.fromXZ(x, z).orElse(null);
if (tile == null) {
return getGlobalRegion();
}
if (regionCache.containsKey(tile.getId())) {
Region region = regionCache.get(tile.getId());
if (fastCache || regions.contains(region)) return region;
}
Region region = regions.stream()
.filter(rg -> rg.getArea().inRegion(x, z, false))
.findFirst()
.orElseGet(this::getGlobalRegion);
if (fastCache || regions.contains(region)) {
regionCache.put(tile.getId(), region);
}
return region;
}
@Override
public @NonNull Region get(@NonNull Location location) {
return get(location.getBlockX(), location.getBlockZ(), true, regionMap.values());
}
@Override
public Optional<Region> getRegion(@NonNull UUID id) {
return Optional.ofNullable(regionMap.get(id));
}
@Override
public @NonNull Stream<Region> getRegions() {
return regionMap.values().stream();
}
public @NonNull Stream<Region> getRegionsByType(RegionType type) {
return regionTypeMap.getOrDefault(type, Collections.emptySet()).stream();
}
@RequiredArgsConstructor
public static class Neighbour<T extends Region> {
public final T region;
public final Tile tile;
public final NeighbourDirection direction;
public <O extends Region> Neighbour<O> as(Class<O> clazz) {
if (!clazz.isInstance(region)) {
return null;
} else {
return (Neighbour<O>) this;
}
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Neighbour<?> neighbour)) return false;
return Objects.equals(tile, neighbour.tile) && direction == neighbour.direction;
}
@Override
public int hashCode() {
return Objects.hash(tile, direction);
}
}
private Stream<Neighbour<Region>> getNeighbours2(Region region, boolean noCorners, boolean fastCache, Collection<Region> regions) {
throw new UnsupportedOperationException();
}
private Stream<Pair<Region, NeighbourDirection>> getNeighbours(Region region, boolean noCorners, boolean fastCache, Collection<Region> regions) {
Point minPoint = region.getArea().getMinPoint(false).subtract(TILE_SIZE_ADJUSTED, 0, TILE_SIZE_ADJUSTED);
Point maxPoint = region.getArea().getMaxPoint(false).add(Tile.tileSize, 0, Tile.tileSize);
Set<Pair<Region, NeighbourDirection>> neighbours = new HashSet<>();
for (int x = minPoint.getX() + (noCorners ? TILE_SIZE_ADJUSTED : 0); x <= maxPoint.getX() - (noCorners ? Tile.tileSize : 0); x += Tile.tileSize) {
NeighbourDirection minZ = NeighbourDirection.North;
if (!noCorners) {
if (x == minPoint.getX()) minZ = NeighbourDirection.NorthWest;
if (x > maxPoint.getX() - TILE_SIZE_ADJUSTED) minZ = NeighbourDirection.NorthEast;
}
neighbours.add(new Pair<>(get(x, minPoint.getZ(), fastCache, regions), minZ));
NeighbourDirection maxZ = NeighbourDirection.South;
if (!noCorners) {
if (x == minPoint.getX()) maxZ = NeighbourDirection.SouthWest;
if (x > maxPoint.getX() - TILE_SIZE_ADJUSTED) maxZ = NeighbourDirection.SouthEast;
}
neighbours.add(new Pair<>(get(x, maxPoint.getZ(), fastCache, regions), maxZ));
}
for (int z = minPoint.getZ() + TILE_SIZE_ADJUSTED; z <= maxPoint.getZ() - Tile.tileSize; z += Tile.tileSize) {
neighbours.add(new Pair<>(get(minPoint.getX(), z, fastCache, regions), NeighbourDirection.West));
neighbours.add(new Pair<>(get(maxPoint.getX(), z, fastCache, regions), NeighbourDirection.East));
}
neighbours.removeIf(data -> data.getKey().getType().isGlobal());
return neighbours.stream();
}
public Stream<Pair<DynamicRegion, NeighbourDirection>> getNeighbours(Region region) {
return getNeighbours(region, false, true, regionMap.values())
.filter(data -> data.getKey() instanceof DynamicRegion)
.map(data -> (Pair<DynamicRegion, NeighbourDirection>) (Pair) data);
}
@Override
@NotNull
public Stream<Region> getConnectedRegions(Region region) {
Set<Region> regions = regionTypeMap.get(region.getType());
Set<Region> connected = new HashSet<>();
LinkedHashSet<Region> current = new LinkedHashSet<>();
current.add(region);
while (!current.isEmpty()) {
Region r = current.removeFirst();
if (!connected.add(r)) continue;
getNeighbours(r, true, false, regions)
.map(Pair::getKey)
.forEach(current::add);
}
return connected.stream();
}
}

View File

@@ -1,331 +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.bausystem.region;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.region.dynamic.DynamicRegion;
import de.steamwar.bausystem.region.dynamic.DynamicRegionRepository;
import de.steamwar.bausystem.region.dynamic.RegionConstructorData;
import de.steamwar.bausystem.region.dynamic.Tile;
import de.steamwar.bausystem.utils.PasteBuilder;
import de.steamwar.core.SWPlayer;
import de.steamwar.entity.*;
import de.steamwar.inventory.SWInventory;
import de.steamwar.inventory.SWItem;
import de.steamwar.inventory.SWListInv;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerMoveEvent;
import java.util.*;
import java.util.stream.Collectors;
public class DynamicRegionVisualizer implements SWPlayer.Component, Listener {
private final REntityServer entityServer;
private Player player;
private Location sourceLocation;
private Tile sourceTile;
private Placement placement = null;
public DynamicRegionVisualizer() {
this.entityServer = new REntityServer();
}
@Override
public void onMount(SWPlayer player) {
this.player = player.getPlayer();
sourceTile = Tile.fromLocation(player.getLocation()).orElse(null);
if (sourceTile == null) {
player.removeComponent(DynamicRegionVisualizer.class);
return;
}
sourceLocation = player.getLocation().add(0, -5, 0).getBlock().getLocation();
entityServer.addPlayer(player.getPlayer());
Bukkit.getPluginManager().registerEvents(this, BauSystem.getInstance());
renderTiles(0, 0);
}
@Override
public void onUnmount(SWPlayer player) {
Bukkit.getPluginManager().registerEvents(this, BauSystem.getInstance());
entityServer.close();
}
@EventHandler
public void onPlayerMove(PlayerMoveEvent event) {
if (event.getPlayer() != player) return;
Location position = event.getTo().getBlock().getLocation();
int dx = position.getBlockX() - sourceLocation.getBlockX();
int dz = position.getBlockZ() - sourceLocation.getBlockZ();
renderTiles(dx, dz);
}
private void renderTiles(int dx, int dz) {
Set<Tile> tileSet = entityServer.getEntitiesByType(CTile.class)
.stream()
.filter(cTile -> {
if (Math.abs(cTile.tile.getTileX() - dx) > 40 || Math.abs(cTile.tile.getTileZ() - dz) > 40) {
cTile.die();
return false;
} else {
return true;
}
})
.map(cTile -> cTile.tile)
.collect(Collectors.toSet());
for (int x = dx - 20; x <= dx + 20; x++) {
for (int z = dz - 20; z <= dz + 20; z++) {
Tile tile = sourceTile.add(x, z).orElse(null);
if (tile == null || tileSet.contains(tile)) continue;
new CTile(entityServer, tile);
}
}
}
private void resetTiles(Set<Tile> tiles) {
entityServer.getEntitiesByType(CTile.class)
.stream()
.filter(cTile -> tiles.contains(cTile.tile))
.peek(CTile::die)
.map(cTile -> cTile.tile)
.forEach(tile -> {
new CTile(entityServer, tile);
});
if (placement != null) {
placement.check();
}
}
private class CTile extends CEntity {
private final Tile tile;
public CTile(REntityServer server, Tile tile) {
super(server);
this.tile = tile;
RegionType regionType = DynamicRegionSystem.INSTANCE.get(tile).getType();
Material material = switch (regionType) {
case SPAWN, SPAWN_EXTENSION -> Material.LODESTONE;
case SPAWN_PATH, PATH -> Material.DIRT_PATH;
case DRY, DRY_SPECIAL -> Material.IRON_BLOCK;
case WET, WET_SPECIAL -> Material.LAPIS_BLOCK;
default -> Material.WHITE_CARPET;
};
Location location = sourceLocation.clone().add(tile.getTileX() - sourceTile.getTileX(), 0, tile.getTileZ() - sourceTile.getTileZ());
if (tile.equals(Tile.ZERO)) {
CCubedTextDisplay spawn = new CCubedTextDisplay(entityServer, location);
spawn.setText("§eSPAWN");
spawn.setBackgroundColor(0);
spawn.setShadowed(false);
entities.add(spawn);
} else if (tile.equals(sourceTile)) {
CCubedTextDisplay origin = new CCubedTextDisplay(entityServer, location);
origin.setText("§eORIGIN");
origin.setBackgroundColor(0);
origin.setShadowed(false);
entities.add(origin);
}
RBlockDisplay blockDisplay = new RBlockDisplay(entityServer, location);
blockDisplay.setBlock(material.createBlockData());
entities.add(blockDisplay);
RInteraction interaction = new RInteraction(entityServer, location.clone().add(0.5, 0, 0.5));
interaction.setInteraction((player, entityAction) -> {
if (placement != null) {
placement.click(tile, regionType.isGlobal());
return;
}
if (!regionType.isGlobal()) {
SWInventory inv = new SWInventory(player, 9, "Delete Region: " + tile.display());
inv.setItem(0, new SWItem(SWItem.getDye(10), "§8Cancel", click -> {
player.closeInventory();
}));
inv.setItem(8, new SWItem(SWItem.getDye(1), "§cDelete", click -> {
player.closeInventory();
Region region = DynamicRegionSystem.INSTANCE.get(tile);
Set<Tile> tiles = DynamicRegionSystem.INSTANCE.getTilesOfRegion(region);
region.delete(tile.getCenterLocation());
SWPlayer.allWithSingleComponent(DynamicRegionVisualizer.class)
.forEach(pair -> {
pair.getComponent().resetTiles(tiles);
});
}));
inv.open();
} else {
List<SWListInv.SWListEntry<Map.Entry<Class<? extends DynamicRegion>, RegionConstructorData>>> entries = new ArrayList<>();
DynamicRegionSystem.constructorDataMap.entrySet()
.stream()
.filter(entry -> entry.getValue().placeable())
.sorted(Comparator.comparing(entry -> entry.getValue().name()))
.forEach(entry -> {
entries.add(new SWListInv.SWListEntry<>(new SWItem(entry.getValue().material(), entry.getValue().name()), entry));
});
SWListInv<Map.Entry<Class<? extends DynamicRegion>, RegionConstructorData>> listInv = new SWListInv<>(player, "Select Region for: " + tile.display(), entries, (click, entry) -> {
new Placement(entry.getKey(), entry.getValue(), tile);
player.closeInventory();
});
listInv.open();
}
});
entities.add(interaction);
}
}
private class Placement {
private final Class<? extends DynamicRegion> regionType;
private final RegionConstructorData constructorData;
private CWireframe wireframe;
private Tile sourceTile;
private int dx;
private int dz;
private boolean valid = false;
private Location getMinLocation() {
Tile tile = sourceTile.add(-DynamicRegionVisualizer.this.sourceTile.getTileX(), -DynamicRegionVisualizer.this.sourceTile.getTileZ()).orElse(null);
if (tile == null) tile = Tile.ZERO;
return sourceLocation.clone().add(tile.getTileX(), 0, tile.getTileZ());
}
private Location getMaxLocation() {
Tile tile = sourceTile.add(-DynamicRegionVisualizer.this.sourceTile.getTileX(), -DynamicRegionVisualizer.this.sourceTile.getTileZ()).orElse(null);
if (tile == null) tile = Tile.ZERO;
return sourceLocation.clone().add(tile.getTileX(), 0, tile.getTileZ()).add(dx, 0, dz);
}
public Placement(Class<? extends DynamicRegion> regionType, RegionConstructorData constructorData, Tile sourceTile) {
this.regionType = regionType;
this.constructorData = constructorData;
this.sourceTile = sourceTile;
dx = constructorData.widthX() / Tile.tileSize - 1;
dz = constructorData.widthZ() / Tile.tileSize - 1;
if (dx == 0 && dz == 0) {
place();
return;
}
placement = this;
wireframe = new CWireframe(entityServer);
check();
}
private void check() {
wireframe.setPos1And2(getMinLocation(), getMaxLocation());
for (int x = 0; x <= dx; x++) {
for (int z = 0; z <= dz; z++) {
Tile tile = sourceTile.add(x, z).orElse(null);
if (tile == null || !DynamicRegionSystem.INSTANCE.get(tile).getType().isGlobal()) {
wireframe.setBlock(Material.RED_CONCRETE.createBlockData());
valid = false;
return;
}
}
}
wireframe.setBlock(Material.LIME_CONCRETE.createBlockData());
valid = true;
}
public void click(Tile tile, boolean global) {
if (tile.getTileX() >= sourceTile.getTileX() && tile.getTileX() <= sourceTile.getTileX() + dx && tile.getTileZ() >= sourceTile.getTileZ() && tile.getTileZ() <= sourceTile.getTileZ() + dz) {
SWInventory inv = new SWInventory(player, 9, "Place Region: " + constructorData.name());
inv.setItem(0, new SWItem(SWItem.getDye(1), "§cDeselect", click -> {
placement = null;
wireframe.die();
player.closeInventory();
}));
if (valid) {
inv.setItem(8, new SWItem(SWItem.getDye(10), "§aPlace", click -> {
player.closeInventory();
place();
}));
} else {
inv.setItem(8, new SWItem(SWItem.getDye(8), "§8Place", click -> {
}));
}
inv.open();
return;
}
if (!global) {
return;
}
Set<Tile> tiles = new HashSet<>();
for (int x = 0; x <= dx; x++) {
for (int z = 0; z <= dz; z++) {
tiles.add(Tile.fromTile(x + sourceTile.getTileX(), z + sourceTile.getTileZ()).orElse(null));
}
}
tiles.remove(null);
Tile selected = tiles.stream().min(Comparator.comparing(current -> {
int dx = current.getTileX() - tile.getTileX();
int dz = current.getTileZ() - tile.getTileZ();
return dx * dx + dz * dz;
}))
.orElse(null);
if (selected == null) return;
int dx = tile.getTileX() - selected.getTileX();
int dz = tile.getTileZ() - selected.getTileZ();
Tile newSourceTile = sourceTile.add(dx, dz).orElse(null);
if (newSourceTile == null) return;
sourceTile = newSourceTile;
check();
}
private void place() {
DynamicRegion dynamicRegion = DynamicRegionRepository.constructRegion(regionType, sourceTile);
if (dynamicRegion == null) {
// TODO: Give error to user
return;
}
dynamicRegion.getArea().place(sourceTile.getCenterLocation(), new PasteBuilder(), false);
dynamicRegion.updateNeighbours();
placement = null;
if (wireframe != null) wireframe.die();
Set<Tile> tiles = new HashSet<>();
for (int x = 0; x <= dx; x++) {
for (int z = 0; z <= dz; z++) {
tiles.add(Tile.fromTile(x + sourceTile.getTileX(), z + sourceTile.getTileZ()).orElse(null));
}
}
tiles.remove(null);
SWPlayer.allWithSingleComponent(DynamicRegionVisualizer.class)
.forEach(pair -> pair.getComponent().resetTiles(tiles));
}
}
}

View File

@@ -1,210 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.bausystem.region.dynamic;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.google.gson.stream.JsonWriter;
import de.steamwar.bausystem.region.DynamicRegionSystem;
import de.steamwar.bausystem.region.Point;
import de.steamwar.bausystem.region.Region;
import de.steamwar.bausystem.region.RegionData;
import de.steamwar.bausystem.region.dynamic.path.PathArea;
import de.steamwar.bausystem.region.dynamic.path.PathRegion;
import de.steamwar.bausystem.shared.Pair;
import lombok.Getter;
import lombok.NonNull;
import org.bukkit.Location;
import java.io.IOException;
import java.util.*;
public abstract class DynamicRegion implements Region {
protected final UUID id;
@Getter
protected RegionData regionData = null;
/**
* This Constructor should be used if a Region is placed newly onto the world!
*
* @param tile this parameter is never used but forces the implementor to have it as a parameter
*/
protected DynamicRegion(Tile tile) {
this.id = UUID.randomUUID();
}
/**
* This constructor is used for loading the Region from a file
*
* @param id
* @param tileData this parameter is never used but forces the implementor to have it as a parameter
*/
protected DynamicRegion(UUID id, JsonArray tileData) {
this.id = id;
}
private static Tile readTile(JsonObject tileData, String prefix) {
JsonPrimitive xData = tileData.getAsJsonPrimitive(prefix + "_x");
JsonPrimitive zData = tileData.getAsJsonPrimitive(prefix + "_z");
if (xData == null || zData == null) return null;
return Tile.fromTile(xData.getAsInt(), zData.getAsInt())
.orElse(null);
}
protected static Tile readTile(JsonArray tileData) {
if (tileData.size() != 1) return null;
try {
return readTile(tileData.get(0).getAsJsonObject(), "tile");
} catch (IllegalStateException e) {
return null;
}
}
protected static List<Pair<Tile, Tile>> readQuantizedTiles(JsonArray tileData) {
List<Pair<Tile, Tile>> list = new ArrayList<>();
for (int i = 0; i < tileData.size(); i++) {
JsonObject tileObject;
try {
tileObject = tileData.get(i).getAsJsonObject();
} catch (IllegalArgumentException e) {
return Collections.emptyList();
}
Tile tile = readTile(tileObject, "tile");
if (tile != null) {
list.add(new Pair<>(tile, null));
continue;
}
Tile minTile = readTile(tileObject, "min");
Tile maxTile = readTile(tileObject, "max");
if (minTile == null || maxTile == null) {
return Collections.emptyList();
}
list.add(new Pair<>(minTile, maxTile));
}
return list;
}
private static void writeTile(JsonWriter writer, Tile tile, String prefix) throws IOException {
writer.name(prefix + "_x");
writer.value(tile.getTileX());
writer.name(prefix + "_z");
writer.value(tile.getTileZ());
}
protected static void writeTile(JsonWriter writer, Tile tile) throws IOException {
writer.beginObject();
writeTile(writer, tile, "tile");
writer.endObject();
}
protected static void writeQuantizedTiles(JsonWriter writer, List<Pair<Tile, Tile>> list) throws IOException {
for (Pair<Tile, Tile> pair : list) {
writer.beginObject();
if (pair.getValue() == null) {
writeTile(writer, pair.getKey(), "tile");
} else {
writeTile(writer, pair.getKey(), "min");
writeTile(writer, pair.getValue(), "max");
}
writer.endObject();
}
}
/**
* This method should be called when a Region is created and needs to be saved afterward
*/
protected final void finishCreate() {
finishLoad();
save();
}
/**
* This method should be called when a Region is loaded from file!
*/
protected final void finishLoad() {
DynamicRegionSystem.INSTANCE.add(this);
}
public abstract void writeData(JsonWriter writer) throws IOException;
public final void updateNeighbours() {
List<Pair<PathRegion, NeighbourDirection>> list = DynamicRegionSystem.INSTANCE.getNeighbours(this)
.filter(data -> data.getKey() instanceof PathRegion)
.map(data -> (Pair<PathRegion, NeighbourDirection>) (Pair) data)
.toList();
// Calculate Garden State for all neighbouring PathRegions
Set<UUID> needsFullReset = new HashSet<>();
list.forEach(data -> {
boolean previousGardenState = data.getKey().isGarden();
data.getKey().calculateGardenState();
if (data.getKey().isGarden() != previousGardenState) {
needsFullReset.add(data.getKey().getID());
}
});
// Updating world state for all neighbouring PathRegions
list.forEach(data -> {
if (needsFullReset.contains(data.getKey().getID())) {
data.getKey().getArea().reset(null, null, false); // TODO: Implement!
} else {
data.getKey().update(this, data.getValue().opposite());
}
});
// All full reset regions need to update their neighbours!
needsFullReset.forEach(uuid -> {
Region region = DynamicRegionSystem.INSTANCE.getRegion(uuid).orElse(null);
if (!(region instanceof DynamicRegion dynamicRegion)) return;
DynamicRegionSystem.INSTANCE.getNeighbours(dynamicRegion)
.filter(data -> data.getKey() instanceof PathRegion)
.map(data -> (Pair<PathRegion, NeighbourDirection>) (Pair) data)
.forEach(data -> {
if (data.getKey().isGarden()) return;
data.getKey().update(dynamicRegion, data.getValue().opposite());
});
});
}
public void setRegionData(@NonNull RegionData regionData) {
this.regionData = regionData;
regionData.setStore(this);
}
@Override
public void delete(Location location) {
if (!getType().isDeletable()) return;
DynamicRegionSystem.INSTANCE.remove(this);
DynamicRegionRepository.deleteRegion(this);
Point minPoint = getArea().getMinPoint(false);
Point maxPoint = getArea().getMaxPoint(false);
PasteUtils.reset(minPoint, maxPoint);
this.updateNeighbours();
}
@Override
public @NonNull UUID getID() {
return id;
}
}

View File

@@ -1,329 +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.bausystem.region.dynamic;
import com.google.gson.*;
import com.google.gson.stream.JsonWriter;
import de.steamwar.bausystem.region.*;
import de.steamwar.bausystem.region.dynamic.path.PathRegion;
import de.steamwar.bausystem.region.flags.Flag;
import lombok.Cleanup;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;
import org.bukkit.Bukkit;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.UUID;
import java.util.logging.Level;
@UtilityClass
public class DynamicRegionRepository {
// Example regions:
// steamwar_regions/
// \- 00000000-0000-0000-0000-000000000000/
// | \- flags.json
// \- 9494bbf6-b22c-4050-b62b-4be0594ed8ba/
// \- meta.json
// \- flags.json
// \- backups/
// \- MANUAL/
// | \- 2026.03.01 14:40:00/
// | \- flags.json
// | \- backup.schem
// \- AUTOMATIC/
// \- 2026.02.30 12:00:00/
// | \- flags.json
// | \- backup.schem
// \- 2026.02.28 19:39:00/
// \- flags.json
// \- backup.schem
public static final File REGION_DATA_FOLDER = new File(Bukkit.getWorlds().get(0).getWorldFolder(), "steamwar_regions");
public static final String META_FILE_NAME = "meta.json";
public static final String META_FILE_REGION_IDENTIFIER = "region_identifier";
public static final String META_FILES_TILES = "tiles";
public static final String FLAG_FILE_NAME = "flags.json";
public static final String BACKUPS_DIR_NAME = "backups";
public static final String BACKUP_FILE_NAME = "backup.schem";
public static final String FLAGS_KEY = "flags";
public static final String PROPERTIES_KEY = "properties";
static {
REGION_DATA_FOLDER.mkdirs();
}
public static void loadRegions() {
// Loading all saved regions from the files
File[] regions = REGION_DATA_FOLDER.listFiles();
for (File region : regions) {
UUID regionUUID;
try {
regionUUID = UUID.fromString(region.getName());
} catch (IllegalArgumentException e) {
RegionSystem.LOGGER.log(Level.WARNING, "Failed to resolve region id: " + region.getName());
continue;
}
if (regionUUID.equals(RegionSystem.GLOBAL_REGION_ID)) {
continue;
}
File metaFile = new File(region, META_FILE_NAME);
if (!metaFile.exists() || !metaFile.isFile()) {
RegionSystem.LOGGER.log(Level.WARNING, "Failed to resolve " + region.getName());
continue;
}
JsonObject metaData;
try {
metaData = JsonParser.parseReader(new FileReader(metaFile)).getAsJsonObject();
} catch (JsonIOException e) {
RegionSystem.LOGGER.log(Level.SEVERE, "Failed to read region metadata file (unknown)");
continue;
} catch (JsonSyntaxException | IllegalStateException e) {
RegionSystem.LOGGER.log(Level.SEVERE, "Failed to read region metadata file (invalid json)");
continue;
} catch (FileNotFoundException e) {
RegionSystem.LOGGER.log(Level.SEVERE, "Failed to read region metadata file (not found)");
continue;
}
String identifier;
try {
identifier = metaData.getAsJsonPrimitive(META_FILE_REGION_IDENTIFIER).getAsString();
} catch (ClassCastException | NumberFormatException e) {
RegionSystem.LOGGER.log(Level.SEVERE, "Failed to read region metadata file (invalid json)");
continue;
}
// TODO: Maybe add static method to DynamicRegionSystem
Class<? extends DynamicRegion> regionClass = DynamicRegionSystem.identifierDataMap.get(identifier);
if (regionClass == null) {
RegionSystem.LOGGER.log(Level.SEVERE, "Failed to read region metadata file (region no longer exists)");
continue;
}
JsonArray tileData = metaData.getAsJsonArray(META_FILES_TILES);
if (tileData == null) {
RegionSystem.LOGGER.log(Level.SEVERE, "Failed to read region metadata file (tile is no longer in bounds)");
continue;
}
constructRegion(regionClass, regionUUID, tileData);
}
}
public static DynamicRegion constructRegion(Class<? extends DynamicRegion> clazz, Tile tile) {
Constructor<? extends DynamicRegion> regionConstructor;
try {
regionConstructor = clazz.getConstructor(Tile.class);
} catch (NoSuchMethodException e) {
RegionSystem.LOGGER.log(Level.SEVERE, "Failed to create region (region constructor not found)");
return null;
}
try {
return regionConstructor.newInstance(tile);
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException |
InvocationTargetException e) {
RegionSystem.LOGGER.log(Level.SEVERE, "Failed to create region (invalid data)");
return null;
}
}
public static DynamicRegion constructRegion(Class<? extends DynamicRegion> clazz, UUID uuid, JsonArray tileData) {
Constructor<? extends DynamicRegion> regionConstructor;
try {
regionConstructor = clazz.getConstructor(UUID.class, JsonArray.class);
} catch (NoSuchMethodException e) {
RegionSystem.LOGGER.log(Level.SEVERE, "Failed to read region metadata file (region constructor not found)");
return null;
}
try {
return regionConstructor.newInstance(uuid, tileData);
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException |
InvocationTargetException e) {
RegionSystem.LOGGER.log(Level.SEVERE, "Failed to read region metadata file (invalid data)");
return null;
}
}
public static File getRegionDirectory(Region region) {
return new File(REGION_DATA_FOLDER, region.getID().toString());
}
public static File getBackupsTypeDirectory(Region region, RegionBackups.BackupType backupType) {
File regionDirectory = getRegionDirectory(region);
File backupsDirectory = new File(regionDirectory, BACKUPS_DIR_NAME);
return new File(backupsDirectory, backupType.name());
}
public static File getBackupDirectory(Region region, RegionBackups.Backup backup) {
return new File(getBackupsTypeDirectory(region, backup.getType()), backup.getName());
}
public static void loadRegionData(Region region, RegionData regionData) {
File regionDirectory = getRegionDirectory(region);
if (!regionDirectory.exists()) return;
loadRegionData(regionDirectory, regionData);
}
public static void loadRegionData(Region region, RegionBackups.Backup backup, RegionData regionData) {
File backupDirectory = getBackupDirectory(region, backup);
if (!backupDirectory.exists()) return;
loadRegionData(backupDirectory, regionData);
}
private static void loadRegionData(File regionDirectory, RegionData regionData) {
JsonObject flagsData;
try {
flagsData = JsonParser.parseReader(new FileReader(new File(regionDirectory, FLAG_FILE_NAME))).getAsJsonObject();
} catch (JsonIOException e) {
RegionSystem.LOGGER.log(Level.SEVERE, "Failed to read region metadata file (unknown)");
return;
} catch (JsonSyntaxException | IllegalStateException e) {
RegionSystem.LOGGER.log(Level.SEVERE, "Failed to read region metadata file (invalid json)");
return;
} catch (FileNotFoundException e) {
RegionSystem.LOGGER.log(Level.SEVERE, "Failed to read region metadata file (not found)");
return;
}
JsonObject flags = flagsData.getAsJsonObject(FLAGS_KEY);
for (String key : flags.keySet()) {
Flag flag;
try {
flag = Flag.valueOf(key);
} catch (IllegalArgumentException e) {
continue;
}
String value = flags.getAsJsonPrimitive(key).getAsString();
Flag.Value<?> flagValue;
try {
flagValue = flag.valueOfValue(value);
} catch (IllegalArgumentException e) {
continue;
}
regionData.getBackedMap().put(flag, flagValue);
}
JsonObject properties = flagsData.getAsJsonObject(PROPERTIES_KEY);
// TODO: Implement!
}
public static void saveRegion(Region region) {
if (!(region.getType().isGlobal() || region instanceof DynamicRegion)) {
throw new IllegalArgumentException();
}
File regionDirectory = new File(REGION_DATA_FOLDER, region.getID().toString());
if (!regionDirectory.exists()) {
regionDirectory.mkdir();
}
if (region instanceof DynamicRegion dynamicRegion) {
RegionConstructorData constructorData = DynamicRegionSystem.constructorDataMap.get(region.getClass());
writeMetaFile(regionDirectory, constructorData, dynamicRegion);
}
writeFlagsFile(regionDirectory, region.getRegionData());
}
public static void saveBackup(Region region, RegionBackups.Backup backup) {
File backupDirectory = getBackupDirectory(region, backup);
if (!backupDirectory.exists()) {
backupDirectory.mkdirs();
}
writeFlagsFile(backupDirectory, backup.getRegionData());
}
@SneakyThrows
private static void writeMetaFile(File regionDirectory, RegionConstructorData constructorData, DynamicRegion dynamicRegion) {
@Cleanup
JsonWriter jsonWriter = new JsonWriter(new FileWriter(new File(regionDirectory, META_FILE_NAME)));
jsonWriter.setIndent(" ");
jsonWriter.beginObject();
jsonWriter.name(META_FILE_REGION_IDENTIFIER);
jsonWriter.value(constructorData.identifier());
jsonWriter.name(META_FILES_TILES);
jsonWriter.beginArray();
dynamicRegion.writeData(jsonWriter);
jsonWriter.endArray();
jsonWriter.endObject();
}
@SneakyThrows
private static void writeFlagsFile(File directory, RegionData regionData) {
JsonWriter jsonWriter = new JsonWriter(new FileWriter(new File(directory, FLAG_FILE_NAME)));
jsonWriter.setIndent(" ");
jsonWriter.beginObject();
jsonWriter.name(FLAGS_KEY);
jsonWriter.beginObject();
for (Flag<?> flag : Flag.getFlags()) {
if (!regionData.has(flag).isApplicable()) continue;
jsonWriter.name(flag.name());
jsonWriter.value(regionData.get(flag).nameWithDefault());
}
jsonWriter.endObject();
jsonWriter.name(PROPERTIES_KEY);
jsonWriter.beginObject();
// TODO: Write out needed properties!
jsonWriter.endObject();
jsonWriter.endObject();
jsonWriter.close();
}
public static void deleteRegion(Region region) {
deleteDir(getRegionDirectory(region));
}
public static void deleteBackup(Region region, RegionBackups.Backup backup) {
deleteDir(getBackupDirectory(region, backup));
}
@SneakyThrows
private static void deleteDir(File file) {
Files.walkFileTree(file.toPath(), new SimpleFileVisitor<>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
Files.delete(file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
Files.delete(dir);
return FileVisitResult.CONTINUE;
}
});
}
}

View File

@@ -1,221 +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.bausystem.region.dynamic;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import de.steamwar.bausystem.region.Point;
import de.steamwar.bausystem.region.Region;
import de.steamwar.bausystem.shared.Pair;
import lombok.NonNull;
import org.bukkit.Location;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.logging.Logger;
public abstract class MultiTileArea implements Region.Area {
private final Logger LOGGER = Logger.getLogger(this.getClass().getTypeName());
protected final Set<Tile> tiles = new HashSet<>();
protected MultiTileArea() {
}
protected MultiTileArea(List<Pair<Tile, Tile>> list) {
for (Pair<Tile, Tile> pair : list) {
Tile from = pair.getKey();
Tile to = pair.getValue();
if (to == null) {
tiles.add(from);
continue;
}
for (int x = from.getTileX(); x <= to.getTileX(); x++) {
for (int z = from.getTileZ(); z <= to.getTileZ(); z++) {
tiles.add(Tile.fromTile(x, z).orElseThrow());
}
}
}
}
/**
* @return {@code true} if the {@link MultiTileArea} did not already contain the {@link Tile}.
*/
public boolean addTile(Tile tile) {
return tiles.add(tile);
}
/**
* @return {@code true} if the {@link MultiTileArea} did not already contain any {@link Tile} passed as argument.
*/
public boolean addTiles(Set<Tile> tiles) {
return this.tiles.addAll(tiles);
}
/**
* @return {@code true} if the {@link MultiTileArea} no longer has any {@link Tile}'s associated with it.
*/
public boolean removeTile(Tile tile) {
tiles.remove(tile);
return tiles.isEmpty();
}
public List<Pair<Tile, Tile>> quantize() {
long time = System.currentTimeMillis();
Quantizer quantizer = new Quantizer(tiles);
List<Pair<Tile, Tile>> results = new ArrayList<>();
while (true) {
Pair<Tile, Tile> pair = quantizer.next();
if (pair == null) break;
results.add(pair);
}
time = System.currentTimeMillis() - time;
LOGGER.info(tiles.size() + ": " + results.size() + " in " + time + "ms");
return results;
}
@Override
public @NonNull Point getMinPoint(boolean extension) {
return Point.ZERO;
}
@Override
public @NonNull Point getMaxPoint(boolean extension) {
return Point.ZERO;
}
@Override
public @NonNull Point getCopyPoint() {
return Point.ZERO;
}
@Override
public boolean inRegion(Location location, boolean extension) {
return inRegion(location.getBlockX(), location.getBlockZ(), extension);
}
@Override
public boolean inRegion(int x, int z, boolean extension) {
Tile tile = Tile.fromXZ(x, z).orElse(null);
return tiles.contains(tile);
}
@Override
public @Nullable Clipboard copy(boolean extension) {
return null;
}
@Override
public void forEachChunk(BiConsumer<Integer, Integer> executor) {
}
@Override
public boolean isChunkOutside(int chunkX, int chunkZ) {
return true;
}
private static class Quantizer {
private Map<Tile, Integer> tileToNeighbourCount = new HashMap<>();
public Quantizer(Set<Tile> tiles) {
for (Tile tile : tiles) {
int count = (int) tile.neighboursRing()
.filter(tiles::contains)
.count();
tileToNeighbourCount.put(tile, count);
}
}
public Pair<Tile, Tile> next() {
Map.Entry<Tile, Integer> entry = tileToNeighbourCount.entrySet()
.stream()
.max(Map.Entry.comparingByValue())
.orElse(null);
if (entry == null) return null;
Tile minTile = entry.getKey();
Tile maxTile = entry.getKey();
tileToNeighbourCount.remove(entry.getKey());
while (true) {
Pair<Set<Tile>, Integer> neg_x = tryExpand(minTile, maxTile, -1, 0);
Pair<Set<Tile>, Integer> pos_x = tryExpand(minTile, maxTile, 1, 0);
Pair<Set<Tile>, Integer> neg_z = tryExpand(minTile, maxTile, 0, -1);
Pair<Set<Tile>, Integer> pos_z = tryExpand(minTile, maxTile, 0, 1);
if (neg_x.getValue() == 0 && pos_x.getValue() == 0 && neg_z.getValue() == 0 && pos_z.getValue() == 0) {
break;
}
Set<Tile> remove;
if (neg_x.getValue() >= pos_x.getValue() && neg_x.getValue() >= neg_z.getValue() && neg_x.getValue() >= pos_z.getValue()) {
minTile = minTile.add(-1, 0).orElseThrow();
remove = neg_x.getKey();
} else if (pos_x.getValue() >= neg_x.getValue() && pos_x.getValue() >= neg_z.getValue() && pos_x.getValue() >= pos_z.getValue()) {
maxTile = maxTile.add(1, 0).orElseThrow();
remove = pos_x.getKey();
} else if (neg_z.getValue() >= neg_x.getValue() && neg_z.getValue() >= pos_x.getValue() && neg_z.getValue() >= pos_z.getValue()) {
minTile = minTile.add(0, -1).orElseThrow();
remove = neg_z.getKey();
} else {
maxTile = maxTile.add(0, 1).orElseThrow();
remove = pos_z.getKey();
}
remove.forEach(tileToNeighbourCount::remove);
}
return new Pair<>(minTile, minTile.equals(maxTile) ? null : maxTile);
}
private static final Pair<Set<Tile>, Integer> EMPTY = new Pair<>(Collections.emptySet(), 0);
private Pair<Set<Tile>, Integer> tryExpand(Tile minTile, Tile maxTile, int dx, int dz) {
Set<Tile> expaned = new HashSet<>();
if (dx == 0) {
for (int x = minTile.getTileX(); x <= maxTile.getTileX(); x++) {
int z = (dz < 0 ? minTile.getTileZ() : maxTile.getTileZ()) + dz;
Tile tile = Tile.fromTile(x, z).orElse(null);
if (tile == null) return EMPTY;
if (!tileToNeighbourCount.containsKey(tile)) return EMPTY;
expaned.add(tile);
}
} else if (dz == 0) {
for (int z = minTile.getTileZ(); z <= maxTile.getTileZ(); z++) {
int x = (dx < 0 ? minTile.getTileX() : maxTile.getTileX()) + dx;
Tile tile = Tile.fromTile(x, z).orElse(null);
if (tile == null) return EMPTY;
if (!tileToNeighbourCount.containsKey(tile)) return EMPTY;
expaned.add(tile);
}
} else {
throw new IllegalStateException();
}
int total = expaned.stream()
.mapToInt(tileToNeighbourCount::get)
.sum();
return new Pair<>(expaned, total);
}
}
}

View File

@@ -1,59 +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.bausystem.region.dynamic;
import de.steamwar.bausystem.region.dynamic.path.PathCorner;
import de.steamwar.bausystem.region.dynamic.path.PathSide;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Set;
@AllArgsConstructor
@Getter
public enum NeighbourDirection {
North(0, -1, Set.of(PathSide.North), Set.of(PathCorner.NorthEast, PathCorner.NorthWest)),
South(0, 1, Set.of(PathSide.South), Set.of(PathCorner.SouthEast, PathCorner.SouthWest)),
West(-1, 0, Set.of(PathSide.West), Set.of(PathCorner.NorthWest, PathCorner.SouthWest)),
East(1, 0, Set.of(PathSide.East), Set.of(PathCorner.NorthEast, PathCorner.SouthEast)),
NorthWest(-1, -1, Set.of(), Set.of(PathCorner.NorthWest)),
NorthEast(1, -1, Set.of(), Set.of(PathCorner.NorthEast)),
SouthWest(-1, 1, Set.of(), Set.of(PathCorner.SouthWest)),
SouthEast(1, 1, Set.of(), Set.of(PathCorner.SouthEast)),
;
private final int tileOffsetX;
private final int tileOffsetZ;
private final Set<PathSide> sideUpdates;
private final Set<PathCorner> cornerUpdates;
public NeighbourDirection opposite() {
return switch (this) {
case North -> South;
case South -> North;
case East -> West;
case West -> East;
case NorthWest -> SouthEast;
case NorthEast -> SouthWest;
case SouthWest -> NorthEast;
case SouthEast -> NorthWest;
};
}
}

View File

@@ -1,62 +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.bausystem.region.dynamic;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.transform.AffineTransform;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.world.block.BlockTypes;
import de.steamwar.bausystem.region.Point;
import de.steamwar.bausystem.utils.FlatteningWrapper;
import lombok.experimental.UtilityClass;
import org.bukkit.Bukkit;
import java.io.File;
@UtilityClass
public class PasteUtils {
public static void reset(Point minPoint, Point maxPoint) {
EditSession editSession = WorldEdit.getInstance()
.newEditSessionBuilder()
.world(BukkitAdapter.adapt(Bukkit.getWorlds().get(0)))
.checkMemory(false)
.allowedRegionsEverywhere()
.limitUnlimited()
.changeSetNull()
.build();
editSession.setBlocks((com.sk89q.worldedit.regions.Region) new CuboidRegion(minPoint.toBlockVector3(), maxPoint.toBlockVector3()), BlockTypes.AIR.getDefaultState());
editSession.close();
}
public static EditSession paste(File file, Point minPoint, int rotate) {
try (Clipboard clipboard = FlatteningWrapper.impl.loadSchematic(file)) {
BlockVector3 offset = clipboard.getRegion().getMinimumPoint().subtract(clipboard.getOrigin());
BlockVector3 to = minPoint.toBlockVector3().subtract(offset);
return clipboard.paste(BukkitAdapter.adapt(Bukkit.getWorlds().get(0)), to, false, true, new AffineTransform().rotateY(rotate));
} catch (SecurityException exception) {
return null;
}
}
}

View File

@@ -1,40 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.bausystem.region.dynamic;
import org.atteo.classindex.IndexAnnotated;
import org.bukkit.Material;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@IndexAnnotated
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface RegionConstructorData {
String identifier();
String name();
Material material();
int widthX();
int widthZ();
boolean placeable() default true;
}

View File

@@ -1,182 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.bausystem.region.dynamic;
import de.steamwar.bausystem.region.Point;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import org.bukkit.Location;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
@Getter
@EqualsAndHashCode
public class Tile {
public static final Tile ZERO = new Tile(0, 0);
public static final int tileSize = 21;
public static final int tileOffset = tileSize / 2;
public static final int maxTile = 255;
public static final int minTile = -maxTile;
public static final int tilesPerAxis = maxTile * 2 + 1;
private final int tileX;
private final int tileZ;
private Tile(int tileX, int tileZ) {
this.tileX = tileX;
this.tileZ = tileZ;
}
public static Optional<Tile> fromTile(int tileX, int tileZ) {
if (tileX < minTile || tileZ < minTile) return Optional.empty();
if (tileX > maxTile || tileZ > maxTile) return Optional.empty();
return Optional.of(new Tile(tileX, tileZ));
}
public static Optional<Tile> fromLocation(Location location) {
return fromXZ(location.getBlockX(), location.getBlockZ());
}
public static Optional<Tile> fromPoint(Point point) {
return fromXZ(point.getX(), point.getZ());
}
public static Optional<Tile> fromXZ(int x, int z) {
x = (int) Math.floor((x + tileOffset) / (double) tileSize);
z = (int) Math.floor((z + tileOffset) / (double) tileSize);
return fromTile(x, z);
}
public static int getMinX(int tileX) {
return tileX * tileSize - tileOffset;
}
public int getMinX() {
return getMinX(tileX);
}
public static int getMinZ(int tileZ) {
return tileZ * tileSize - tileOffset;
}
public int getMinZ() {
return getMinZ(tileZ);
}
public static int getMaxX(int tileX) {
return tileX * tileSize + tileOffset;
}
public int getMaxX() {
return getMaxX(tileX);
}
public static int getMaxZ(int tileZ) {
return tileZ * tileSize + tileOffset;
}
public int getMaxZ() {
return getMaxZ(tileZ);
}
public static Location getMinLocation(int tileX, int tileZ) {
return new Location(null, getMinX(tileX), 0, getMinZ(tileZ));
}
public Location getMinLocation() {
return getMinLocation(tileX, tileZ);
}
public static Location getMaxLocation(int tileX, int tileZ) {
return new Location(null, getMaxX(tileX), 0, getMaxZ(tileZ));
}
public Location getMaxLocation() {
return getMaxLocation(tileX, tileZ);
}
public static Location getCenterLocation(int tileX, int tileZ) {
return new Location(null, tileX * tileSize, 0, tileZ * tileSize);
}
public Location getCenterLocation() {
return getCenterLocation(tileX, tileZ);
}
public Optional<Tile> add(int offsetX, int offsetZ) {
return fromTile(tileX + offsetX, tileZ + offsetZ);
}
public static long getId(int tileX, int tileZ) {
return (tileX + maxTile) * tilesPerAxis + tileZ + maxTile;
}
public long getId() {
return getId(tileX, tileZ);
}
public Stream<Tile> neighboursPlus() {
List<Tile> tiles = new ArrayList<>();
add(-1, 0).ifPresent(tiles::add);
add(1, 0).ifPresent(tiles::add);
add(0, -1).ifPresent(tiles::add);
add(0, 1).ifPresent(tiles::add);
return tiles.stream();
}
public Stream<Tile> neighboursRing() {
List<Tile> tiles = new ArrayList<>();
add(-1, -1).ifPresent(tiles::add);
add(-1, 0).ifPresent(tiles::add);
add(-1, 1).ifPresent(tiles::add);
add(0, -1).ifPresent(tiles::add);
add(0, 1).ifPresent(tiles::add);
add(1, -1).ifPresent(tiles::add);
add(1, 0).ifPresent(tiles::add);
add(1, 1).ifPresent(tiles::add);
return tiles.stream();
}
@Override
public String toString() {
return tileX + ":" + tileZ;
}
public String display() {
StringBuilder st = new StringBuilder();
if (tileX >= 0) {
st.append("+");
}
st.append(tileX);
st.append(" / ");
if (tileZ >= 0) {
st.append("+");
}
st.append(tileZ);
return st.toString();
}
}

View File

@@ -1,96 +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.bausystem.region.dynamic;
import lombok.NonNull;
import java.io.File;
import java.time.LocalDate;
import java.time.Month;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Optional;
import java.util.Random;
public abstract class VariantSelector {
private static final Random RANDOM = new Random();
public static final VariantSelector EMPTY = new VariantSelector() {
@Override
public Optional<File> select() {
return Optional.empty();
}
};
private VariantSelector() {
}
public abstract Optional<File> select();
public final VariantSelector or(VariantSelector other) {
if (this == EMPTY) return other;
VariantSelector self = this;
return new VariantSelector() {
@Override
public Optional<File> select() {
return self.select().or(() -> other.select());
}
};
}
public final VariantSelector atDate(int day, Month month) {
if (this == EMPTY) return this;
VariantSelector self = this;
return new VariantSelector() {
@Override
public Optional<File> select() {
LocalDate date = LocalDate.now();
if (date.getDayOfMonth() == day && date.getMonth() == month) {
return self.select();
} else {
return Optional.empty();
}
}
};
}
public static VariantSelector Get(@NonNull File directory) {
final File[] files = directory.listFiles();
if (files == null || files.length == 0) return EMPTY;
if (files.length == 1) {
final File file = files[0];
return new VariantSelector() {
@Override
public Optional<File> select() {
return Optional.of(file);
}
};
}
Arrays.sort(files, Comparator.comparing(File::getName));
final int filesCount = files.length;
return new VariantSelector() {
@Override
public Optional<File> select() {
return Optional.of(files[RANDOM.nextInt(filesCount)]);
}
};
}
}

View File

@@ -1,140 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.bausystem.region.dynamic.global;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import de.steamwar.bausystem.region.*;
import de.steamwar.bausystem.region.dynamic.DynamicRegionRepository;
import de.steamwar.bausystem.utils.PasteBuilder;
import de.steamwar.sql.GameModeConfig;
import lombok.NonNull;
import org.bukkit.Location;
import org.bukkit.Material;
import javax.annotation.Nullable;
import java.io.File;
import java.util.UUID;
import java.util.function.BiConsumer;
public class GlobalRegion implements Region {
public static final GlobalRegion INSTANCE = new GlobalRegion();
private static final Point MIN_POINT = new Point(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE);
private static final Point MAX_POINT = new Point(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
private static final Region.Area GLOBAL_AREA = new Region.Area() {
@Override
public @NonNull Point getMinPoint(boolean extension) {
return MIN_POINT;
}
@Override
public @NonNull Point getMaxPoint(boolean extension) {
return MAX_POINT;
}
@Override
public @NonNull Point getCopyPoint() {
return Point.ZERO;
}
@Override
public boolean inRegion(Location location, boolean extension) {
return true;
}
@Nullable
@Override
public Clipboard copy(boolean extension) {
return null;
}
@Override
public void place(Location location, PasteBuilder pasteBuilder, boolean extension) {
}
@Override
public void forEachChunk(BiConsumer<Integer, Integer> executor) {
}
@Override
public boolean isChunkOutside(int chunkX, int chunkZ) {
return false;
}
};
private static final GlobalRegionData REGION_DATA = new GlobalRegionData(INSTANCE);
@Override
public @NonNull UUID getID() {
return RegionSystem.GLOBAL_REGION_ID;
}
@Override
public @NonNull RegionType getType() {
return RegionType.GLOBAL;
}
@Override
public @NonNull RegionData getRegionData() {
return REGION_DATA;
}
@Override
public @NonNull Area getArea() {
return GLOBAL_AREA;
}
@Override
public @NonNull Area getBuildArea() {
return Area.EMPTY;
}
@Override
public @NonNull Area getTestblockArea() {
return Area.EMPTY;
}
@Override
public @NonNull GameModeConfig<Material, String> getGameModeConfig() {
return GameModeConfig.getDefaults();
}
@Override
public @NonNull RegionHistory getHistory() {
return RegionHistory.EMPTY;
}
@Override
public @NonNull RegionBackups getBackups() {
return RegionBackups.EMPTY;
}
@Override
public void save() {
DynamicRegionRepository.saveRegion(this);
}
@Override
public void load(RegionData regionData) {
DynamicRegionRepository.loadRegionData(this, regionData);
}
}

View File

@@ -1,55 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.bausystem.region.dynamic.global;
import de.steamwar.bausystem.region.RegionData;
import de.steamwar.bausystem.region.RegionFlagPolicy;
import de.steamwar.bausystem.region.flags.Flag;
import de.steamwar.bausystem.region.flags.ProtectMode;
import de.steamwar.bausystem.region.flags.TNTMode;
import de.steamwar.core.Core;
import lombok.NonNull;
public class GlobalRegionData extends RegionData {
public GlobalRegionData(GlobalRegion globalRegion) {
super(globalRegion);
}
@Override
protected void initialize() {
flagMap.put(Flag.TNT, TNTMode.DENY);
flagMap.put(Flag.PROTECT, ProtectMode.INACTIVE);
}
@Override
public @NonNull <T extends Enum<T> & Flag.Value<T>> RegionFlagPolicy has(@NonNull Flag<T> flag) {
if (flag.oneOf(Flag.PROTECT)) {
return RegionFlagPolicy.READ_ONLY;
}
if (flag.oneOf(Flag.ITEMS) && Core.getVersion() >= 20) {
return RegionFlagPolicy.WRITABLE;
}
if (flag.oneOf(Flag.TNT, Flag.FIRE, Flag.FREEZE, Flag.WATER_DESTROY)) {
return RegionFlagPolicy.WRITABLE;
}
return RegionFlagPolicy.NOT_APPLICABLE;
}
}

View File

@@ -1,171 +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.bausystem.region.dynamic.modes;
import com.sk89q.worldedit.EditSession;
import de.steamwar.bausystem.region.Point;
import de.steamwar.bausystem.region.Region;
import de.steamwar.bausystem.region.dynamic.VariantSelector;
import de.steamwar.bausystem.shared.Pair;
import de.steamwar.bausystem.utils.PasteBuilder;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.NonNull;
import org.bukkit.Location;
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class AreaBlock implements Region.Area {
public enum CopyLocation {
/**
* If the CopyLocation is located in between the two Area.
*/
CENTER,
/**
* If the CopyLocation is located on the side of the Area.
*/
SIDE
}
/**
* Returns North and then South Region in the Pair.
*/
public static Pair<AreaBlock, AreaBlock> create(Region region, int yOffset, Point size, Point minExtension, Point maxExtension, CopyLocation copyLocation, int distance) {
Point regionMinPoint = region.getArea().getMinPoint(false);
Point regionMaxPoint = region.getArea().getMaxPoint(false);
int regionSizeX = regionMaxPoint.getX() - regionMinPoint.getX() + 1;
int regionSizeZ = regionMaxPoint.getZ() - regionMinPoint.getZ() + 1;
int tempSizeZ = switch (copyLocation) {
case CENTER -> size.getZ() * 2 + distance;
case SIDE -> size.getZ() + distance;
};
// Calculate Offset Region to North
int minOffsetX = regionSizeX / 2 - size.getX() / 2;
int minOffsetZ = regionSizeZ / 2 - tempSizeZ / 2;
// Calculate North Points
Point northMinPoint = regionMinPoint.add(minOffsetX, yOffset, minOffsetZ);
Point northMaxPoint = northMinPoint.add(size).subtract(1, 1, 1);
Point northMinPointExtension = northMinPoint.subtract(minExtension);
Point northMaxPointExtension = northMaxPoint.add(maxExtension);
Point northCopyPoint = switch (copyLocation) {
case CENTER -> northMinPoint.add(size.getX() / 2, 0, size.getZ());
case SIDE -> northMinPoint.add(-1, 0, size.getZ() / 2);
};
// System.out.println(northMinPoint + " " + northMaxPoint + " @ " + northCopyPoint);
// fill(northCopyPoint.add(0, -1, 0), northCopyPoint.add(0, -1, 0), Material.GOLD_BLOCK);
// fill(northMinPointExtension, northMaxPointExtension, Material.RED_STAINED_GLASS);
// fill(northMinPoint, northMaxPoint, Material.RED_WOOL);
// Calculate Offset North to South
minOffsetZ += distance;
switch (copyLocation) {
case CENTER -> minOffsetZ += size.getZ();
case SIDE -> minOffsetZ -= 1;
}
// Calculate South Points
Point southMinPoint = regionMinPoint.add(minOffsetX, yOffset, minOffsetZ);
Point southMaxPoint = southMinPoint.add(size).subtract(1, 1, 1);
Point southMinPointExtension = southMinPoint.subtract(minExtension);
Point southMaxPointExtension = southMaxPoint.add(maxExtension);
Point southCopyPoint = switch (copyLocation) {
case CENTER -> southMinPoint.add(size.getX() / 2, 0, -1);
case SIDE -> southMinPoint.add(-1, 0, size.getZ() / 2);
};
// System.out.println(southMinPoint + " " + southMaxPoint + " @ " + southCopyPoint);
// fill(southCopyPoint.add(0, -1, 0), southCopyPoint.add(0, -1, 0), Material.GOLD_BLOCK);
// fill(southMinPointExtension, southMaxPointExtension, Material.GREEN_STAINED_GLASS);
// fill(southMinPoint, southMaxPoint, Material.GREEN_WOOL);
AreaBlock northArea = new AreaBlock(region, northMinPoint, northMinPointExtension, northMaxPoint, northMaxPointExtension, northCopyPoint);
AreaBlock southArea = new AreaBlock(region, southMinPoint, southMinPointExtension, southMaxPoint, southMaxPointExtension, southCopyPoint);
return new Pair<>(northArea, southArea);
}
public static AreaBlock create(Region region, int yOffset, Point minExtension, Point maxExtension) {
return null; // TODO: Is this needed?
}
/*
private static final World WORLD = Bukkit.getWorlds().get(0);
private static void fill(Point minPoint, Point maxPoint, Material material) {
BlockData blockData = material.createBlockData();
for (int x = minPoint.getX(); x <= maxPoint.getX(); x++) {
for (int z = minPoint.getZ(); z <= maxPoint.getZ(); z++) {
for (int y = minPoint.getY(); y <= maxPoint.getY(); y++) {
WORLD.setBlockData(x, y, z, blockData);
}
}
}
}
*/
private final Region region;
private final Point minPoint;
private final Point minPointExtension;
private final Point maxPoint;
private final Point maxPointExtension;
private final Point copyPoint;
private VariantSelector selector = null;
private AreaBlock(Region region, Point minPoint, Point minPointExtension, Point maxPoint, Point maxPointExtension, Point copyPoint) {
this.region = region;
this.minPoint = minPoint;
this.minPointExtension = minPointExtension;
this.maxPoint = maxPoint;
this.maxPointExtension = maxPointExtension;
this.copyPoint = copyPoint;
}
public AreaBlock withSelector(VariantSelector selector) {
this.selector = selector;
return this;
}
@Override
public @NonNull Point getMinPoint(boolean extension) {
return extension ? minPointExtension : minPoint;
}
@Override
public @NonNull Point getMaxPoint(boolean extension) {
return extension ? maxPointExtension : maxPoint;
}
@Override
public @NonNull Point getCopyPoint() {
return copyPoint;
}
@Override
public void place(Location location, PasteBuilder pasteBuilder, boolean extension) {
EditSession editSession = pasteBuilder
.with(PasteBuilder.ClipboardProvider.file(selector.select().orElse(null)))
.pastePoint(copyPoint)
.run();
region.getHistory()
.remember(editSession);
}
}

View File

@@ -1,73 +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.bausystem.region.dynamic.modes;
import de.steamwar.bausystem.region.Point;
import de.steamwar.bausystem.region.Region;
import de.steamwar.bausystem.region.dynamic.PasteUtils;
import de.steamwar.bausystem.region.dynamic.Tile;
import de.steamwar.bausystem.region.dynamic.VariantSelector;
import de.steamwar.bausystem.utils.PasteBuilder;
import lombok.Getter;
import lombok.NonNull;
import org.bukkit.Location;
import java.io.File;
public class AreaTile implements Region.Area {
@Getter
private final Tile tile;
private final Point minPoint;
private final Point maxPoint;
private final Point copyPoint;
private final VariantSelector selector;
public AreaTile(Tile tile, int tileX, int tileZ, VariantSelector selector) {
this.tile = tile;
minPoint = Point.fromLocation(tile.getMinLocation()).setY(WORLD_MIN_Y);
maxPoint = Point.fromLocation(tile.add(tileX - 1, tileZ - 1).orElseThrow().getMaxLocation()).setY(WORLD_MAX_Y);
int x = minPoint.getX() + (maxPoint.getX() - minPoint.getX()) / 2;
int z = minPoint.getZ() + (maxPoint.getZ() - minPoint.getZ()) / 2;
copyPoint = new Point(x, 0, z);
this.selector = selector;
}
@Override
public @NonNull Point getMinPoint(boolean extension) {
return minPoint;
}
@Override
public @NonNull Point getMaxPoint(boolean extension) {
return maxPoint;
}
@Override
public @NonNull Point getCopyPoint() {
return copyPoint;
}
@Override
public void place(Location location, PasteBuilder pasteBuilder, boolean extension) {
File resetFile = selector.select().orElse(null);
if (resetFile != null) PasteUtils.paste(resetFile, minPoint, 0);
}
}

View File

@@ -1,54 +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.bausystem.region.dynamic.modes;
import de.steamwar.bausystem.region.RegionData;
import de.steamwar.bausystem.region.RegionFlagPolicy;
import de.steamwar.bausystem.region.dynamic.DynamicRegion;
import de.steamwar.bausystem.region.flags.Flag;
import de.steamwar.bausystem.region.flags.TNTMode;
import de.steamwar.core.Core;
import lombok.NonNull;
public class DisplayRegionData extends RegionData {
public DisplayRegionData(DynamicRegion region) {
super(region);
}
@Override
protected void initialize() {
flagMap.put(Flag.TNT, TNTMode.DENY);
}
@Override
public @NonNull <T extends Enum<T> & Flag.Value<T>> RegionFlagPolicy has(@NonNull Flag<T> flag) {
if (flag.oneOf(Flag.FIRE, Flag.FREEZE, Flag.NO_GRAVITY, Flag.WATER_DESTROY)) {
return RegionFlagPolicy.WRITABLE;
}
if (flag.oneOf(Flag.ITEMS) && Core.getVersion() >= 20) {
return RegionFlagPolicy.WRITABLE;
}
if (flag.oneOf(Flag.TNT, Flag.PROTECT)) {
return RegionFlagPolicy.READ_ONLY;
}
return RegionFlagPolicy.NOT_APPLICABLE;
}
}

View File

@@ -1,158 +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.bausystem.region.dynamic.modes;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.extent.clipboard.io.BuiltInClipboardFormat;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter;
import de.steamwar.bausystem.region.RegionBackups;
import de.steamwar.bausystem.region.RegionData;
import de.steamwar.bausystem.region.dynamic.DynamicRegion;
import de.steamwar.bausystem.region.dynamic.DynamicRegionRepository;
import de.steamwar.bausystem.region.dynamic.PasteUtils;
import lombok.NonNull;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.logging.Level;
public class PlotRegionBackups implements RegionBackups {
private final DynamicRegion region;
private final Function<Backup, RegionData> regionDataConstructor;
private final Map<BackupType, List<Backup>> backups = new EnumMap<>(BackupType.class);
/**
* @param region
* @param regionDataConstructor construct the regionData copying the values from the region into the returned value.
*/
public PlotRegionBackups(DynamicRegion region, Function<Backup, RegionData> regionDataConstructor) {
this.region = region;
this.regionDataConstructor = regionDataConstructor;
// Load all Backups of the Region
for (BackupType backupType : BackupType.values()) {
File backupsTypeDirectory = DynamicRegionRepository.getBackupsTypeDirectory(region, backupType);
if (!backupsTypeDirectory.exists()) continue;
File[] backupsDirectory = backupsTypeDirectory.listFiles();
if (backupsDirectory == null) continue;
List<Backup> backupList = backups.computeIfAbsent(backupType, __ -> new ArrayList<>());
for (File backupDirectory : backupsDirectory) {
backupList.add(new BackupImpl(backupType, region, backupDirectory));
}
}
}
@Override
public Optional<Backup> create(BackupType backupType) {
List<Backup> backupList = backups.computeIfAbsent(backupType, __ -> new ArrayList<>());
// Cleanup backups if there are too many!
backupList.sort(Backup::compareTo);
while (backupList.size() >= backupType.maxBackups) {
backupList.removeFirst().delete(null);
}
// Create backup and save!
Backup backup = new BackupImpl(backupType, region);
backupList.add(backup);
return Optional.of(backup);
}
@Override
public @NonNull List<Backup> list() {
return backups.values()
.stream()
.flatMap(List::stream)
.toList();
}
@Override
public @Nullable Backup get(@NonNull String name) {
return backups.values()
.stream()
.flatMap(List::stream)
.filter(backup -> backup.getName().equals(name))
.findFirst()
.orElse(null);
}
private final class BackupImpl extends Backup {
private static final String SCHEM_FILE = "backup.schem";
private final DynamicRegion region;
public BackupImpl(@NonNull BackupType type, DynamicRegion region) {
super(type, LocalDateTime.now().format(FORMATTER), regionDataConstructor);
region.getRegionData().copyInto(regionData);
this.region = region;
File backupDirectory = DynamicRegionRepository.getBackupDirectory(region, this);
backupDirectory.mkdirs();
DynamicRegionRepository.saveBackup(region, this);
Clipboard clipboard = region.getArea().copy(false);
try (ClipboardWriter writer = BuiltInClipboardFormat.SPONGE_SCHEMATIC.getWriter(new FileOutputStream(new File(backupDirectory, SCHEM_FILE)))) {
writer.write(clipboard);
} catch (IOException e) {
Bukkit.getLogger().log(Level.SEVERE, e.getMessage(), e);
}
}
public BackupImpl(@NonNull BackupType type, DynamicRegion region, File backupDirectory) {
super(type, backupDirectory.getName(), regionDataConstructor);
this.region = region;
DynamicRegionRepository.loadRegionData(region, this, regionData);
}
@Override
public boolean load() {
File file = new File(DynamicRegionRepository.getBackupDirectory(region, this), SCHEM_FILE);
if (!file.exists()) return false;
EditSession editSession = PasteUtils.paste(file, region.getArea().getMinPoint(false), 0);
if (editSession == null) return false;
region.getHistory().remember(editSession);
regionData.copyInto(region.getRegionData());
return true;
}
@Override
public long getCreationTime() {
return DynamicRegionRepository.getBackupDirectory(region, this).lastModified();
}
@Override
public void delete(Location location) {
backups.getOrDefault(type, Collections.emptyList())
.remove(this);
DynamicRegionRepository.deleteBackup(region, this);
}
}
}

View File

@@ -1,53 +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.bausystem.region.dynamic.modes;
import de.steamwar.bausystem.region.RegionData;
import de.steamwar.bausystem.region.RegionDataStore;
import de.steamwar.bausystem.region.RegionFlagPolicy;
import de.steamwar.bausystem.region.flags.Flag;
import de.steamwar.bausystem.region.flags.TNTMode;
import de.steamwar.bausystem.region.flags.TestblockMode;
import de.steamwar.core.Core;
import lombok.NonNull;
public class PlotRegionData extends RegionData {
public PlotRegionData(RegionDataStore store) {
super(store);
}
@Override
protected void initialize() {
flagMap.put(Flag.TNT, TNTMode.ALLOW);
flagMap.put(Flag.TESTBLOCK, TestblockMode.NO_VALUE);
}
@Override
public @NonNull <T extends Enum<T> & Flag.Value<T>> RegionFlagPolicy has(@NonNull Flag<T> flag) {
if (flag.oneOf(Flag.TNT, Flag.PROTECT, Flag.FIRE, Flag.FREEZE, Flag.NO_GRAVITY, Flag.WATER_DESTROY, Flag.TESTBLOCK, Flag.CHANGED)) {
return RegionFlagPolicy.WRITABLE;
}
if (flag.oneOf(Flag.ITEMS) && Core.getVersion() >= 20) {
return RegionFlagPolicy.WRITABLE;
}
return RegionFlagPolicy.NOT_APPLICABLE;
}
}

View File

@@ -1,122 +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.bausystem.region.dynamic.modes.microwargear_7;
import com.google.gson.JsonArray;
import com.google.gson.stream.JsonWriter;
import de.steamwar.bausystem.region.RegionBackups;
import de.steamwar.bausystem.region.RegionData;
import de.steamwar.bausystem.region.RegionHistory;
import de.steamwar.bausystem.region.RegionType;
import de.steamwar.bausystem.region.dynamic.*;
import de.steamwar.bausystem.region.dynamic.modes.AreaTile;
import de.steamwar.bausystem.region.dynamic.modes.DisplayRegionData;
import de.steamwar.sql.GameModeConfig;
import lombok.NonNull;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
@RegionConstructorData(
identifier = "microwargear_display_7",
name = "MicroWarGearDisplay",
material = Material.STONE_BUTTON,
widthX = MiWG7DisplayRegion.TILE_X * Tile.tileSize,
widthZ = MiWG7DisplayRegion.TILE_Z * Tile.tileSize
)
public class MiWG7DisplayRegion extends DynamicRegion {
protected static final int TILE_X = 1;
protected static final int TILE_Z = 1;
private static final File DIRECTORY = new File(Bukkit.getWorlds().get(0).getWorldFolder(), "sections/microwargear_7/display");
private static final VariantSelector SELECTOR = VariantSelector.Get(DIRECTORY);
private final AreaTile area;
public MiWG7DisplayRegion(Tile tile) {
this(UUID.randomUUID(), tile);
finishCreate();
}
public MiWG7DisplayRegion(UUID id, JsonArray tileData) {
this(id, readTile(tileData));
finishLoad();
}
private MiWG7DisplayRegion(UUID id, Tile tile) {
super(id, null);
area = new AreaTile(tile, TILE_X, TILE_Z, SELECTOR);
regionData = new DisplayRegionData(this);
}
@Override
public void writeData(JsonWriter writer) throws IOException {
writeTile(writer, area.getTile());
}
@Override
public @NonNull RegionType getType() {
return RegionType.DRY;
}
@Override
public @NonNull Area getArea() {
return area;
}
@Override
public @NonNull Area getBuildArea() {
return Area.EMPTY;
}
@Override
public @NonNull Area getTestblockArea() {
return Area.EMPTY;
}
@Override
public @NonNull GameModeConfig<Material, String> getGameModeConfig() {
return MiWG7Utils.GAME_MODE_CONFIG;
}
@Override
public @NonNull RegionHistory getHistory() {
return RegionHistory.EMPTY;
}
@Override
public @NonNull RegionBackups getBackups() {
return RegionBackups.EMPTY;
}
@Override
public void save() {
DynamicRegionRepository.saveRegion(this);
}
@Override
public void load(RegionData regionData) {
DynamicRegionRepository.loadRegionData(this, regionData);
}
}

View File

@@ -1,144 +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.bausystem.region.dynamic.modes.microwargear_7;
import com.google.gson.JsonArray;
import com.google.gson.stream.JsonWriter;
import de.steamwar.bausystem.region.*;
import de.steamwar.bausystem.region.dynamic.*;
import de.steamwar.bausystem.region.dynamic.modes.AreaBlock;
import de.steamwar.bausystem.region.dynamic.modes.AreaTile;
import de.steamwar.bausystem.region.dynamic.modes.PlotRegionBackups;
import de.steamwar.bausystem.region.dynamic.modes.PlotRegionData;
import de.steamwar.bausystem.region.flags.Flag;
import de.steamwar.bausystem.shared.Pair;
import de.steamwar.sql.GameModeConfig;
import lombok.NonNull;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
@RegionConstructorData(
identifier = "microwargear_plot_7",
name = "MicroWarGearPlot",
material = Material.STONE_BUTTON,
widthX = Tile.tileSize * MiWG7PlotRegion.TILE_X,
widthZ = Tile.tileSize * MiWG7PlotRegion.TILE_Z
)
public class MiWG7PlotRegion extends DynamicRegion {
protected static final int TILE_X = 3;
protected static final int TILE_Z = 5;
private static final File DIRECTORY = new File(Bukkit.getWorlds().get(0).getWorldFolder(), "sections/microwargear_7/plot");
private static final VariantSelector REGION = VariantSelector.Get(new File(DIRECTORY, "region"));
private static final VariantSelector TESTBLOCK = VariantSelector.Get(new File(DIRECTORY, "testblock"));
private static final VariantSelector WIREFRAME = VariantSelector.Get(new File(DIRECTORY, "wireframe"));
private final AreaTile area;
private final AreaBlock northArea;
private final AreaBlock southArea;
private final RegionHistory history;
private final RegionBackups backups;
public MiWG7PlotRegion(Tile tile) {
this(UUID.randomUUID(), tile);
finishCreate();
}
public MiWG7PlotRegion(UUID id, JsonArray tileData) {
this(id, readTile(tileData));
finishLoad();
}
private MiWG7PlotRegion(UUID id, Tile tile) {
super(id, null);
area = new AreaTile(tile, TILE_X, TILE_Z, REGION);
Pair<AreaBlock, AreaBlock> pair = AreaBlock.create(this, 36, new Point(7, 7, 7), new Point(7, 0, 7), new Point(7, 7, 7), AreaBlock.CopyLocation.CENTER, 50);
northArea = pair.getKey();
southArea = pair.getValue();
regionData = new PlotRegionData(this);
history = new RegionHistory.Impl(10);
backups = new PlotRegionBackups(this, PlotRegionData::new);
}
@Override
public void writeData(JsonWriter writer) throws IOException {
writeTile(writer, area.getTile());
}
@Override
public @NonNull RegionType getType() {
return RegionType.DRY;
}
@Override
public @NonNull Area getArea() {
return area;
}
@Override
public @NonNull Area getBuildArea() {
return switch (regionData.get(Flag.TESTBLOCK).getWithDefault()) {
case NO_VALUE -> Area.EMPTY;
case NORTH -> southArea.withSelector(WIREFRAME);
case SOUTH -> northArea.withSelector(WIREFRAME);
};
}
@Override
public @NonNull Area getTestblockArea() {
return switch (regionData.get(Flag.TESTBLOCK).getWithDefault()) {
case NO_VALUE -> Area.EMPTY;
case NORTH -> northArea.withSelector(TESTBLOCK);
case SOUTH -> southArea.withSelector(TESTBLOCK);
};
}
@Override
public @NonNull GameModeConfig<Material, String> getGameModeConfig() {
return MiWG7Utils.GAME_MODE_CONFIG;
}
@Override
public @NonNull RegionHistory getHistory() {
return history;
}
@Override
public @NonNull RegionBackups getBackups() {
return backups;
}
@Override
public void save() {
DynamicRegionRepository.saveRegion(this);
}
@Override
public void load(RegionData regionData) {
DynamicRegionRepository.loadRegionData(this, regionData);
}
}

View File

@@ -1,35 +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.bausystem.region.dynamic.modes.microwargear_7;
import de.steamwar.sql.GameModeConfig;
import lombok.experimental.UtilityClass;
import org.bukkit.Material;
@UtilityClass
public class MiWG7Utils {
public static final GameModeConfig<Material, String> GAME_MODE_CONFIG;
static {
GameModeConfig<Material, String> config = GameModeConfig.getByGameName("MicroWarGear");
GAME_MODE_CONFIG = config != null ? config : GameModeConfig.getDefaults();
}
}

View File

@@ -1,122 +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.bausystem.region.dynamic.modes.miniwargear;
import com.google.gson.JsonArray;
import com.google.gson.stream.JsonWriter;
import de.steamwar.bausystem.region.RegionBackups;
import de.steamwar.bausystem.region.RegionData;
import de.steamwar.bausystem.region.RegionHistory;
import de.steamwar.bausystem.region.RegionType;
import de.steamwar.bausystem.region.dynamic.*;
import de.steamwar.bausystem.region.dynamic.modes.AreaTile;
import de.steamwar.bausystem.region.dynamic.modes.DisplayRegionData;
import de.steamwar.sql.GameModeConfig;
import lombok.NonNull;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
@RegionConstructorData(
identifier = "miniwargear_display",
name = "MiniWarGearDisplay",
material = Material.END_STONE_BRICK_SLAB,
widthX = MWGDisplayRegion.TILE_X * Tile.tileSize,
widthZ = MWGDisplayRegion.TILE_Z * Tile.tileSize
)
public class MWGDisplayRegion extends DynamicRegion {
protected static final int TILE_X = 3;
protected static final int TILE_Z = 3;
private static final File DIRECTORY = new File(Bukkit.getWorlds().get(0).getWorldFolder(), "sections/miniwargear/display");
private static final VariantSelector SELECTOR = VariantSelector.Get(DIRECTORY);
private final AreaTile area;
public MWGDisplayRegion(Tile tile) {
this(UUID.randomUUID(), tile);
finishCreate();
}
public MWGDisplayRegion(UUID id, JsonArray tileData) {
this(id, readTile(tileData));
finishLoad();
}
private MWGDisplayRegion(UUID id, Tile tile) {
super(id, null);
area = new AreaTile(tile, TILE_X, TILE_Z, SELECTOR);
regionData = new DisplayRegionData(this);
}
@Override
public void writeData(JsonWriter writer) throws IOException {
writeTile(writer, area.getTile());
}
@Override
public @NonNull RegionType getType() {
return RegionType.DRY;
}
@Override
public @NonNull Area getArea() {
return area;
}
@Override
public @NonNull Area getBuildArea() {
return Area.EMPTY;
}
@Override
public @NonNull Area getTestblockArea() {
return Area.EMPTY;
}
@Override
public @NonNull GameModeConfig<Material, String> getGameModeConfig() {
return MWGUtils.GAME_MODE_CONFIG;
}
@Override
public @NonNull RegionHistory getHistory() {
return RegionHistory.EMPTY;
}
@Override
public @NonNull RegionBackups getBackups() {
return RegionBackups.EMPTY;
}
@Override
public void save() {
DynamicRegionRepository.saveRegion(this);
}
@Override
public void load(RegionData regionData) {
DynamicRegionRepository.loadRegionData(this, regionData);
}
}

View File

@@ -1,144 +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.bausystem.region.dynamic.modes.miniwargear;
import com.google.gson.JsonArray;
import com.google.gson.stream.JsonWriter;
import de.steamwar.bausystem.region.*;
import de.steamwar.bausystem.region.dynamic.*;
import de.steamwar.bausystem.region.dynamic.modes.AreaBlock;
import de.steamwar.bausystem.region.dynamic.modes.AreaTile;
import de.steamwar.bausystem.region.dynamic.modes.PlotRegionBackups;
import de.steamwar.bausystem.region.dynamic.modes.PlotRegionData;
import de.steamwar.bausystem.region.flags.Flag;
import de.steamwar.bausystem.shared.Pair;
import de.steamwar.sql.GameModeConfig;
import lombok.NonNull;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
@RegionConstructorData(
identifier = "miniwargear_plot",
name = "MiniWarGearPlot",
material = Material.END_STONE_BRICK_SLAB,
widthX = Tile.tileSize * MWGPlotRegion.TILE_X,
widthZ = Tile.tileSize * MWGPlotRegion.TILE_Z
)
public class MWGPlotRegion extends DynamicRegion {
protected static final int TILE_X = 3;
protected static final int TILE_Z = 6;
private static final File DIRECTORY = new File(Bukkit.getWorlds().get(0).getWorldFolder(), "sections/miniwargear/plot");
private static final VariantSelector REGION = VariantSelector.Get(new File(DIRECTORY, "region"));
private static final VariantSelector TESTBLOCK = VariantSelector.Get(new File(DIRECTORY, "testblock"));
private static final VariantSelector WIREFRAME = VariantSelector.Get(new File(DIRECTORY, "wireframe"));
private final AreaTile area;
private final AreaBlock northArea;
private final AreaBlock southArea;
private final RegionHistory history;
private final RegionBackups backups;
public MWGPlotRegion(Tile tile) {
this(UUID.randomUUID(), tile);
finishCreate();
}
public MWGPlotRegion(UUID id, JsonArray tileData) {
this(id, readTile(tileData));
finishLoad();
}
private MWGPlotRegion(UUID id, Tile tile) {
super(id, null);
area = new AreaTile(tile, TILE_X, TILE_Z, REGION);
Pair<AreaBlock, AreaBlock> pair = AreaBlock.create(this, 36, new Point(37, 26, 22), new Point(7, 0, 7), new Point(7, 7, 7), AreaBlock.CopyLocation.CENTER, 50);
northArea = pair.getKey();
southArea = pair.getValue();
regionData = new PlotRegionData(this);
history = new RegionHistory.Impl(10);
backups = new PlotRegionBackups(this, PlotRegionData::new);
}
@Override
public void writeData(JsonWriter writer) throws IOException {
writeTile(writer, area.getTile());
}
@Override
public @NonNull RegionType getType() {
return RegionType.DRY;
}
@Override
public @NonNull Area getArea() {
return area;
}
@Override
public @NonNull Area getBuildArea() {
return switch (regionData.get(Flag.TESTBLOCK).getWithDefault()) {
case NO_VALUE -> Area.EMPTY;
case NORTH -> southArea.withSelector(WIREFRAME);
case SOUTH -> northArea.withSelector(WIREFRAME);
};
}
@Override
public @NonNull Area getTestblockArea() {
return switch (regionData.get(Flag.TESTBLOCK).getWithDefault()) {
case NO_VALUE -> Area.EMPTY;
case NORTH -> northArea.withSelector(TESTBLOCK);
case SOUTH -> southArea.withSelector(TESTBLOCK);
};
}
@Override
public @NonNull GameModeConfig<Material, String> getGameModeConfig() {
return MWGUtils.GAME_MODE_CONFIG;
}
@Override
public @NonNull RegionHistory getHistory() {
return history;
}
@Override
public @NonNull RegionBackups getBackups() {
return backups;
}
@Override
public void save() {
DynamicRegionRepository.saveRegion(this);
}
@Override
public void load(RegionData regionData) {
DynamicRegionRepository.loadRegionData(this, regionData);
}
}

View File

@@ -1,35 +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.bausystem.region.dynamic.modes.miniwargear;
import de.steamwar.sql.GameModeConfig;
import lombok.experimental.UtilityClass;
import org.bukkit.Material;
@UtilityClass
public class MWGUtils {
public static final GameModeConfig<Material, String> GAME_MODE_CONFIG;
static {
GameModeConfig<Material, String> config = GameModeConfig.getByGameName("MiniWarGear");
GAME_MODE_CONFIG = config != null ? config : GameModeConfig.getDefaults();
}
}

View File

@@ -1,122 +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.bausystem.region.dynamic.modes.wargear_45;
import com.google.gson.JsonArray;
import com.google.gson.stream.JsonWriter;
import de.steamwar.bausystem.region.RegionBackups;
import de.steamwar.bausystem.region.RegionData;
import de.steamwar.bausystem.region.RegionHistory;
import de.steamwar.bausystem.region.RegionType;
import de.steamwar.bausystem.region.dynamic.*;
import de.steamwar.bausystem.region.dynamic.modes.AreaTile;
import de.steamwar.bausystem.region.dynamic.modes.DisplayRegionData;
import de.steamwar.sql.GameModeConfig;
import lombok.NonNull;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
@RegionConstructorData(
identifier = "wargear_display_45",
name = "WarGearDisplay 45",
material = Material.END_STONE_BRICKS,
widthX = WG45DisplayRegion.TILE_X * Tile.tileSize,
widthZ = WG45DisplayRegion.TILE_Z * Tile.tileSize
)
public class WG45DisplayRegion extends DynamicRegion {
protected static final int TILE_X = 5;
protected static final int TILE_Z = 5;
private static final File DIRECTORY = new File(Bukkit.getWorlds().get(0).getWorldFolder(), "sections/wargear_45/display");
private static final VariantSelector SELECTOR = VariantSelector.Get(DIRECTORY);
private final AreaTile area;
public WG45DisplayRegion(Tile tile) {
this(UUID.randomUUID(), tile);
finishCreate();
}
public WG45DisplayRegion(UUID id, JsonArray tileData) {
this(id, readTile(tileData));
finishLoad();
}
private WG45DisplayRegion(UUID id, Tile tile) {
super(id, null);
area = new AreaTile(tile, TILE_X, TILE_Z, SELECTOR);
regionData = new DisplayRegionData(this);
}
@Override
public void writeData(JsonWriter writer) throws IOException {
writeTile(writer, area.getTile());
}
@Override
public @NonNull RegionType getType() {
return RegionType.DRY;
}
@Override
public @NonNull Area getArea() {
return area;
}
@Override
public @NonNull Area getBuildArea() {
return Area.EMPTY;
}
@Override
public @NonNull Area getTestblockArea() {
return Area.EMPTY;
}
@Override
public @NonNull GameModeConfig<Material, String> getGameModeConfig() {
return WG45Utils.GAME_MODE_CONFIG;
}
@Override
public @NonNull RegionHistory getHistory() {
return RegionHistory.EMPTY;
}
@Override
public @NonNull RegionBackups getBackups() {
return RegionBackups.EMPTY;
}
@Override
public void save() {
DynamicRegionRepository.saveRegion(this);
}
@Override
public void load(RegionData regionData) {
DynamicRegionRepository.loadRegionData(this, regionData);
}
}

View File

@@ -1,144 +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.bausystem.region.dynamic.modes.wargear_45;
import com.google.gson.JsonArray;
import com.google.gson.stream.JsonWriter;
import de.steamwar.bausystem.region.*;
import de.steamwar.bausystem.region.dynamic.*;
import de.steamwar.bausystem.region.dynamic.modes.AreaBlock;
import de.steamwar.bausystem.region.dynamic.modes.AreaTile;
import de.steamwar.bausystem.region.dynamic.modes.PlotRegionBackups;
import de.steamwar.bausystem.region.dynamic.modes.PlotRegionData;
import de.steamwar.bausystem.region.flags.Flag;
import de.steamwar.bausystem.shared.Pair;
import de.steamwar.sql.GameModeConfig;
import lombok.NonNull;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
@RegionConstructorData(
identifier = "wargear_plot_45",
name = "WarGearPlot 45",
material = Material.END_STONE_BRICKS,
widthX = Tile.tileSize * WG45PlotRegion.TILE_X,
widthZ = Tile.tileSize * WG45PlotRegion.TILE_Z
)
public class WG45PlotRegion extends DynamicRegion {
protected static final int TILE_X = 7;
protected static final int TILE_Z = 10;
private static final File DIRECTORY = new File(Bukkit.getWorlds().get(0).getWorldFolder(), "sections/wargear_45/plot");
private static final VariantSelector REGION = VariantSelector.Get(new File(DIRECTORY, "region"));
private static final VariantSelector TESTBLOCK = VariantSelector.Get(new File(DIRECTORY, "testblock"));
private static final VariantSelector WIREFRAME = VariantSelector.Get(new File(DIRECTORY, "wireframe"));
private final AreaTile area;
private final AreaBlock northArea;
private final AreaBlock southArea;
private final RegionHistory history;
private final RegionBackups backups;
public WG45PlotRegion(Tile tile) {
this(UUID.randomUUID(), tile);
finishCreate();
}
public WG45PlotRegion(UUID id, JsonArray tileData) {
this(id, readTile(tileData));
finishLoad();
}
private WG45PlotRegion(UUID id, Tile tile) {
super(id, null);
area = new AreaTile(tile, TILE_X, TILE_Z, REGION);
Pair<AreaBlock, AreaBlock> pair = AreaBlock.create(this, 36, new Point(67, 41, 47), new Point(16, 0, 16), new Point(16, 16, 16), AreaBlock.CopyLocation.CENTER, 50);
northArea = pair.getKey();
southArea = pair.getValue();
regionData = new PlotRegionData(this);
history = new RegionHistory.Impl(10);
backups = new PlotRegionBackups(this, PlotRegionData::new);
}
@Override
public void writeData(JsonWriter writer) throws IOException {
writeTile(writer, area.getTile());
}
@Override
public @NonNull RegionType getType() {
return RegionType.DRY;
}
@Override
public @NonNull Area getArea() {
return area;
}
@Override
public @NonNull Area getBuildArea() {
return switch (regionData.get(Flag.TESTBLOCK).getWithDefault()) {
case NO_VALUE -> Area.EMPTY;
case NORTH -> southArea.withSelector(WIREFRAME);
case SOUTH -> northArea.withSelector(WIREFRAME);
};
}
@Override
public @NonNull Area getTestblockArea() {
return switch (regionData.get(Flag.TESTBLOCK).getWithDefault()) {
case NO_VALUE -> Area.EMPTY;
case NORTH -> northArea.withSelector(TESTBLOCK);
case SOUTH -> southArea.withSelector(TESTBLOCK);
};
}
@Override
public @NonNull GameModeConfig<Material, String> getGameModeConfig() {
return WG45Utils.GAME_MODE_CONFIG;
}
@Override
public @NonNull RegionHistory getHistory() {
return history;
}
@Override
public @NonNull RegionBackups getBackups() {
return backups;
}
@Override
public void save() {
DynamicRegionRepository.saveRegion(this);
}
@Override
public void load(RegionData regionData) {
DynamicRegionRepository.loadRegionData(this, regionData);
}
}

View File

@@ -1,35 +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.bausystem.region.dynamic.modes.wargear_45;
import de.steamwar.sql.GameModeConfig;
import lombok.experimental.UtilityClass;
import org.bukkit.Material;
@UtilityClass
public class WG45Utils {
public static final GameModeConfig<Material, String> GAME_MODE_CONFIG;
static {
GameModeConfig<Material, String> config = GameModeConfig.getByGameName("WarGear");
GAME_MODE_CONFIG = config != null ? config : GameModeConfig.getDefaults();
}
}

View File

@@ -1,122 +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.bausystem.region.dynamic.modes.warship_175;
import com.google.gson.JsonArray;
import com.google.gson.stream.JsonWriter;
import de.steamwar.bausystem.region.RegionBackups;
import de.steamwar.bausystem.region.RegionData;
import de.steamwar.bausystem.region.RegionHistory;
import de.steamwar.bausystem.region.RegionType;
import de.steamwar.bausystem.region.dynamic.*;
import de.steamwar.bausystem.region.dynamic.modes.AreaTile;
import de.steamwar.bausystem.region.dynamic.modes.DisplayRegionData;
import de.steamwar.sql.GameModeConfig;
import lombok.NonNull;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
@RegionConstructorData(
identifier = "warship_display_175",
name = "WarShipDisplay 175",
material = Material.BIRCH_BOAT,
widthX = WS175DisplayRegion.TILE_X * Tile.tileSize,
widthZ = WS175DisplayRegion.TILE_Z * Tile.tileSize
)
public class WS175DisplayRegion extends DynamicRegion {
protected static final int TILE_X = 11;
protected static final int TILE_Z = 3;
private static final File DIRECTORY = new File(Bukkit.getWorlds().get(0).getWorldFolder(), "sections/warship_175/display");
private static final VariantSelector SELECTOR = VariantSelector.Get(DIRECTORY);
private final AreaTile area;
public WS175DisplayRegion(Tile tile) {
this(UUID.randomUUID(), tile);
finishCreate();
}
public WS175DisplayRegion(UUID id, JsonArray tileData) {
this(id, readTile(tileData));
finishLoad();
}
private WS175DisplayRegion(UUID id, Tile tile) {
super(id, null);
area = new AreaTile(tile, TILE_X, TILE_Z, SELECTOR);
regionData = new DisplayRegionData(this);
}
@Override
public void writeData(JsonWriter writer) throws IOException {
writeTile(writer, area.getTile());
}
@Override
public @NonNull RegionType getType() {
return RegionType.WET;
}
@Override
public @NonNull Area getArea() {
return area;
}
@Override
public @NonNull Area getBuildArea() {
return Area.EMPTY;
}
@Override
public @NonNull Area getTestblockArea() {
return Area.EMPTY;
}
@Override
public @NonNull GameModeConfig<Material, String> getGameModeConfig() {
return WS175Utils.GAME_MODE_CONFIG;
}
@Override
public @NonNull RegionHistory getHistory() {
return RegionHistory.EMPTY;
}
@Override
public @NonNull RegionBackups getBackups() {
return RegionBackups.EMPTY;
}
@Override
public void save() {
DynamicRegionRepository.saveRegion(this);
}
@Override
public void load(RegionData regionData) {
DynamicRegionRepository.loadRegionData(this, regionData);
}
}

View File

@@ -1,144 +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.bausystem.region.dynamic.modes.warship_175;
import com.google.gson.JsonArray;
import com.google.gson.stream.JsonWriter;
import de.steamwar.bausystem.region.*;
import de.steamwar.bausystem.region.dynamic.*;
import de.steamwar.bausystem.region.dynamic.modes.AreaBlock;
import de.steamwar.bausystem.region.dynamic.modes.AreaTile;
import de.steamwar.bausystem.region.dynamic.modes.PlotRegionBackups;
import de.steamwar.bausystem.region.dynamic.modes.PlotRegionData;
import de.steamwar.bausystem.region.flags.Flag;
import de.steamwar.bausystem.shared.Pair;
import de.steamwar.sql.GameModeConfig;
import lombok.NonNull;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
@RegionConstructorData(
identifier = "warship_plot_175",
name = "WarShipPlot 175",
material = Material.BIRCH_BOAT,
widthX = Tile.tileSize * WS175PlotRegion.TILE_X,
widthZ = Tile.tileSize * WS175PlotRegion.TILE_Z
)
public class WS175PlotRegion extends DynamicRegion {
protected static final int TILE_X = 11;
protected static final int TILE_Z = 10;
private static final File DIRECTORY = new File(Bukkit.getWorlds().get(0).getWorldFolder(), "sections/warship_175/plot");
private static final VariantSelector REGION = VariantSelector.Get(new File(DIRECTORY, "region"));
private static final VariantSelector TESTBLOCK = VariantSelector.Get(new File(DIRECTORY, "testblock"));
private static final VariantSelector WIREFRAME = VariantSelector.Get(new File(DIRECTORY, "wireframe"));
private final AreaTile area;
private final AreaBlock northArea;
private final AreaBlock southArea;
private final RegionHistory history;
private final RegionBackups backups;
public WS175PlotRegion(Tile tile) {
this(UUID.randomUUID(), tile);
finishCreate();
}
public WS175PlotRegion(UUID id, JsonArray tileData) {
this(id, readTile(tileData));
finishLoad();
}
private WS175PlotRegion(UUID id, Tile tile) {
super(id, null);
area = new AreaTile(tile, TILE_X, TILE_Z, REGION);
Pair<AreaBlock, AreaBlock> pair = AreaBlock.create(this, 25, new Point(175, 58, 39), new Point(12, 0, 8), new Point(12, 0, 8), AreaBlock.CopyLocation.SIDE, 132);
northArea = pair.getKey();
southArea = pair.getValue();
regionData = new PlotRegionData(this);
history = new RegionHistory.Impl(10);
backups = new PlotRegionBackups(this, PlotRegionData::new);
}
@Override
public void writeData(JsonWriter writer) throws IOException {
writeTile(writer, area.getTile());
}
@Override
public @NonNull RegionType getType() {
return RegionType.WET;
}
@Override
public @NonNull Area getArea() {
return area;
}
@Override
public @NonNull Area getBuildArea() {
return switch (regionData.get(Flag.TESTBLOCK).getWithDefault()) {
case NO_VALUE -> Area.EMPTY;
case NORTH -> southArea.withSelector(WIREFRAME);
case SOUTH -> northArea.withSelector(WIREFRAME);
};
}
@Override
public @NonNull Area getTestblockArea() {
return switch (regionData.get(Flag.TESTBLOCK).getWithDefault()) {
case NO_VALUE -> Area.EMPTY;
case NORTH -> northArea.withSelector(TESTBLOCK);
case SOUTH -> southArea.withSelector(TESTBLOCK);
};
}
@Override
public @NonNull GameModeConfig<Material, String> getGameModeConfig() {
return WS175Utils.GAME_MODE_CONFIG;
}
@Override
public @NonNull RegionHistory getHistory() {
return history;
}
@Override
public @NonNull RegionBackups getBackups() {
return backups;
}
@Override
public void save() {
DynamicRegionRepository.saveRegion(this);
}
@Override
public void load(RegionData regionData) {
DynamicRegionRepository.loadRegionData(this, regionData);
}
}

View File

@@ -1,35 +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.bausystem.region.dynamic.modes.warship_175;
import de.steamwar.sql.GameModeConfig;
import lombok.experimental.UtilityClass;
import org.bukkit.Material;
@UtilityClass
public class WS175Utils {
public static final GameModeConfig<Material, String> GAME_MODE_CONFIG;
static {
GameModeConfig<Material, String> config = GameModeConfig.getByGameName("WarShip21");
GAME_MODE_CONFIG = config != null ? config : GameModeConfig.getDefaults();
}
}

View File

@@ -1,122 +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.bausystem.region.dynamic.modes.warship_230;
import com.google.gson.JsonArray;
import com.google.gson.stream.JsonWriter;
import de.steamwar.bausystem.region.RegionBackups;
import de.steamwar.bausystem.region.RegionData;
import de.steamwar.bausystem.region.RegionHistory;
import de.steamwar.bausystem.region.RegionType;
import de.steamwar.bausystem.region.dynamic.*;
import de.steamwar.bausystem.region.dynamic.modes.AreaTile;
import de.steamwar.bausystem.region.dynamic.modes.DisplayRegionData;
import de.steamwar.sql.GameModeConfig;
import lombok.NonNull;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
@RegionConstructorData(
identifier = "warship_display_230",
name = "WarShipDisplay 230",
material = Material.BIRCH_BOAT,
widthX = WS230DisplayRegion.TILE_X * Tile.tileSize,
widthZ = WS230DisplayRegion.TILE_Z * Tile.tileSize
)
public class WS230DisplayRegion extends DynamicRegion {
protected static final int TILE_X = 13;
protected static final int TILE_Z = 3;
private static final File DIRECTORY = new File(Bukkit.getWorlds().get(0).getWorldFolder(), "sections/warship_230/display");
private static final VariantSelector SELECTOR = VariantSelector.Get(DIRECTORY);
private final AreaTile area;
public WS230DisplayRegion(Tile tile) {
this(UUID.randomUUID(), tile);
finishCreate();
}
public WS230DisplayRegion(UUID id, JsonArray tileData) {
this(id, readTile(tileData));
finishLoad();
}
private WS230DisplayRegion(UUID id, Tile tile) {
super(id, null);
area = new AreaTile(tile, TILE_X, TILE_Z, SELECTOR);
regionData = new DisplayRegionData(this);
}
@Override
public void writeData(JsonWriter writer) throws IOException {
writeTile(writer, area.getTile());
}
@Override
public @NonNull RegionType getType() {
return RegionType.WET;
}
@Override
public @NonNull Area getArea() {
return area;
}
@Override
public @NonNull Area getBuildArea() {
return Area.EMPTY;
}
@Override
public @NonNull Area getTestblockArea() {
return Area.EMPTY;
}
@Override
public @NonNull GameModeConfig<Material, String> getGameModeConfig() {
return WS230Utils.GAME_MODE_CONFIG;
}
@Override
public @NonNull RegionHistory getHistory() {
return RegionHistory.EMPTY;
}
@Override
public @NonNull RegionBackups getBackups() {
return RegionBackups.EMPTY;
}
@Override
public void save() {
DynamicRegionRepository.saveRegion(this);
}
@Override
public void load(RegionData regionData) {
DynamicRegionRepository.loadRegionData(this, regionData);
}
}

View File

@@ -1,144 +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.bausystem.region.dynamic.modes.warship_230;
import com.google.gson.JsonArray;
import com.google.gson.stream.JsonWriter;
import de.steamwar.bausystem.region.*;
import de.steamwar.bausystem.region.dynamic.*;
import de.steamwar.bausystem.region.dynamic.modes.AreaBlock;
import de.steamwar.bausystem.region.dynamic.modes.AreaTile;
import de.steamwar.bausystem.region.dynamic.modes.PlotRegionBackups;
import de.steamwar.bausystem.region.dynamic.modes.PlotRegionData;
import de.steamwar.bausystem.region.flags.Flag;
import de.steamwar.bausystem.shared.Pair;
import de.steamwar.sql.GameModeConfig;
import lombok.NonNull;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
@RegionConstructorData(
identifier = "warship_plot_230",
name = "WarShipPlot 230",
material = Material.BIRCH_BOAT,
widthX = Tile.tileSize * WS230PlotRegion.TILE_X,
widthZ = Tile.tileSize * WS230PlotRegion.TILE_Z
)
public class WS230PlotRegion extends DynamicRegion {
protected static final int TILE_X = 13;
protected static final int TILE_Z = 10;
private static final File DIRECTORY = new File(Bukkit.getWorlds().get(0).getWorldFolder(), "sections/warship_230/plot");
private static final VariantSelector REGION = VariantSelector.Get(new File(DIRECTORY, "region"));
private static final VariantSelector TESTBLOCK = VariantSelector.Get(new File(DIRECTORY, "testblock"));
private static final VariantSelector WIREFRAME = VariantSelector.Get(new File(DIRECTORY, "wireframe"));
private final AreaTile area;
private final AreaBlock northArea;
private final AreaBlock southArea;
private final RegionHistory history;
private final RegionBackups backups;
public WS230PlotRegion(Tile tile) {
this(UUID.randomUUID(), tile);
finishCreate();
}
public WS230PlotRegion(UUID id, JsonArray tileData) {
this(id, readTile(tileData));
finishLoad();
}
private WS230PlotRegion(UUID id, Tile tile) {
super(id, null);
area = new AreaTile(tile, TILE_X, TILE_Z, REGION);
Pair<AreaBlock, AreaBlock> pair = AreaBlock.create(this, 25, new Point(230, 58, 43), new Point(12, 0, 8), new Point(12, 0, 8), AreaBlock.CopyLocation.SIDE, 136);
northArea = pair.getKey();
southArea = pair.getValue();
regionData = new PlotRegionData(this);
history = new RegionHistory.Impl(10);
backups = new PlotRegionBackups(this, PlotRegionData::new);
}
@Override
public void writeData(JsonWriter writer) throws IOException {
writeTile(writer, area.getTile());
}
@Override
public @NonNull RegionType getType() {
return RegionType.WET;
}
@Override
public @NonNull Area getArea() {
return area;
}
@Override
public @NonNull Area getBuildArea() {
return switch (regionData.get(Flag.TESTBLOCK).getWithDefault()) {
case NO_VALUE -> Area.EMPTY;
case NORTH -> southArea.withSelector(WIREFRAME);
case SOUTH -> northArea.withSelector(WIREFRAME);
};
}
@Override
public @NonNull Area getTestblockArea() {
return switch (regionData.get(Flag.TESTBLOCK).getWithDefault()) {
case NO_VALUE -> Area.EMPTY;
case NORTH -> northArea.withSelector(TESTBLOCK);
case SOUTH -> southArea.withSelector(TESTBLOCK);
};
}
@Override
public @NonNull GameModeConfig<Material, String> getGameModeConfig() {
return WS230Utils.GAME_MODE_CONFIG;
}
@Override
public @NonNull RegionHistory getHistory() {
return history;
}
@Override
public @NonNull RegionBackups getBackups() {
return backups;
}
@Override
public void save() {
DynamicRegionRepository.saveRegion(this);
}
@Override
public void load(RegionData regionData) {
DynamicRegionRepository.loadRegionData(this, regionData);
}
}

View File

@@ -1,35 +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.bausystem.region.dynamic.modes.warship_230;
import de.steamwar.sql.GameModeConfig;
import lombok.experimental.UtilityClass;
import org.bukkit.Material;
@UtilityClass
public class WS230Utils {
public static final GameModeConfig<Material, String> GAME_MODE_CONFIG;
static {
GameModeConfig<Material, String> config = GameModeConfig.getByGameName("WarShip20");
GAME_MODE_CONFIG = config != null ? config : GameModeConfig.getDefaults();
}
}

View File

@@ -1,221 +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.bausystem.region.dynamic.path;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.region.DynamicRegionSystem;
import de.steamwar.bausystem.region.Point;
import de.steamwar.bausystem.region.Region;
import de.steamwar.bausystem.region.RegionType;
import de.steamwar.bausystem.region.dynamic.MultiTileArea;
import de.steamwar.bausystem.region.dynamic.PasteUtils;
import de.steamwar.bausystem.region.dynamic.Tile;
import de.steamwar.bausystem.region.dynamic.VariantSelector;
import de.steamwar.bausystem.shared.Pair;
import de.steamwar.bausystem.utils.PasteBuilder;
import lombok.NonNull;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import java.io.File;
import java.util.List;
import java.util.Optional;
import static de.steamwar.bausystem.region.RegionType.ConnectionType.*;
public class PathArea extends MultiTileArea {
private static final File PATH_DIR = new File(Bukkit.getWorlds().get(0).getWorldFolder(), "sections/path");
private static final File FALLBACK_SCHEM = new File(PATH_DIR, "Fallback.schem");
private static final VariantSelector CENTER_NORMAL = VariantSelector.Get(new File(PATH_DIR, "center/normal"));
private static final VariantSelector SIDE_GLOBAL = VariantSelector.Get(new File(PATH_DIR, "side/global"));
private static final VariantSelector CORNER_INNER_GLOBAL = VariantSelector.Get(new File(PATH_DIR, "cinner/global"));
private static final VariantSelector CORNER_OUTER_GLOBAL = VariantSelector.Get(new File(PATH_DIR, "couter/global"));
private static final VariantSelector SIDE_WATER = VariantSelector.Get(new File(PATH_DIR, "side/water"));
private static final VariantSelector CORNER_INNER_WATER = VariantSelector.Get(new File(PATH_DIR, "cinner/water"));
private static final VariantSelector CORNER_OUTER_WATER = VariantSelector.Get(new File(PATH_DIR, "couter/water"));
private static final VariantSelector SIDE_CLOSED = VariantSelector.Get(new File(PATH_DIR, "side/closed"));
private static final VariantSelector CORNER_INNER_CLOSED = VariantSelector.Get(new File(PATH_DIR, "cinner/closed"));
private static final VariantSelector CORNER_OUTER_CLOSED = VariantSelector.Get(new File(PATH_DIR, "couter/closed"));
private static final VariantSelector GARDEN = VariantSelector.Get(new File(PATH_DIR, "garden"));
private static final VariantSelector SIDE_GARDEN = VariantSelector.Get(new File(PATH_DIR, "side/garden"));
private static final VariantSelector SIDE_GARDEN_CONNECTED = VariantSelector.Get(new File(PATH_DIR, "side/garden_connected"));
private static final VariantSelector CORNER_INNER_GARDEN = VariantSelector.Get(new File(PATH_DIR, "cinner/garden"));
private static final VariantSelector CORNER_OUTER_GARDEN = VariantSelector.Get(new File(PATH_DIR, "couter/garden"));
private static final SelectorSide SELECTOR_SIDE = new SelectorSide()
.Case(Path, CENTER_NORMAL)
.Case(Global, SIDE_GLOBAL)
.Case(Water, SIDE_WATER)
.Case(Closed, SIDE_CLOSED)
.Case(Garden, SIDE_GARDEN_CONNECTED);
private static final SelectorCorner SELECTOR_CORNER = new SelectorCorner()
// Path to Path
.Case(Path, Path, Path, CENTER_NORMAL, RotationCorrection.UsingOrdinal)
// Path to Global
.Case(Path, Global, Global, SIDE_GLOBAL, RotationCorrection.WithCorrection)
.Case(Global, Path, Path, SIDE_GLOBAL)
.Case(Global, Path, Global, SIDE_GLOBAL)
.Case(Path, Global, Path, SIDE_GLOBAL, RotationCorrection.WithCorrection)
.Case(Global, Global, Global, CORNER_OUTER_GLOBAL, RotationCorrection.UsingOrdinal)
.Case(Global, Global, Path, CORNER_OUTER_GLOBAL, RotationCorrection.UsingOrdinal)
.Case(Path, Path, Global, CORNER_INNER_GLOBAL, RotationCorrection.UsingOrdinal)
// Path to Water
.Case(Path, Water, Water, SIDE_WATER, RotationCorrection.WithCorrection)
.Case(Water, Path, Path, SIDE_WATER)
.Case(Water, Path, Water, SIDE_WATER)
.Case(Path, Water, Path, SIDE_WATER, RotationCorrection.WithCorrection)
.Case(Water, Water, Water, CORNER_OUTER_WATER, RotationCorrection.UsingOrdinal)
.Case(Water, Water, Path, CORNER_OUTER_WATER, RotationCorrection.UsingOrdinal)
.Case(Path, Path, Water, CORNER_INNER_WATER, RotationCorrection.UsingOrdinal)
// Path to Closed
.Case(Path, Closed, Closed, SIDE_CLOSED, RotationCorrection.WithCorrection)
.Case(Closed, Path, Path, SIDE_CLOSED)
.Case(Closed, Path, Closed, SIDE_CLOSED)
.Case(Path, Closed, Path, SIDE_CLOSED, RotationCorrection.WithCorrection)
.Case(Closed, Closed, Closed, CORNER_OUTER_CLOSED, RotationCorrection.UsingOrdinal)
.Case(Closed, Closed, Path, CORNER_OUTER_CLOSED, RotationCorrection.UsingOrdinal)
.Case(Path, Path, Closed, CORNER_INNER_CLOSED, RotationCorrection.UsingOrdinal)
// Path to Garden
.Case(Path, Garden, Garden, SIDE_GARDEN, RotationCorrection.WithCorrection)
.Case(Garden, Path, Path, SIDE_GARDEN)
.Case(Garden, Path, Garden, SIDE_GARDEN)
.Case(Path, Garden, Path, SIDE_GARDEN, RotationCorrection.WithCorrection)
.Case(Garden, Garden, Garden, CORNER_OUTER_GARDEN, RotationCorrection.UsingOrdinal)
.Case(Garden, Garden, Path, CORNER_OUTER_GARDEN, RotationCorrection.UsingOrdinal)
.Case(Path, Path, Garden, CORNER_INNER_GARDEN, RotationCorrection.UsingOrdinal)
;
public PathArea() {
}
public PathArea(List<Pair<Tile, Tile>> list) {
super(list);
}
public void reset(@NonNull Tile tile, PathSide side) {
if (!tiles.contains(tile)) return;
File resetFile = null;
VariantSelector selector = SELECTOR_SIDE.Select(tile, side);
if (selector != null) resetFile = selector.select().orElse(null);
if (selector == null || resetFile == null) {
if (!BauSystem.DEV_SERVER) return;
resetFile = FALLBACK_SCHEM;
}
Point minPoint = Point.fromLocation(tile.getMinLocation());
PasteUtils.paste(resetFile, minPoint.add(side.pasteOffsetX, 0, side.pasteOffsetZ), side.rotate);
}
public void reset(@NonNull Tile tile, PathCorner corner) {
if (!tiles.contains(tile)) return;
File resetFile = null;
Pair<VariantSelector, RotationCorrection> pair = SELECTOR_CORNER.Select(tile, corner);
VariantSelector selector = pair.getKey();
RotationCorrection rotationCorrection = pair.getValue();
if (selector != null) resetFile = selector.select().orElse(null);
if (selector == null || resetFile == null) {
if (!BauSystem.DEV_SERVER) return;
resetFile = FALLBACK_SCHEM;
rotationCorrection = RotationCorrection.Unchanged;
}
int rotate = corner.rotate;
switch (rotationCorrection) {
case Unchanged:
break;
case UsingOrdinal:
rotate = corner.ordinal() * 90;
break;
case WithCorrection:
rotate += corner.rotateCorrection;
break;
}
Point minPoint = Point.fromLocation(tile.getMinLocation());
PasteUtils.paste(resetFile, minPoint.add(corner.pasteOffsetX, 0, corner.pasteOffsetZ), rotate);
}
@Override
public void place(Location location, PasteBuilder pasteBuilder, boolean extension) {
Tile tile = Tile.fromLocation(location).orElse(null);
if (tile == null || !tiles.contains(tile)) return;
Point minPoint = Point.fromLocation(tile.getMinLocation());
if (isGarden(tile)) {
File resetFile = GARDEN.select().orElse(null);
if (resetFile != null) {
PasteUtils.paste(resetFile, minPoint, 0);
}
return;
}
File resetFile = CENTER_NORMAL.select().orElse(null);
if (resetFile != null) {
PasteUtils.paste(resetFile, minPoint.add(7, 0, 7), 0);
}
for (PathSide side : PathSide.values()) {
reset(tile, side);
}
for (PathCorner corner : PathCorner.values()) {
reset(tile, corner);
}
}
public static boolean isGarden(Tile tile) {
for (int x = -1; x <= 1; x++) {
for (int z = -1; z <= 1; z++) {
if (x == 0 && z == 0) continue;
Tile t = tile.add(x, z).orElse(null);
if (t == null) {
return false;
}
if (!DynamicRegionSystem.INSTANCE.get(t).getType().isPath()) {
return false;
}
}
}
return true;
}
protected static RegionType.ConnectionType getConnectionType(Tile tile, PathSide side, PathSide optionalSide) {
Optional<Tile> optionalTile = tile.add(side.tileOffsetX, side.tileOffsetZ);
if (optionalSide != null) {
optionalTile = optionalTile.flatMap(t -> t.add(optionalSide.tileOffsetX, optionalSide.tileOffsetZ));
}
if (optionalTile.isEmpty()) {
return RegionType.ConnectionType.Global;
}
tile = optionalTile.get();
Region region = DynamicRegionSystem.INSTANCE.get(tile.getCenterLocation());
if (region instanceof PathRegion) {
return isGarden(tile) ? Garden : Path;
}
return region.getType().getConnectionType();
}
}

View File

@@ -1,38 +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.bausystem.region.dynamic.path;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public enum PathCorner {
NorthEast(PathSide.North, PathSide.East, 14, 0, 0, -90),
NorthWest(PathSide.North, PathSide.West, 0, 0, 0, 90),
SouthWest(PathSide.South, PathSide.West, 0, 14, 180, -90),
SouthEast(PathSide.South, PathSide.East, 14, 14, 180, 90),
;
public final PathSide side1;
public final PathSide side2;
public final int pasteOffsetX;
public final int pasteOffsetZ;
public final int rotate;
public final int rotateCorrection;
}

View File

@@ -1,143 +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.bausystem.region.dynamic.path;
import com.google.gson.JsonArray;
import com.google.gson.stream.JsonWriter;
import de.steamwar.bausystem.region.*;
import de.steamwar.bausystem.region.dynamic.*;
import de.steamwar.sql.GameModeConfig;
import lombok.NonNull;
import org.bukkit.Location;
import org.bukkit.Material;
import java.io.IOException;
import java.util.UUID;
@RegionConstructorData(
identifier = "path",
name = "Path",
material = Material.DIRT_PATH,
widthX = Tile.tileSize,
widthZ = Tile.tileSize
)
public class PathRegion extends DynamicRegion {
private final PathArea area;
public PathRegion(Tile minTile, PathArea area) {
super(minTile);
this.area = new PathArea();
regionData = new PathRegionData(this);
finishCreate();
}
public PathRegion(UUID id, JsonArray tileData) {
super(id, tileData);
area = new PathArea(readQuantizedTiles(tileData));
regionData = new PathRegionData(this);
finishLoad();
}
@Override
public void writeData(JsonWriter writer) throws IOException {
writeQuantizedTiles(writer, area.quantize());
}
public void add(Tile tile) {
if (area.addTile(tile)) {
save();
}
}
public void update(Tile toUpdate, NeighbourDirection toDirection) {
for (PathSide side : toDirection.getSideUpdates()) {
area.reset(toUpdate, side);
}
for (PathCorner corner : toDirection.getCornerUpdates()) {
area.reset(toUpdate, corner);
}
}
@Override
public @NonNull RegionType getType() {
return RegionType.PATH;
}
@Override
public @NonNull Area getArea() {
return area;
}
@Override
public @NonNull Area getBuildArea() {
return Area.EMPTY;
}
@Override
public @NonNull Area getTestblockArea() {
return Area.EMPTY;
}
@Override
public @NonNull GameModeConfig<Material, String> getGameModeConfig() {
return GameModeConfig.getDefaults();
}
@Override
public @NonNull RegionHistory getHistory() {
return RegionHistory.EMPTY;
}
@Override
public @NonNull RegionBackups getBackups() {
return RegionBackups.EMPTY;
}
@Override
public void save() {
DynamicRegionRepository.saveRegion(this);
}
@Override
public void load(RegionData regionData) {
DynamicRegionRepository.loadRegionData(this, regionData);
}
@Override
public void delete(Location location) {
Tile tile = Tile.fromLocation(location).orElse(null);
if (tile == null) return;
boolean delete = area.removeTile(tile);
if (delete) {
DynamicRegionSystem.INSTANCE.remove(this);
DynamicRegionRepository.deleteRegion(this);
}
Point minPoint = Point.fromLocation(tile.getMinLocation());
Point maxPoint = Point.fromLocation(tile.getMaxLocation());
PasteUtils.reset(minPoint, maxPoint);
this.updateNeighbours();
if (!delete) {
save();
}
}
}

View File

@@ -1,57 +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.bausystem.region.dynamic.path;
import de.steamwar.bausystem.region.RegionData;
import de.steamwar.bausystem.region.RegionFlagPolicy;
import de.steamwar.bausystem.region.flags.FireMode;
import de.steamwar.bausystem.region.flags.Flag;
import de.steamwar.bausystem.region.flags.ProtectMode;
import de.steamwar.bausystem.region.flags.TNTMode;
import de.steamwar.core.Core;
import lombok.NonNull;
public class PathRegionData extends RegionData {
public PathRegionData(PathRegion pathRegion) {
super(pathRegion);
}
@Override
protected void initialize() {
flagMap.put(Flag.TNT, TNTMode.DENY);
flagMap.put(Flag.FIRE, FireMode.DENY);
flagMap.put(Flag.PROTECT, ProtectMode.INACTIVE);
}
@Override
public @NonNull <T extends Enum<T> & Flag.Value<T>> RegionFlagPolicy has(@NonNull Flag<T> flag) {
if (flag.oneOf(Flag.TNT, Flag.FIRE, Flag.PROTECT)) {
return RegionFlagPolicy.READ_ONLY;
}
if (flag.oneOf(Flag.ITEMS) && Core.getVersion() >= 20) {
return RegionFlagPolicy.WRITABLE;
}
if (flag.oneOf(Flag.FREEZE)) {
return RegionFlagPolicy.WRITABLE;
}
return RegionFlagPolicy.NOT_APPLICABLE;
}
}

View File

@@ -1,37 +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.bausystem.region.dynamic.path;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public enum PathSide {
North(0, -1, 7, 0, 0),
South(0, 1, 7, 14, 180),
West(-1, 0, 0, 7, 90),
East(1, 0, 14, 7, 270),
;
public final int tileOffsetX;
public final int tileOffsetZ;
public final int pasteOffsetX;
public final int pasteOffsetZ;
public final int rotate;
}

View File

@@ -1,68 +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.bausystem.region.dynamic.path;
import de.steamwar.bausystem.region.RegionType;
import de.steamwar.bausystem.region.dynamic.Tile;
import de.steamwar.bausystem.region.dynamic.VariantSelector;
import de.steamwar.bausystem.shared.Pair;
public class SelectorCorner {
private static final int BITS;
private static final int SELECTOR_COUNT;
static {
int values = RegionType.ConnectionType.values().length;
BITS = (int) Math.ceil(Math.log(values) / Math.log(2));
SELECTOR_COUNT = (int) Math.pow(2, BITS * 3D);
}
private VariantSelector[] selectors = new VariantSelector[SELECTOR_COUNT];
private RotationCorrection[] rotationCorrections = new RotationCorrection[SELECTOR_COUNT];
private int getIndex(RegionType.ConnectionType left, RegionType.ConnectionType right, RegionType.ConnectionType diagonal) {
int index = left.ordinal();
index = index << BITS | right.ordinal();
return index << BITS | diagonal.ordinal();
}
public SelectorCorner Case(RegionType.ConnectionType left, RegionType.ConnectionType right, RegionType.ConnectionType diagonal, VariantSelector selector) {
int index = getIndex(left, right, diagonal);
selectors[index] = selector;
rotationCorrections[index] = RotationCorrection.Unchanged;
return this;
}
public SelectorCorner Case(RegionType.ConnectionType left, RegionType.ConnectionType right, RegionType.ConnectionType diagonal, VariantSelector selector, RotationCorrection rotationCorrection) {
int index = getIndex(left, right, diagonal);
selectors[index] = selector;
rotationCorrections[index] = rotationCorrection;
return this;
}
public Pair<VariantSelector, RotationCorrection> Select(Tile tile, PathCorner corner) {
RegionType.ConnectionType left = PathArea.getConnectionType(tile, corner.side1, null);
RegionType.ConnectionType right = PathArea.getConnectionType(tile, corner.side2, null);
RegionType.ConnectionType diagonal = PathArea.getConnectionType(tile, corner.side1, corner.side2);
int index = getIndex(left, right, diagonal);
return new Pair<>(selectors[index], rotationCorrections[index]);
}
}

View File

@@ -1,39 +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.bausystem.region.dynamic.path;
import de.steamwar.bausystem.region.RegionType;
import de.steamwar.bausystem.region.dynamic.Tile;
import de.steamwar.bausystem.region.dynamic.VariantSelector;
public class SelectorSide {
private VariantSelector[] selectors = new VariantSelector[RegionType.ConnectionType.values().length];
public SelectorSide Case(RegionType.ConnectionType type, VariantSelector selector) {
selectors[type.ordinal()] = selector;
return this;
}
public VariantSelector Select(Tile tile, PathSide side) {
RegionType.ConnectionType type = PathArea.getConnectionType(tile, side, null);
return selectors[type.ordinal()];
}
}

View File

@@ -1,113 +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.bausystem.region.dynamic.special;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.region.DynamicRegionSystem;
import de.steamwar.bausystem.region.Point;
import de.steamwar.bausystem.region.Region;
import de.steamwar.bausystem.region.dynamic.DynamicRegion;
import de.steamwar.bausystem.region.dynamic.PasteUtils;
import de.steamwar.bausystem.region.dynamic.Tile;
import de.steamwar.bausystem.region.dynamic.VariantSelector;
import de.steamwar.bausystem.utils.PasteBuilder;
import lombok.Getter;
import lombok.NonNull;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
public class SpecialArea implements Region.Area {
public static final File SPECIAL_PATH_DIR = new File(Bukkit.getWorlds().get(0).getWorldFolder(), "sections/special");
private final DynamicRegion region;
@Getter
private final Tile tile;
private final Point minPoint;
private final Point maxPoint;
private final Point copyPoint;
private final VariantSelector resetFiles;
public SpecialArea(Tile tile, DynamicRegion region, VariantSelector resetFiles) {
this.region = region;
this.tile = tile;
minPoint = Point.fromLocation(tile.getMinLocation()).setY(WORLD_MIN_Y);
maxPoint = Point.fromLocation(tile.getMaxLocation()).setY(WORLD_MAX_Y);
copyPoint = Point.fromLocation(tile.getCenterLocation());
this.resetFiles = resetFiles;
}
@Override
public @NonNull Point getMinPoint(boolean extension) {
return minPoint;
}
@Override
public @NonNull Point getMaxPoint(boolean extension) {
return maxPoint;
}
@Override
public @NonNull Point getCopyPoint() {
return copyPoint;
}
private boolean resetting = false;
@Override
public void reset(Location location, PasteBuilder pasteBuilder, boolean extension) {
if (resetting) return;
AtomicBoolean hasResetting = new AtomicBoolean(false);
List<Region> regions = DynamicRegionSystem.INSTANCE.getConnectedRegions(region)
.peek(region -> {
if (((SpecialArea) region.getArea()).resetting) hasResetting.set(true);
})
.toList();
if (hasResetting.get()) return;
regions.forEach(region -> {
((SpecialArea) region.getArea()).resetting = true;
});
for (int i = 0; i < regions.size(); i++) {
final int offset = i / 10;
final Region region = regions.get(i);
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
region.getArea().place(null, null, false); // TODO: Fix this!
}, offset);
}
}
@Override
public void place(Location location, PasteBuilder pasteBuilder, boolean extension) {
File resetFile = resetFiles.select().orElse(null);
if (resetFile != null) PasteUtils.paste(resetFile, minPoint, 0);
resetting = false;
}
}

View File

@@ -1,63 +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.bausystem.region.dynamic.special;
import de.steamwar.bausystem.region.DynamicRegionSystem;
import de.steamwar.bausystem.region.Region;
import de.steamwar.bausystem.region.RegionData;
import de.steamwar.bausystem.region.RegionFlagPolicy;
import de.steamwar.bausystem.region.dynamic.DynamicRegion;
import de.steamwar.bausystem.region.flags.Flag;
import de.steamwar.bausystem.region.flags.TNTMode;
import de.steamwar.core.Core;
import lombok.NonNull;
import java.util.stream.Stream;
public class SpecialRegionData extends RegionData {
private final DynamicRegion region;
public SpecialRegionData(DynamicRegion region) {
super(region);
this.region = region;
}
@Override
protected void initialize() {
flagMap.put(Flag.TNT, TNTMode.DENY);
}
@Override
protected Stream<Region> connectedRegions() {
return DynamicRegionSystem.INSTANCE.getConnectedRegions(region);
}
@Override
public @NonNull <T extends Enum<T> & Flag.Value<T>> RegionFlagPolicy has(@NonNull Flag<T> flag) {
if (flag.oneOf(Flag.TNT, Flag.FIRE, Flag.FREEZE, Flag.PROTECT, Flag.NO_GRAVITY, Flag.WATER_DESTROY)) {
return RegionFlagPolicy.WRITABLE;
}
if (flag.oneOf(Flag.ITEMS) && Core.getVersion() >= 20) {
return RegionFlagPolicy.WRITABLE;
}
return RegionFlagPolicy.NOT_APPLICABLE;
}
}

View File

@@ -1,121 +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.bausystem.region.dynamic.special.dry;
import com.google.gson.JsonArray;
import com.google.gson.stream.JsonWriter;
import de.steamwar.bausystem.region.RegionBackups;
import de.steamwar.bausystem.region.RegionData;
import de.steamwar.bausystem.region.RegionHistory;
import de.steamwar.bausystem.region.RegionType;
import de.steamwar.bausystem.region.dynamic.*;
import de.steamwar.bausystem.region.dynamic.modes.AreaTile;
import de.steamwar.bausystem.region.dynamic.modes.DisplayRegionData;
import de.steamwar.bausystem.region.dynamic.special.SpecialArea;
import de.steamwar.bausystem.region.dynamic.special.SpecialRegionData;
import de.steamwar.sql.GameModeConfig;
import lombok.NonNull;
import org.bukkit.Material;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
import static de.steamwar.bausystem.region.dynamic.special.SpecialArea.SPECIAL_PATH_DIR;
@RegionConstructorData(
identifier = "special_dry",
name = "Dry",
material = Material.SAND,
widthX = Tile.tileSize,
widthZ = Tile.tileSize
)
public class DryRegion extends DynamicRegion {
private static final VariantSelector DRY = VariantSelector.Get(new File(SPECIAL_PATH_DIR, "dry"));
private final SpecialArea area;
public DryRegion(Tile tile) {
this(UUID.randomUUID(), tile);
finishCreate();
}
public DryRegion(UUID id, JsonArray tileData) {
this(id, readTile(tileData));
finishLoad();
}
private DryRegion(UUID id, Tile tile) {
super(id, null);
area = new SpecialArea(tile, this, DRY);
regionData = new SpecialRegionData(this);
}
@Override
public void writeData(JsonWriter writer) throws IOException {
writeTile(writer, area.getTile());
}
@Override
public @NonNull RegionType getType() {
return RegionType.DRY_SPECIAL;
}
@Override
public @NonNull Area getArea() {
return area;
}
@Override
public @NonNull Area getBuildArea() {
return Area.EMPTY;
}
@Override
public @NonNull Area getTestblockArea() {
return Area.EMPTY;
}
@Override
public @NonNull GameModeConfig<Material, String> getGameModeConfig() {
return GameModeConfig.getDefaults();
}
@Override
public @NonNull RegionHistory getHistory() {
return RegionHistory.EMPTY;
}
@Override
public @NonNull RegionBackups getBackups() {
return RegionBackups.EMPTY;
}
@Override
public void save() {
DynamicRegionRepository.saveRegion(this);
}
@Override
public void load(RegionData regionData) {
DynamicRegionRepository.loadRegionData(this, regionData);
}
}

View File

@@ -1,119 +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.bausystem.region.dynamic.special.wet;
import com.google.gson.JsonArray;
import com.google.gson.stream.JsonWriter;
import de.steamwar.bausystem.region.RegionBackups;
import de.steamwar.bausystem.region.RegionData;
import de.steamwar.bausystem.region.RegionHistory;
import de.steamwar.bausystem.region.RegionType;
import de.steamwar.bausystem.region.dynamic.*;
import de.steamwar.bausystem.region.dynamic.special.SpecialArea;
import de.steamwar.bausystem.region.dynamic.special.SpecialRegionData;
import de.steamwar.sql.GameModeConfig;
import lombok.NonNull;
import org.bukkit.Material;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
import static de.steamwar.bausystem.region.dynamic.special.SpecialArea.SPECIAL_PATH_DIR;
@RegionConstructorData(
identifier = "special_wet",
name = "Wet",
material = Material.LIGHT_BLUE_CONCRETE_POWDER,
widthX = Tile.tileSize,
widthZ = Tile.tileSize
)
public class WetRegion extends DynamicRegion {
private static final VariantSelector WET = VariantSelector.Get(new File(SPECIAL_PATH_DIR, "wet"));
private final SpecialArea area;
public WetRegion(Tile tile) {
this(UUID.randomUUID(), tile);
finishCreate();
}
public WetRegion(UUID id, JsonArray tileData) {
this(id, readTile(tileData));
finishLoad();
}
private WetRegion(UUID id, Tile tile) {
super(id, null);
area = new SpecialArea(tile, this, WET);
regionData = new WetRegionData(this);
}
@Override
public void writeData(JsonWriter writer) throws IOException {
writeTile(writer, area.getTile());
}
@Override
public @NonNull RegionType getType() {
return RegionType.WET_SPECIAL;
}
@Override
public @NonNull Area getArea() {
return area;
}
@Override
public @NonNull Area getBuildArea() {
return Area.EMPTY;
}
@Override
public @NonNull Area getTestblockArea() {
return Area.EMPTY;
}
@Override
public @NonNull GameModeConfig<Material, String> getGameModeConfig() {
return GameModeConfig.getDefaults();
}
@Override
public @NonNull RegionHistory getHistory() {
return RegionHistory.EMPTY;
}
@Override
public @NonNull RegionBackups getBackups() {
return RegionBackups.EMPTY;
}
@Override
public void save() {
DynamicRegionRepository.saveRegion(this);
}
@Override
public void load(RegionData regionData) {
DynamicRegionRepository.loadRegionData(this, regionData);
}
}

View File

@@ -1,45 +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.bausystem.region.dynamic.special.wet;
import de.steamwar.bausystem.region.RegionFlagPolicy;
import de.steamwar.bausystem.region.dynamic.DynamicRegion;
import de.steamwar.bausystem.region.dynamic.special.SpecialRegionData;
import de.steamwar.bausystem.region.flags.Flag;
import de.steamwar.bausystem.region.flags.ProtectMode;
import lombok.NonNull;
public class WetRegionData extends SpecialRegionData {
public WetRegionData(DynamicRegion region) {
super(region);
}
@Override
protected void initialize() {
flagMap.put(Flag.PROTECT, ProtectMode.INACTIVE);
}
@Override
public @NonNull <T extends Enum<T> & Flag.Value<T>> RegionFlagPolicy has(@NonNull Flag<T> flag) {
if (flag.oneOf(Flag.PROTECT)) return RegionFlagPolicy.NOT_APPLICABLE;
return super.has(flag);
}
}

View File

@@ -46,6 +46,10 @@ public class FixedRegionSystem implements RegionSystem {
RegionLoader.load();
}
@Override
public void save() {
}
@Override
public @NonNull Location getWorldSpawn() {
Location spawnLocation = Bukkit.getWorlds().get(0).getSpawnLocation();
@@ -75,9 +79,4 @@ public class FixedRegionSystem implements RegionSystem {
public Stream<Region> getRegions() {
return REGION_MAP.values().stream();
}
@Override
public @NonNull Stream<Region> getConnectedRegions(Region region) {
return Stream.empty();
}
}

View File

@@ -21,15 +21,12 @@ package de.steamwar.bausystem.region.fixed;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import de.steamwar.bausystem.region.*;
import de.steamwar.bausystem.region.flags.Flag;
import de.steamwar.bausystem.utils.PasteBuilder;
import de.steamwar.bausystem.worlddata.WorldData;
import de.steamwar.sql.GameModeConfig;
import lombok.NonNull;
import lombok.Setter;
import org.bukkit.Location;
import org.bukkit.Material;
import yapion.hierarchy.types.YAPIONObject;
import javax.annotation.Nullable;
import java.io.File;
@@ -46,6 +43,8 @@ public final class FixedGlobalRegion implements Region {
@Setter
private static RegionData FLAG_STORAGE;
private static final UUID GLOBAL_REGION_ID = new UUID(0, 0);
private static final Area GLOBAL_AREA = new Area() {
@Override
public @NonNull Point getMinPoint(boolean extension) {
@@ -73,8 +72,14 @@ public final class FixedGlobalRegion implements Region {
return null;
}
@Nullable
@Override
public void place(Location location, PasteBuilder pasteBuilder, boolean extension) {
public File getResetFile() {
return null;
}
@Override
public void reset(PasteBuilder pasteBuilder, boolean extension) {
}
@Override
@@ -97,7 +102,7 @@ public final class FixedGlobalRegion implements Region {
@Override
public @NonNull UUID getID() {
return RegionSystem.GLOBAL_REGION_ID;
return GLOBAL_REGION_ID;
}
@Override
@@ -134,14 +139,4 @@ public final class FixedGlobalRegion implements Region {
public @NonNull RegionBackups getBackups() {
return RegionBackups.EMPTY;
}
@Override
public void save() {
FixedRegionDataUtils.saveRegionData("global", FLAG_STORAGE);
}
@Override
public void load(RegionData regionData) {
FixedRegionDataUtils.loadRegionData("global", FLAG_STORAGE);
}
}

View File

@@ -20,7 +20,6 @@
package de.steamwar.bausystem.region.fixed;
import de.steamwar.bausystem.region.RegionData;
import de.steamwar.bausystem.region.RegionDataStore;
import de.steamwar.bausystem.region.RegionFlagPolicy;
import de.steamwar.bausystem.region.flags.ColorMode;
import de.steamwar.bausystem.region.flags.Flag;
@@ -28,11 +27,12 @@ import de.steamwar.bausystem.region.flags.ProtectMode;
import de.steamwar.bausystem.region.flags.TNTMode;
import de.steamwar.core.Core;
import lombok.NonNull;
import yapion.hierarchy.types.YAPIONObject;
public class FixedGlobalRegionData extends RegionData {
public FixedGlobalRegionData(RegionDataStore store) {
super(store);
public FixedGlobalRegionData(YAPIONObject data, Runnable onChange) {
super(data, onChange);
}
@Override

View File

@@ -31,7 +31,6 @@ import de.steamwar.sql.GameModeConfig;
import de.steamwar.sql.SchematicType;
import lombok.NonNull;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import yapion.hierarchy.types.YAPIONObject;
@@ -46,6 +45,7 @@ import java.util.stream.Collectors;
public class FixedRegion implements Region {
private static final File backupFolder = new File(Bukkit.getWorlds().get(0).getWorldFolder(), "backup");
private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd' 'HH:mm:ss");
private final String name;
private final UUID uuid;
@@ -75,7 +75,7 @@ public class FixedRegion implements Region {
while (files.size() >= 20) files.remove(0).delete();
}
final File backupFile = new File(definedBackupFolder, LocalDateTime.now().format(FORMATTER) + ".schem");
final File backupFile = new File(definedBackupFolder, LocalDateTime.now().format(formatter) + ".schem");
Point minPoint = area.getMinPoint(false);
Point maxPoint = area.getMaxPoint(false);
if (!FlatteningWrapper.impl.backup(minPoint, maxPoint, backupFile)) {
@@ -101,7 +101,7 @@ public class FixedRegion implements Region {
@Nullable
@Override
public Backup get(@NonNull String name) {
public Backup get(String name) {
final File definedBackupFolder = new File(new File(backupFolder, prototype.getName()), FixedRegion.this.name);
//noinspection ResultOfMethodCallIgnored
definedBackupFolder.mkdirs();
@@ -116,14 +116,14 @@ public class FixedRegion implements Region {
private final File file;
public BackupImpl(File file) {
super(RegionBackups.BackupType.AUTOMATIC, file.getName().replace(' ', '_').replace(".schem", ""), __ -> flagStorage);
super(RegionBackups.BackupType.AUTOMATIC, file.getName().replace(' ', '_').replace(".schem", ""), flagStorage);
this.file = file;
}
@Override
public boolean load() {
if (!file.exists()) return false;
EditSession editSession = new PasteBuilder(PasteBuilder.ClipboardProvider.file(file))
EditSession editSession = new PasteBuilder(new PasteBuilder.FileProvider(file))
.pastePoint(area.getMinPoint(false).add(prototype.getSizeX() / 2, 0, prototype.getSizeZ() / 2))
.minPoint(area.getMinPoint(false))
.maxPoint(area.getMaxPoint(false))
@@ -134,20 +134,15 @@ public class FixedRegion implements Region {
}
@Override
public long getCreationTime() {
return file.lastModified();
}
@Override
public void delete(Location location) {
public void delete() {
file.delete();
}
}
public FixedRegion(String name, Prototype prototype, YAPIONObject regionConfig) {
public FixedRegion(String name, FixedRegionData flagStorage, Prototype prototype, YAPIONObject regionConfig, YAPIONObject regionData) {
this.name = name;
uuid = UUID.nameUUIDFromBytes(name.getBytes(StandardCharsets.UTF_8));
this.flagStorage = new FixedRegionData(this);
this.flagStorage = flagStorage;
this.prototype = prototype;
this.skin = prototype.getDefaultSkin();
@@ -175,10 +170,15 @@ public class FixedRegion implements Region {
return minPoint;
}
@Nullable
@Override
public void place(Location location, PasteBuilder pasteBuilder, boolean extension) {
pasteBuilder.with(PasteBuilder.ClipboardProvider.file(prototype.getSkinMap().get(skin).getSchematicFile()))
.reset(extension)
public File getResetFile() {
return prototype.getSkinMap().get(skin).getSchematicFile();
}
@Override
public void reset(PasteBuilder pasteBuilder, boolean extension) {
pasteBuilder.reset(extension)
.minPoint(getMinPoint(extension))
.maxPoint(getMaxPoint(extension))
.waterLevel(waterLevel);
@@ -223,10 +223,15 @@ public class FixedRegion implements Region {
return copyPoint;
}
@Nullable
@Override
public void place(Location location, PasteBuilder pasteBuilder, boolean extension) {
pasteBuilder.with(PasteBuilder.ClipboardProvider.file(prototype.getSkinMap().get(skin).getBuildSchematicFile()))
.reset(extension)
public File getResetFile() {
return prototype.getSkinMap().get(skin).getBuildSchematicFile();
}
@Override
public void reset(PasteBuilder pasteBuilder, boolean extension) {
pasteBuilder.reset(extension)
.minPoint(getMinPoint(extension))
.maxPoint(getMaxPoint(extension))
.waterLevel(waterLevel);
@@ -272,10 +277,15 @@ public class FixedRegion implements Region {
return copyPoint;
}
@Nullable
@Override
public void place(Location location, PasteBuilder pasteBuilder, boolean extension) {
pasteBuilder.with(PasteBuilder.ClipboardProvider.file(prototype.getSkinMap().get(skin).getTestblockSchematicFile()))
.reset(extension)
public File getResetFile() {
return prototype.getSkinMap().get(skin).getTestblockSchematicFile();
}
@Override
public void reset(PasteBuilder pasteBuilder, boolean extension) {
pasteBuilder.reset(extension)
.minPoint(getMinPoint(extension))
.maxPoint(getMaxPoint(extension))
.waterLevel(waterLevel);
@@ -377,14 +387,4 @@ public class FixedRegion implements Region {
public @NonNull RegionBackups getBackups() {
return regionBackups;
}
@Override
public void save() {
FixedRegionDataUtils.saveRegionData(name, flagStorage);
}
@Override
public void load(RegionData regionData) {
FixedRegionDataUtils.loadRegionData(name, flagStorage);
}
}

View File

@@ -20,16 +20,16 @@
package de.steamwar.bausystem.region.fixed;
import de.steamwar.bausystem.region.RegionData;
import de.steamwar.bausystem.region.RegionDataStore;
import de.steamwar.bausystem.region.RegionFlagPolicy;
import de.steamwar.bausystem.region.flags.Flag;
import de.steamwar.core.Core;
import lombok.NonNull;
import yapion.hierarchy.types.YAPIONObject;
public class FixedRegionData extends RegionData {
public FixedRegionData(RegionDataStore store) {
super(store);
public FixedRegionData(YAPIONObject data, Runnable onChange) {
super(data, onChange);
}
@Override

View File

@@ -1,64 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.bausystem.region.fixed;
import de.steamwar.bausystem.region.RegionData;
import de.steamwar.bausystem.region.flags.Flag;
import de.steamwar.bausystem.worlddata.WorldData;
import lombok.NonNull;
import lombok.experimental.UtilityClass;
import yapion.hierarchy.types.YAPIONObject;
@UtilityClass
public class FixedRegionDataUtils {
public void saveRegionData(@NonNull String identifier, @NonNull RegionData regionData) {
YAPIONObject yapionObject = new YAPIONObject();
YAPIONObject flagData = yapionObject.getOrSetDefault("flagStorage", new YAPIONObject());
regionData.getBackedMap().forEach((flag, value) -> {
flagData.put(flag.name(), value.name());
});
regionData.getBackedProperties().forEach(property -> {
yapionObject.put(property.field, property.writer.apply(property.get()));
});
WorldData.getRegionsData().put(identifier, yapionObject);
WorldData.write();
}
public void loadRegionData(@NonNull String identifier, @NonNull RegionData regionData) {
YAPIONObject values = WorldData.getRegionsData().getObject(identifier);
if (values == null) return;
YAPIONObject flagData = values.getObjectOrDefault("flagStorage", new YAPIONObject());
for (final Flag flag : Flag.getFlags()) {
if (!regionData.has(flag).isWritable()) continue;
try {
String s = flagData.getPlainValue(flag.name());
regionData.getBackedMap().put(flag, flag.valueOfValue(s));
} catch (Exception e) {
regionData.getBackedMap().put(flag, (Flag.Value<?>) flag.getDefaultValue());
}
}
regionData.getBackedProperties().forEach(property -> {
Object object = values.getPlainValue(property.field);
property.set(property.loader.apply(object));
});
}
}

View File

@@ -210,8 +210,14 @@ public class Prototype {
}
}
public static void generateRegion(String name, YAPIONObject regionConfig) {
Prototype prototype = PROTOTYPE_MAP.get(regionConfig.getPlainValue("prototype"));
FixedRegionSystem.addRegion(new FixedRegion(name, prototype, regionConfig));
public static void generateRegion(String name, YAPIONObject regionConfig, YAPIONObject regionData) {
Prototype prototype;
if (regionData.containsKey("prototype", String.class)) {
prototype = PROTOTYPE_MAP.get(regionData.getPlainValue("prototype"));
} else {
prototype = PROTOTYPE_MAP.get(regionConfig.getPlainValue("prototype"));
}
FixedRegionData flagStorage = new FixedRegionData(regionData, WorldData::write);
FixedRegionSystem.addRegion(new FixedRegion(name, flagStorage, prototype, regionConfig, regionData));
}
}

View File

@@ -19,14 +19,16 @@
package de.steamwar.bausystem.region.fixed.loader;
import de.steamwar.bausystem.region.fixed.FixedGlobalRegion;
import de.steamwar.bausystem.region.fixed.FixedGlobalRegionData;
import de.steamwar.bausystem.region.fixed.FixedGlobalRegion;
import de.steamwar.bausystem.region.fixed.Prototype;
import de.steamwar.bausystem.worlddata.WorldData;
import lombok.experimental.UtilityClass;
import org.bukkit.Bukkit;
import yapion.hierarchy.diff.DiffChange;
import yapion.hierarchy.diff.YAPIONDiff;
import yapion.hierarchy.types.YAPIONObject;
import yapion.hierarchy.types.YAPIONType;
import yapion.parser.YAPIONParser;
import java.io.BufferedInputStream;
@@ -53,6 +55,7 @@ public class RegionLoader {
}
loaded = yapionObject;
YAPIONObject optionsYapionObject = WorldData.getRegionsData();
yapionObject.forEach((key, yapionAnyType) -> {
if (key.equals("global")) {
return;
@@ -60,9 +63,23 @@ public class RegionLoader {
if (!(yapionAnyType instanceof YAPIONObject)) {
return;
}
Prototype.generateRegion(key, (YAPIONObject) yapionAnyType);
YAPIONObject regionConfig = (YAPIONObject) yapionAnyType;
YAPIONObject regionData = new YAPIONObject();
if (optionsYapionObject.containsKey(key, YAPIONType.OBJECT)) {
regionData = optionsYapionObject.getObject(key);
} else {
optionsYapionObject.add(key, regionData);
}
Prototype.generateRegion(key, regionConfig, regionData);
});
FixedGlobalRegion.setFLAG_STORAGE(new FixedGlobalRegionData(FixedGlobalRegion.INSTANCE));
YAPIONObject globalOptions = optionsYapionObject.getObject("global");
if (globalOptions == null) {
globalOptions = new YAPIONObject();
optionsYapionObject.add("global", globalOptions);
}
FixedGlobalRegion.setFLAG_STORAGE(new FixedGlobalRegionData(globalOptions, WorldData::write));
}
}

View File

@@ -28,7 +28,6 @@ tasks.build {
dependencies {
implementation(project(":BauSystem:BauSystem_RegionFixed"))
implementation(project(":BauSystem:BauSystem_RegionDynamic"))
implementation(project(":BauSystem:BauSystem_Main"))
implementation(project(":BauSystem:BauSystem_15"))
implementation(project(":BauSystem:BauSystem_18"))
@@ -54,13 +53,3 @@ tasks.register<DevServer>("DevBau21") {
dependsOn(":SchematicSystem:shadowJar")
template = "Bau21"
}
tasks.register<DevServer>("DevBau21Dynamic") {
group = "run"
description = "Run a 1.21 Dynamic Dev Bau"
dependsOn(":SpigotCore:shadowJar")
dependsOn(":BauSystem:shadowJar")
dependsOn(":SchematicSystem:shadowJar")
template = "Bau21-Dynamic"
jar = "/jars/paper-1.21.6.jar"
}

View File

@@ -24,4 +24,5 @@ plugins {
dependencies {
testImplementation(libs.junit)
testImplementation(libs.hamcrest)
compileOnly(project(":CommonCore:SQL"))
}

View File

@@ -17,10 +17,6 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.bausystem.region.dynamic.path;
package de.steamwar
public enum RotationCorrection {
Unchanged,
WithCorrection,
UsingOrdinal,
}
data class ServerInfo(val name: String, val version: Int, val checkpointed: Boolean)

View File

@@ -0,0 +1,71 @@
/*
* 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.logger
import de.steamwar.sql.AuditLog
import de.steamwar.sql.AuditLogTable
import de.steamwar.sql.SQLWrapper
import de.steamwar.sql.SteamwarUser
import de.steamwar.sql.internal.useDb
import org.jetbrains.exposed.v1.jdbc.insertIgnore
class LogEntry(val type: AuditLog.Type, val user: SteamwarUser) {
private val start = System.currentTimeMillis()
var text: String = ""
var arguments: String = ""
private var exceptionType: String? = null
private var exceptionText: String? = null
private var exceptionStacktrace: String? = null
private var owner: SteamwarUser? = null
fun addException(e: Throwable) {
exceptionType = e.javaClass.name
exceptionText = e.message
exceptionStacktrace = e.stackTraceToString()
}
fun addServerOwner(owner: SteamwarUser) {
this.owner = owner
}
fun finish() {
val end = System.currentTimeMillis()
val info = SQLWrapper.impl.serverInfo
useDb {
AuditLogTable.insertIgnore {
it[this.action] = type
it[this.actor] = user.getId()
it[this.actionText] = text
it[this.actionArguments] = arguments
it[this.duration] = end - start
it[this.errorType] = exceptionType
it[this.errorText] = exceptionText
it[this.stackTrace] = exceptionStacktrace
it[this.server] = info.name
it[this.checkpointed] = info.checkpointed
it[this.duration] = end - start
it[this.serverOwner] = owner?.getId()
}
}
}
}

View File

@@ -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,13 +17,11 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.bausystem.region;
package de.steamwar.logger
import org.bukkit.Location;
import de.steamwar.sql.AuditLog
import de.steamwar.sql.SteamwarUser
public interface RegionDataStore {
void save();
void load(RegionData regionData);
default void delete(Location location) {
}
}
object SWLogger {
fun startCommand(user: SteamwarUser) = LogEntry(AuditLog.Type.COMMAND, user)
}

View File

@@ -19,94 +19,47 @@
package de.steamwar.sql
import de.steamwar.sql.internal.useDb
import org.jetbrains.exposed.v1.core.dao.id.EntityID
import org.jetbrains.exposed.v1.core.dao.id.IntIdTable
import org.jetbrains.exposed.v1.dao.IntEntity
import org.jetbrains.exposed.v1.dao.IntEntityClass
import org.jetbrains.exposed.v1.javatime.timestamp
import java.time.Instant
object AuditLogTable: IntIdTable("AuditLog", "AuditLogId") {
val time = timestamp("Time")
val server = varchar("ServerName", 255)
val serverOwner = reference("ServerOwner", SteamwarUserTable).nullable()
val serverType = varchar("ServerType", 255)
val serverOwner = optReference("ServerOwner", SteamwarUserTable)
val checkpointed = bool("Checkpointed")
val actor = reference("Actor", SteamwarUserTable)
val action = enumerationByName("ActionType", 255, AuditLog.Type::class)
val actionText = text("ActionText")
val actionArguments = text("ActionArguments")
val duration = long("Duration")
val errorType = text("ErrorType").nullable()
val errorText = text("ErrorText").nullable()
val stackTrace = text("StackTrace").nullable()
}
class AuditLog(id: EntityID<Int>): IntEntity(id) {
companion object: IntEntityClass<AuditLog>(AuditLogTable) {
const val SERVER_NAME_VELOCITY: String = "Velocity"
private fun create(
serverName: String,
serverOwner: SteamwarUser?,
actor: SteamwarUser,
actionType: Type,
text: String = ""
) = useDb {
new {
this.time = Instant.now()
this.server = serverName
this.serverOwner = serverOwner?.id
this.actor = actor.id
this.action = actionType
this.actionText = text
}
}
@JvmStatic
fun createJoin(jointServerName: String, serverOwner: SteamwarUser?, joinedPlayer: SteamwarUser) = create(jointServerName, serverOwner, joinedPlayer, Type.JOIN)
@JvmStatic
fun createLeave(leftServerName: String, serverOwner: SteamwarUser?, joinedPlayer: SteamwarUser) = create(leftServerName, serverOwner, joinedPlayer, Type.LEAVE)
@JvmStatic
fun createCommand(serverName: String, serverOwner: SteamwarUser?, player: SteamwarUser?, command: String) = player?.let { create(serverName, serverOwner, it, Type.COMMAND, command) }
@JvmStatic
fun createSensitiveCommand(
serverName: String,
serverOwner: SteamwarUser?,
player: SteamwarUser?,
command: String
) = player?.let { create(serverName, serverOwner, it, Type.SENSITIVE_COMMAND, command) }
@JvmStatic
fun createChat(serverName: String, serverOwner: SteamwarUser?, chatter: SteamwarUser, chat: String) = create(serverName, serverOwner, chatter, Type.CHAT, chat)
@JvmStatic
fun createGuiOpen(serverName: String, serverOwner: SteamwarUser?, player: SteamwarUser, guiName: String) = create(serverName, serverOwner, player, Type.GUI_OPEN, guiName)
@JvmStatic
fun createGuiClick(
serverName: String,
serverOwner: SteamwarUser?,
player: SteamwarUser,
guiName: String,
clickType: String,
slot: Int,
itemName: String
) = create(
serverName,
serverOwner,
player,
Type.GUI_CLICK,
"Gui: $guiName\nSlot: $slot\nClickType: $clickType\nItemName: $itemName"
)
@JvmStatic
fun createGuiClose(serverName: String, serverOwner: SteamwarUser?, player: SteamwarUser, guiName: String) = create(serverName, serverOwner, player, Type.GUI_CLOSE, guiName)
@JvmField
val SERVER_NAME_VELOCITY: String = "Velocity"
}
var time by AuditLogTable.time
var server by AuditLogTable.server
val serverType by AuditLogTable.serverType
var serverOwner by AuditLogTable.serverOwner
var actor by AuditLogTable.actor
var checkpointed by AuditLogTable.checkpointed
var action by AuditLogTable.action
var actionText by AuditLogTable.actionText
var actionArguments by AuditLogTable.actionArguments
var duration by AuditLogTable.duration
var errorType by AuditLogTable.errorType
var errorText by AuditLogTable.errorText
var stackTrace by AuditLogTable.stackTrace
enum class Type {
JOIN,

View File

@@ -35,11 +35,7 @@ import java.time.Instant
object BannedUserIPsTable: CompositeIdTable("BannedUserIPs") {
val userId = reference("UserID", SteamwarUserTable)
val timestamp = timestamp("Timestamp")
val ip = varchar("IP", 45).entityId()
init {
addIdColumn(userId)
}
val ip = varchar("IP", 45)
override val primaryKey = PrimaryKey(userId, ip)
}

View File

@@ -402,13 +402,6 @@ public final class GameModeConfig<M, W> {
*/
public final int WaterDepth;
/**
* If TNT should break blocks even underwater
*
* @implSpec {@code true} by default
*/
public final boolean WaterDamage;
/**
* The outer border of the arena, measured in blocks around the schematic areas
*/
@@ -464,7 +457,6 @@ public final class GameModeConfig<M, W> {
private ArenaConfig(YMLWrapper loader, SchematicConfig.SizeConfig Size, List<Integer> EnterStages) {
loaded = loader.canLoad();
WaterDepth = loader.getInt("WaterDepth", 0);
WaterDamage = loader.getBoolean("WaterDamage", true);
Schem2Border = new Schem2BorderConfig(loader.with("Schem2Border"));
SpawnOffset = new SpawnOffsetConfig(loader.with("SpawnOffset"), Size);
BorderFromSchematic = loader.getInt("BorderFromSchematic", 21);
@@ -605,18 +597,18 @@ public final class GameModeConfig<M, W> {
public final boolean IgnorePublicOnly;
/**
* Replacements that should be done during PRE_RUNNING with no block updates
* If obsidian and bedrock should be replaced during PRE_RUNNING
*
* @implSpec {@code {}} by default
* @implSpec {@code false} by default
*/
public final Map<M, M> ReplacementsWithoutBlockUpdates;
public final boolean ReplaceObsidianBedrock;
/**
* Replacements that should be done during PRE_RUNNING with block updates
* If the replacement should happen with block updates
*
* @implSpec {@code {}} by default
* @implSpec {@code false} by default
*/
public final Map<M, M> ReplacementsWithBlockUpdates;
public final boolean ReplaceWithBlockupdates;
/**
* If the schematic perparation arena mode is time limited
@@ -649,7 +641,7 @@ public final class GameModeConfig<M, W> {
/**
* Maximal blast resistance for the design blocks
*
* @implSpec {@link SchematicConfig#MaxBlastResistance} by default
* @implSpec {@code Double.MAX_VALUE} by default
*/
public final double MaxDesignBlastResistance;
@@ -673,11 +665,13 @@ public final class GameModeConfig<M, W> {
PasteAligned = loader.getBoolean("PasteAligned", false);
OnlyPublicSchematics = loader.getBoolean("OnlyPublicSchematics", false);
IgnorePublicOnly = loader.getBoolean("IgnorePublicOnly", false);
ReplaceObsidianBedrock = loader.getBoolean("ReplaceObsidianBedrock", false);
ReplaceWithBlockupdates = loader.getBoolean("ReplaceWithBlockupdates", false);
UnlimitedPrepare = loader.getBoolean("UnlimitedPrepare", false);
MaxBlocks = loader.getInt("MaxBlocks", 0);
MaxDispenserItems = loader.getInt("MaxDispenserItems", 128);
MaxBlastResistance = loader.getDouble("MaxBlastResistance", Double.MAX_VALUE);
MaxDesignBlastResistance = loader.getDouble("MaxDesignBlastResistance", MaxBlastResistance);
MaxDesignBlastResistance = loader.getDouble("MaxDesignBlastResistance", Double.MAX_VALUE);
Map<Set<M>, Integer> Limited = new HashMap<>();
for (Map<?, ?> entry : loader.getMapList("Limited")) {
@@ -696,9 +690,6 @@ public final class GameModeConfig<M, W> {
Limited.put(Collections.singleton((M) material), 0);
});
this.Limited = Collections.unmodifiableMap(Limited);
this.ReplacementsWithoutBlockUpdates = loader.getMap("ReplacementsWithoutBlockUpdates", loader.materialMapper, loader.materialMapper);
this.ReplacementsWithBlockUpdates = loader.getMap("ReplacementsWithBlockUpdates", loader.materialMapper, loader.materialMapper);
}
@ToString

View File

@@ -20,6 +20,7 @@
package de.steamwar.sql;
import de.steamwar.ImplementationProvider;
import de.steamwar.ServerInfo;
import java.io.File;
import java.util.Collections;
@@ -40,4 +41,6 @@ public interface SQLWrapper<M> {
}
void additionalExceptionMetadata(StringBuilder builder);
ServerInfo getServerInfo();
}

View File

@@ -36,6 +36,8 @@ object ExceptionTable: IntIdTable("Exception") {
class SWException {
companion object {
private val exceptionCache = HashSet<String>()
val cwd = System.getProperty("user.dir")
val serverName = File(cwd).name
@@ -43,7 +45,9 @@ class SWException {
fun init() = Unit
@JvmStatic
fun log(message: String, stacktrace: String) = useDb {
fun log(message: String, stacktrace: String) = if (exceptionCache.contains(stacktrace)) Unit else useDb {
exceptionCache.add(stacktrace)
ExceptionTable.insert {
it[ExceptionTable.server] = serverName
it[ExceptionTable.message] = generateMessage(message)

View File

@@ -186,10 +186,10 @@ class SteamwarUser(id: EntityID<Int>): IntEntity(id) {
val salt = ByteArray(16)
random.nextBytes(salt)
val saltString = Base64.getEncoder().encodeToString(salt)
val saltString = Base64.getEncoder().encode(salt)
val hash = generateHash(value, salt)
val hashString = Base64.getEncoder().encodeToString(hash)
val hashString = Base64.getEncoder().encode(hash)
useDb {
passwordInternal = "$hashString:$saltString"

Some files were not shown because too many files have changed in this diff Show More