Add PathRegion, PathArea and PathRegionData

Add VariantSelector
Add DynamicRegionCommand
Fix RegionData constructor
This commit is contained in:
2026-03-02 11:58:51 +01:00
parent 5795476e23
commit c3319a53d2
19 changed files with 664 additions and 70 deletions
@@ -24,6 +24,7 @@ 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;
@@ -82,6 +83,9 @@ 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() {
@@ -41,7 +41,7 @@ public abstract class RegionData {
protected RegionData(RegionDataStore store) {
this.store = store;
initialize();
store.loadRegion();
store.loadRegion(this);
}
public final void setStore(RegionDataStore store) {
@@ -21,7 +21,7 @@ package de.steamwar.bausystem.region;
public interface RegionDataStore {
void saveRegion();
void loadRegion();
void loadRegion(RegionData regionData);
default void deleteRegion() {
}
}
@@ -32,6 +32,7 @@ java {
dependencies {
compileOnly(libs.classindex)
annotationProcessor(libs.classindex)
compileOnly(project(":BauSystem:BauSystem_Main", "default"))
compileOnly(project(":SpigotCore", "default"))
@@ -0,0 +1,66 @@
/*
* 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.features.region.RegionCommand;
import de.steamwar.bausystem.region.dynamic.DynamicRegion;
import de.steamwar.bausystem.region.dynamic.Tile;
import de.steamwar.bausystem.region.dynamic.path.PathRegion;
import de.steamwar.bausystem.region.dynamic.path.PathRegionData;
import de.steamwar.bausystem.utils.PasteBuilder;
import de.steamwar.command.AbstractSWCommand;
import de.steamwar.command.SWCommand;
import org.bukkit.entity.Player;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
@AbstractSWCommand.PartOf(RegionCommand.class)
public class DynamicRegionCommand extends SWCommand {
public DynamicRegionCommand() {
super("");
}
@Register({"dynamic", "place"})
public void placeRegion(Player player) {
Region region = DynamicRegionSystem.INSTANCE.get(player.getLocation());
if (!region.getType().isGlobal()) return;
Optional<Tile> optionalTile = Tile.fromLocation(player.getLocation());
if (optionalTile.isEmpty()) return;
Tile tile = optionalTile.get();
// TODO: Replace!
PathRegion dynamicRegion = new PathRegion(UUID.randomUUID(), tile.getMinX(), tile.getMinZ());
dynamicRegion.setRegionData(new PathRegionData(dynamicRegion));
dynamicRegion.getArea().reset(new PasteBuilder(new PasteBuilder.FileProvider(dynamicRegion.getArea().getResetFile())), false);
DynamicRegionSystem.INSTANCE.getNeighbours(dynamicRegion).collect(Collectors.toList())
.forEach(r -> r.update(dynamicRegion));
}
@Register({"dynamic", "delete"})
public void deleteRegion(Player player) {
Region region = DynamicRegionSystem.INSTANCE.get(player.getLocation());
if (region.getType().isCannotDelete()) return;
((DynamicRegion) region).delete();
}
}
@@ -28,7 +28,8 @@ import de.steamwar.bausystem.region.dynamic.global.GlobalRegion;
import lombok.NonNull;
import org.bukkit.Location;
import java.io.*;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -55,7 +56,7 @@ public class DynamicRegionSystem implements RegionSystem {
public void remove(DynamicRegion region) {
regionCache.clear();
regionMap.remove(region.getId());
regionMap.remove(region.getID());
regionTypeMap.getOrDefault(region.getType(), Collections.emptySet()).remove(region);
}
@@ -84,6 +85,7 @@ public class DynamicRegionSystem implements RegionSystem {
.collect(Collectors.toUnmodifiableMap(entry -> entry.getValue().identifier(), Map.Entry::getKey));
DynamicRegionRepository.loadRegions();
new DynamicRegionCommand();
}
@Override
@@ -151,8 +153,10 @@ public class DynamicRegionSystem implements RegionSystem {
return neighbours.stream();
}
public Stream<Region> getNeighbours(Region region) {
return getNeighbours(region, false, true, regionMap.values());
public Stream<DynamicRegion> getNeighbours(Region region) {
return getNeighbours(region, false, true, regionMap.values())
.filter(DynamicRegion.class::isInstance)
.map(DynamicRegion.class::cast);
}
public Stream<Region> getConnectedRegions(Region region) {
@@ -19,17 +19,24 @@
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.regions.CuboidRegion;
import com.sk89q.worldedit.world.block.BlockTypes;
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 lombok.Getter;
import lombok.NonNull;
import org.bukkit.Bukkit;
import java.util.UUID;
import java.util.stream.Collectors;
public abstract class DynamicRegion implements Region {
@Getter
protected final UUID id;
protected final int minX;
protected final int minZ;
@@ -42,20 +49,42 @@ public abstract class DynamicRegion implements Region {
this.minX = minX;
this.minZ = minZ;
DynamicRegionSystem.INSTANCE.add(this);
// TODO: Implement further
}
public void update(DynamicRegion updateFrom) {
// TODO: Implement further
}
public void setRegionData(@NonNull RegionData regionData) {
regionData.setStore(this);
this.regionData = regionData;
regionData.setStore(this);
}
public void delete() {
if (getType().isCannotDelete()) return;
DynamicRegionSystem.INSTANCE.remove(this);
// TODO: Implement!
DynamicRegionRepository.deleteRegion(this);
Point minPoint = getArea().getMinPoint(false);
Point maxPoint = getArea().getMaxPoint(false);
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();
DynamicRegionSystem.INSTANCE.getNeighbours(this).collect(Collectors.toList())
.forEach(r -> r.update(this));
}
@Override
public @NonNull UUID getID() {
return id;
}
}
@@ -35,7 +35,6 @@ import org.bukkit.Location;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Parameter;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -156,48 +155,22 @@ public class DynamicRegionRepository {
continue;
}
Class<? extends RegionData> regionDataClass = constructorData.regionDataClass();
Constructor<? extends RegionData> regionDataConstructor = null;
for (Constructor<?> constructor : regionDataClass.getConstructors()) {
if (constructor.getParameters().length != 1) continue;
Parameter parameter = constructor.getParameters()[0];
if (parameter.getType().isAssignableFrom(regionClass)) {
regionDataConstructor = (Constructor<? extends RegionData>) constructor;
break;
}
}
if (regionDataConstructor == null) {
RegionSystem.LOGGER.log(Level.SEVERE, "Failed to read region metadata file (region data constructor not found)");
continue;
}
DynamicRegion dynamicRegion;
try {
dynamicRegion = regionConstructor.newInstance(regionUUID, minTileLocation.getBlockX(), minTileLocation.getBlockZ());
regionConstructor.newInstance(regionUUID, minTileLocation.getBlockX(), minTileLocation.getBlockZ());
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException |
InvocationTargetException e) {
RegionSystem.LOGGER.log(Level.SEVERE, "Failed to read region metadata file (invalid data)");
continue;
}
RegionData regionData;
try {
regionData = regionDataConstructor.newInstance(dynamicRegion);
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
RegionSystem.LOGGER.log(Level.SEVERE, "Failed to instantiate region data (invalid data)");
continue;
}
dynamicRegion.setRegionData(regionData);
}
}
public static void loadRegionData(Region region) {
public static void loadRegionData(Region region, RegionData regionData) {
File regionDirectory = new File(REGION_DATA_FOLDER, region.getID().toString());
if (!regionDirectory.exists()) return;
loadRegionData(regionDirectory, region.getRegionData());
loadRegionData(regionDirectory, regionData);
}
public static void loadRegionData(Region region, RegionBackups.Backup backup) {
public static void loadRegionData(Region region, RegionBackups.Backup backup, RegionData regionData) {
File regionDirectory = new File(REGION_DATA_FOLDER, region.getID().toString());
if (!regionDirectory.exists()) return;
File backupsDirectory = new File(regionDirectory, BACKUPS_DIR_NAME);
@@ -206,7 +179,7 @@ public class DynamicRegionRepository {
if (!backupsTypeDirectory.exists()) return;
File backupDirectory = new File(backupsTypeDirectory, backup.getName());
if (!backupDirectory.exists()) return;
loadRegionData(backupDirectory, backup.getRegionData());
loadRegionData(backupDirectory, regionData);
}
private static void loadRegionData(File regionDirectory, RegionData regionData) {
@@ -281,11 +254,10 @@ public class DynamicRegionRepository {
@SneakyThrows
private static void writeFlagsFile(File regionDirectory, RegionData regionData) {
@Cleanup
JsonWriter jsonWriter = new JsonWriter(new FileWriter(new File(regionDirectory, FLAG_FILE_NAME)));
jsonWriter.setIndent(" ");
jsonWriter.beginObject();
{
jsonWriter.name(FLAGS_KEY);
jsonWriter.beginObject();
for (Flag<?> flag : Flag.getFlags()) {
@@ -294,14 +266,14 @@ public class DynamicRegionRepository {
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) {
@@ -19,7 +19,6 @@
package de.steamwar.bausystem.region.dynamic;
import de.steamwar.bausystem.region.RegionData;
import org.atteo.classindex.IndexAnnotated;
import java.lang.annotation.ElementType;
@@ -35,5 +34,4 @@ public @interface RegionConstructorData {
int widthX();
int widthZ();
boolean placeable() default true;
Class<? extends RegionData> regionDataClass();
}
@@ -28,7 +28,7 @@ import java.util.Optional;
@Getter
public class Tile {
public static final int tileSize = 19;
public static final int tileSize = 21;
public static final int tileOffset = tileSize / 2;
public static final int maxTile = 1023;
public static final int minTile = -maxTile;
@@ -78,6 +78,22 @@ public class Tile {
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));
}
@@ -86,6 +102,22 @@ public class Tile {
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);
}
@@ -0,0 +1,94 @@
/*
* 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.*;
public abstract class VariantSelector {
private static final Random RANDOM = new Random();
public static final VariantSelector EMPTY = new VariantSelector() {
@Override
public Optional<File> select(UUID regionID, int drift) {
return Optional.empty();
}
};
private VariantSelector() {
}
public abstract Optional<File> select(UUID regionID, int drift);
public final VariantSelector or(VariantSelector other) {
if (this == EMPTY) return other;
VariantSelector self = this;
return new VariantSelector() {
@Override
public Optional<File> select(UUID regionID, int drift) {
return self.select(regionID, drift).or(() -> other.select(regionID, drift));
}
};
}
public final VariantSelector atDate(int day, Month month) {
if (this == EMPTY) return this;
VariantSelector self = this;
return new VariantSelector() {
@Override
public Optional<File> select(UUID regionID, int drift) {
LocalDate date = LocalDate.now();
if (date.getDayOfMonth() == day && date.getMonth() == month) {
return self.select(regionID, drift);
} 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(UUID regionID, int drift) {
return Optional.of(file);
}
};
}
Arrays.sort(files, Comparator.comparing(File::getName));
final int filesCount = files.length;
return new VariantSelector() {
@Override
public Optional<File> select(UUID regionID, int drift) {
RANDOM.setSeed(regionID.getLeastSignificantBits() ^ regionID.getMostSignificantBits() ^ drift);
return Optional.of(files[RANDOM.nextInt(filesCount)]);
}
};
}
}
@@ -140,7 +140,7 @@ public class GlobalRegion implements Region {
}
@Override
public void loadRegion() {
DynamicRegionRepository.loadRegionData(this);
public void loadRegion(RegionData regionData) {
DynamicRegionRepository.loadRegionData(this, regionData);
}
}
@@ -21,7 +21,6 @@ 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.ColorMode;
import de.steamwar.bausystem.region.flags.Flag;
import de.steamwar.bausystem.region.flags.ProtectMode;
import de.steamwar.bausystem.region.flags.TNTMode;
@@ -37,13 +36,12 @@ public class GlobalRegionData extends RegionData {
@Override
protected void initialize() {
flagMap.put(Flag.TNT, TNTMode.DENY);
flagMap.put(Flag.COLOR, ColorMode.YELLOW);
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.COLOR, Flag.PROTECT)) {
if (flag.oneOf(Flag.PROTECT)) {
return RegionFlagPolicy.READ_ONLY;
}
if (flag.oneOf(Flag.ITEMS) && Core.getVersion() >= 20) {
@@ -0,0 +1,238 @@
/*
* 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.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 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.Tile;
import de.steamwar.bausystem.region.dynamic.VariantSelector;
import de.steamwar.bausystem.utils.FlatteningWrapper;
import de.steamwar.bausystem.utils.PasteBuilder;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.bukkit.Bukkit;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.Optional;
import java.util.UUID;
public class PathArea implements Region.Area {
private static final File PATH_DIR = new File(Bukkit.getWorlds().get(0).getWorldFolder(), "sections/path");
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"));
@RequiredArgsConstructor
private enum Side {
North(0, -1, 7, 0, 0),
South(0, 1, 7, 14, 180),
West(-1, 0, 0, 7, 90),
East(1, 0, 14, 7, 270),
;
private final int tileOffsetX;
private final int tileOffsetZ;
private final int pasteOffsetX;
private final int pasteOffsetZ;
private final int rotate;
}
@RequiredArgsConstructor
private enum Corner {
NorthEast(Side.North, Side.East, 14, 0, 0, -90),
NorthWest(Side.North, Side.West, 0, 0, 0, 90),
SouthEast(Side.South, Side.East, 14, 14, 180, 90),
SouthWest(Side.South, Side.West, 0, 14, 180, -90),
;
private final Side side1;
private final Side side2;
private final int pasteOffsetX;
private final int pasteOffsetZ;
private final int rotate;
private final int rotateCorrection;
}
private final UUID regionIdentifier;
private final Tile tile;
private final Point minPoint;
private final Point maxPoint;
private final Point copyPoint;
public PathArea(Tile tile, PathRegion region) {
this.regionIdentifier = region.getID();
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());
}
@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 @Nullable File getResetFile() {
return null; // I know what I do!
}
@Override
public void reset(PasteBuilder pasteBuilder, boolean extension) {
if (surroundedByPath()) {
// TODO: Implement Garden case!
}
File resetFile = CENTER_NORMAL.select(regionIdentifier, 0).orElse(null);
if (resetFile != null) {
paste(resetFile, minPoint.add(7, 0, 7), 0);
}
for (Side side : Side.values()) {
RegionType.ConnectionType connectionType = getConnectionType(side, null);
VariantSelector selector = switch (connectionType) {
case Path -> CENTER_NORMAL;
case Water -> null;
case Closed -> null;
case Global -> SIDE_GLOBAL;
};
if (selector == null) {
continue;
}
resetFile = selector.select(regionIdentifier, side.ordinal() + side.rotate).orElse(null);
if (resetFile == null) {
continue;
}
paste(resetFile, minPoint.add(side.pasteOffsetX, 0, side.pasteOffsetZ), side.rotate);
}
for (Corner corner : Corner.values()) {
RegionType.ConnectionType connectionType1 = getConnectionType(corner.side1, null);
RegionType.ConnectionType connectionType2 = getConnectionType(corner.side2, null);
RegionType.ConnectionType connectionType3 = getConnectionType(corner.side1, corner.side2);
VariantSelector selector = null;
int rotate = corner.rotate;
if (connectionType1 == RegionType.ConnectionType.Path && connectionType2 == connectionType3) {
selector = switch (connectionType2) {
case Global -> SIDE_GLOBAL;
case Closed -> null;
case Water -> null;
case Path -> null;
};
rotate += corner.rotateCorrection;
}
if (connectionType1 == RegionType.ConnectionType.Global && connectionType2 == connectionType3) {
selector = switch (connectionType2) {
case Global -> null;
case Closed -> null;
case Water -> null;
case Path -> SIDE_GLOBAL;
};
}
if (connectionType2 == RegionType.ConnectionType.Path && connectionType1 == connectionType3) {
selector = switch (connectionType1) {
case Global -> SIDE_GLOBAL;
case Closed -> null;
case Water -> null;
case Path -> null;
};
}
if (connectionType2 == RegionType.ConnectionType.Global && connectionType1 == connectionType3) {
selector = switch (connectionType1) {
case Global -> null;
case Closed -> null;
case Water -> null;
case Path -> SIDE_GLOBAL;
};
rotate += corner.rotateCorrection;
}
if (connectionType1 == RegionType.ConnectionType.Path && connectionType1 == connectionType2 && connectionType1 == connectionType3) {
selector = CENTER_NORMAL;
rotate = 0;
}
if (selector == null) {
continue;
}
resetFile = selector.select(regionIdentifier, corner.side1.ordinal() * corner.side2.ordinal() + corner.side1.rotate * corner.side2.rotate).orElse(null);
if (resetFile == null) {
continue;
}
paste(resetFile, minPoint.add(corner.pasteOffsetX, 0, corner.pasteOffsetZ), rotate);
}
}
private boolean surroundedByPath() {
for (int x = -1; x <= 1; x++) {
for (int z = -1; z <= 1; z++) {
if (x == 0 && z == 0) continue;
Optional<Tile> optionalTile = this.tile.add(x, z);
if (optionalTile.isEmpty()) {
return false;
}
if (DynamicRegionSystem.INSTANCE.get(optionalTile.get().getCenterLocation()).getType() != RegionType.PATH) {
return false;
}
}
}
return true;
}
private RegionType.ConnectionType getConnectionType(Side side, Side optionalSide) {
Optional<Tile> optionalTile = this.tile.add(side.tileOffsetX, side.tileOffsetZ);
if (optionalSide != null) {
optionalTile = optionalTile.flatMap(tile -> tile.add(optionalSide.tileOffsetX, optionalSide.tileOffsetZ));
}
return optionalTile.map(tile -> DynamicRegionSystem.INSTANCE.get(tile.getCenterLocation()).getType().getConnectionType())
.orElse(RegionType.ConnectionType.Global);
}
private void paste(File file, Point minPoint, int rotate) {
Clipboard clipboard = FlatteningWrapper.impl.loadSchematic(file);
BlockVector3 offset = clipboard.getRegion().getMinimumPoint().subtract(clipboard.getOrigin());
BlockVector3 to = minPoint.toBlockVector3().subtract(offset);
clipboard.paste(BukkitAdapter.adapt(Bukkit.getWorlds().get(0)), to, false, true, new AffineTransform().rotateY(rotate));
}
}
@@ -0,0 +1,101 @@
/*
* 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.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.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.sql.GameModeConfig;
import lombok.NonNull;
import org.bukkit.Material;
import java.util.UUID;
@RegionConstructorData(
identifier = "path",
widthX = Tile.tileSize,
widthZ = Tile.tileSize
)
public class PathRegion extends DynamicRegion {
private final PathArea area;
public PathRegion(UUID id, int minX, int minZ) {
super(id, minX, minZ);
area = new PathArea(Tile.fromXZ(minX, minZ).orElseThrow(), this);
regionData = new PathRegionData(this);
}
@Override
public void update(DynamicRegion updateFrom) {
// TODO: Improve
area.reset(null, false);
}
@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 saveRegion() {
DynamicRegionRepository.saveRegion(this);
}
@Override
public void loadRegion(RegionData regionData) {
DynamicRegionRepository.loadRegionData(this, regionData);
}
}
@@ -0,0 +1,57 @@
/*
* 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;
}
}
@@ -147,7 +147,7 @@ public final class FixedGlobalRegion implements Region {
}
@Override
public void loadRegion() {
public void loadRegion(RegionData regionData) {
FixedRegionDataUtils.loadRegionData("global", FLAG_STORAGE);
}
}
@@ -143,7 +143,7 @@ public class FixedRegion implements Region {
}
@Override
public void loadRegion() {
public void loadRegion(RegionData regionData) {
}
@Override
@@ -407,7 +407,7 @@ public class FixedRegion implements Region {
}
@Override
public void loadRegion() {
public void loadRegion(RegionData regionData) {
FixedRegionDataUtils.loadRegionData(name, flagStorage);
}
}
+1 -1
View File
@@ -166,7 +166,7 @@ include(
"BauSystem",
"BauSystem:BauSystem_Main",
"BauSystem:BauSystem_RegionDynamic",
"BauSystem:BauSystem_RegionDynamic2",
// "BauSystem:BauSystem_RegionDynamic2",
"BauSystem:BauSystem_RegionFixed"
)