forked from SteamWar/SteamWar
Add PathRegion, PathArea and PathRegionData
Add VariantSelector Add DynamicRegionCommand Fix RegionData constructor
This commit is contained in:
@@ -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"))
|
||||
|
||||
+66
@@ -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();
|
||||
}
|
||||
}
|
||||
+8
-4
@@ -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) {
|
||||
|
||||
+33
-4
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+20
-48
@@ -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,27 +254,26 @@ 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()) {
|
||||
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.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) {
|
||||
|
||||
-2
@@ -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();
|
||||
}
|
||||
|
||||
+33
-1
@@ -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);
|
||||
}
|
||||
|
||||
+94
@@ -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)]);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-3
@@ -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) {
|
||||
|
||||
+238
@@ -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));
|
||||
}
|
||||
}
|
||||
+101
@@ -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);
|
||||
}
|
||||
}
|
||||
+57
@@ -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;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -147,7 +147,7 @@ public final class FixedGlobalRegion implements Region {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadRegion() {
|
||||
public void loadRegion(RegionData regionData) {
|
||||
FixedRegionDataUtils.loadRegionData("global", FLAG_STORAGE);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user