forked from SteamWar/SteamWar
Improve DynamicRegion
This commit is contained in:
+3
-2
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"region_identifier": "SpawnRegion",
|
"region_identifier": "SpawnRegion",
|
||||||
"tile_x": 0,
|
"tiles": [
|
||||||
"tile_z": 0
|
{ "tile_x": 0, "tile_z": 0 }
|
||||||
|
]
|
||||||
}
|
}
|
||||||
+1
-1
@@ -306,7 +306,7 @@ public class DynamicRegionVisualizer implements SWPlayer.Component, Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void place() {
|
private void place() {
|
||||||
DynamicRegion dynamicRegion = DynamicRegionRepository.constructRegion(regionType, UUID.randomUUID(), sourceTile.getMinX(), sourceTile.getMinZ());
|
DynamicRegion dynamicRegion = DynamicRegionRepository.constructRegion(regionType, sourceTile);
|
||||||
if (dynamicRegion == null) {
|
if (dynamicRegion == null) {
|
||||||
// TODO: Give error to user
|
// TODO: Give error to user
|
||||||
return;
|
return;
|
||||||
|
|||||||
+105
-14
@@ -19,6 +19,10 @@
|
|||||||
|
|
||||||
package de.steamwar.bausystem.region.dynamic;
|
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.DynamicRegionSystem;
|
||||||
import de.steamwar.bausystem.region.Point;
|
import de.steamwar.bausystem.region.Point;
|
||||||
import de.steamwar.bausystem.region.Region;
|
import de.steamwar.bausystem.region.Region;
|
||||||
@@ -29,34 +33,121 @@ import lombok.Getter;
|
|||||||
import lombok.NonNull;
|
import lombok.NonNull;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.*;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public abstract class DynamicRegion implements Region {
|
public abstract class DynamicRegion implements Region {
|
||||||
|
|
||||||
protected final UUID id;
|
protected final UUID id;
|
||||||
protected final int minX;
|
|
||||||
protected final int minZ;
|
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
protected RegionData regionData = null;
|
protected RegionData regionData = null;
|
||||||
|
|
||||||
protected DynamicRegion(UUID id, int minX, int minZ) {
|
/**
|
||||||
this.id = id;
|
* This Constructor should be used if a Region is placed newly onto the world!
|
||||||
this.minX = minX;
|
*
|
||||||
this.minZ = minZ;
|
* @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 method must be called from the super constructor in a way that everything is already initialized.
|
* 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 final void finishInit() {
|
protected DynamicRegion(UUID id, JsonArray tileData) {
|
||||||
DynamicRegionSystem.INSTANCE.add(this);
|
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();
|
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() {
|
public final void updateNeighbours() {
|
||||||
List<Pair<PathRegion, NeighbourDirection>> list = DynamicRegionSystem.INSTANCE.getNeighbours(this)
|
List<Pair<PathRegion, NeighbourDirection>> list = DynamicRegionSystem.INSTANCE.getNeighbours(this)
|
||||||
.filter(data -> data.getKey() instanceof PathRegion)
|
.filter(data -> data.getKey() instanceof PathRegion)
|
||||||
@@ -74,7 +165,7 @@ public abstract class DynamicRegion implements Region {
|
|||||||
// Updating world state for all neighbouring PathRegions
|
// Updating world state for all neighbouring PathRegions
|
||||||
list.forEach(data -> {
|
list.forEach(data -> {
|
||||||
if (needsFullReset.contains(data.getKey().getID())) {
|
if (needsFullReset.contains(data.getKey().getID())) {
|
||||||
data.getKey().getArea().reset(null,null, false); // TODO: Implement!
|
data.getKey().getArea().reset(null, null, false); // TODO: Implement!
|
||||||
} else {
|
} else {
|
||||||
data.getKey().update(this, data.getValue().opposite());
|
data.getKey().update(this, data.getValue().opposite());
|
||||||
}
|
}
|
||||||
|
|||||||
+37
-28
@@ -19,10 +19,7 @@
|
|||||||
|
|
||||||
package de.steamwar.bausystem.region.dynamic;
|
package de.steamwar.bausystem.region.dynamic;
|
||||||
|
|
||||||
import com.google.gson.JsonIOException;
|
import com.google.gson.*;
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
import com.google.gson.JsonParser;
|
|
||||||
import com.google.gson.JsonSyntaxException;
|
|
||||||
import com.google.gson.stream.JsonWriter;
|
import com.google.gson.stream.JsonWriter;
|
||||||
import de.steamwar.bausystem.region.*;
|
import de.steamwar.bausystem.region.*;
|
||||||
import de.steamwar.bausystem.region.dynamic.path.PathRegion;
|
import de.steamwar.bausystem.region.dynamic.path.PathRegion;
|
||||||
@@ -31,7 +28,6 @@ import lombok.Cleanup;
|
|||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import lombok.experimental.UtilityClass;
|
import lombok.experimental.UtilityClass;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Location;
|
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
@@ -71,8 +67,7 @@ public class DynamicRegionRepository {
|
|||||||
|
|
||||||
public static final String META_FILE_NAME = "meta.json";
|
public static final String META_FILE_NAME = "meta.json";
|
||||||
public static final String META_FILE_REGION_IDENTIFIER = "region_identifier";
|
public static final String META_FILE_REGION_IDENTIFIER = "region_identifier";
|
||||||
public static final String META_FILE_TILE_X = "tile_x";
|
public static final String META_FILES_TILES = "tiles";
|
||||||
public static final String META_FILE_TILE_Z = "tile_z";
|
|
||||||
|
|
||||||
public static final String FLAG_FILE_NAME = "flags.json";
|
public static final String FLAG_FILE_NAME = "flags.json";
|
||||||
public static final String BACKUPS_DIR_NAME = "backups";
|
public static final String BACKUPS_DIR_NAME = "backups";
|
||||||
@@ -119,12 +114,8 @@ public class DynamicRegionRepository {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tileX;
|
|
||||||
int tileZ;
|
|
||||||
String identifier;
|
String identifier;
|
||||||
try {
|
try {
|
||||||
tileX = metaData.getAsJsonPrimitive(META_FILE_TILE_X).getAsInt();
|
|
||||||
tileZ = metaData.getAsJsonPrimitive(META_FILE_TILE_Z).getAsInt();
|
|
||||||
identifier = metaData.getAsJsonPrimitive(META_FILE_REGION_IDENTIFIER).getAsString();
|
identifier = metaData.getAsJsonPrimitive(META_FILE_REGION_IDENTIFIER).getAsString();
|
||||||
} catch (ClassCastException | NumberFormatException e) {
|
} catch (ClassCastException | NumberFormatException e) {
|
||||||
RegionSystem.LOGGER.log(Level.SEVERE, "Failed to read region metadata file (invalid json)");
|
RegionSystem.LOGGER.log(Level.SEVERE, "Failed to read region metadata file (invalid json)");
|
||||||
@@ -138,13 +129,12 @@ public class DynamicRegionRepository {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Tile tile = Tile.fromTile(tileX, tileZ).orElse(null);
|
JsonArray tileData = metaData.getAsJsonArray(META_FILES_TILES);
|
||||||
if (tile == null) {
|
if (tileData == null) {
|
||||||
RegionSystem.LOGGER.log(Level.SEVERE, "Failed to read region metadata file (tile is no longer in bounds)");
|
RegionSystem.LOGGER.log(Level.SEVERE, "Failed to read region metadata file (tile is no longer in bounds)");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Location minTileLocation = tile.getMinLocation();
|
constructRegion(regionClass, regionUUID, tileData);
|
||||||
constructRegion(regionClass, regionUUID, minTileLocation.getBlockX(), minTileLocation.getBlockZ());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate Garden State for all PathRegions
|
// Calculate Garden State for all PathRegions
|
||||||
@@ -154,17 +144,35 @@ public class DynamicRegionRepository {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static DynamicRegion constructRegion(Class<? extends DynamicRegion> clazz, UUID uuid, int minX, int minZ) {
|
public static DynamicRegion constructRegion(Class<? extends DynamicRegion> clazz, Tile tile) {
|
||||||
Constructor<? extends DynamicRegion> regionConstructor;
|
Constructor<? extends DynamicRegion> regionConstructor;
|
||||||
try {
|
try {
|
||||||
regionConstructor = clazz.getConstructor(UUID.class, int.class, int.class);
|
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) {
|
} catch (NoSuchMethodException e) {
|
||||||
RegionSystem.LOGGER.log(Level.SEVERE, "Failed to read region metadata file (region constructor not found)");
|
RegionSystem.LOGGER.log(Level.SEVERE, "Failed to read region metadata file (region constructor not found)");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return regionConstructor.newInstance(uuid, minX, minZ);
|
return regionConstructor.newInstance(uuid, tileData);
|
||||||
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException |
|
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException |
|
||||||
InvocationTargetException e) {
|
InvocationTargetException e) {
|
||||||
RegionSystem.LOGGER.log(Level.SEVERE, "Failed to read region metadata file (invalid data)");
|
RegionSystem.LOGGER.log(Level.SEVERE, "Failed to read region metadata file (invalid data)");
|
||||||
@@ -236,17 +244,18 @@ public class DynamicRegionRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void saveRegion(Region region) {
|
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());
|
File regionDirectory = new File(REGION_DATA_FOLDER, region.getID().toString());
|
||||||
if (!regionDirectory.exists()) {
|
if (!regionDirectory.exists()) {
|
||||||
regionDirectory.mkdir();
|
regionDirectory.mkdir();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!region.getType().isGlobal()) {
|
if (region instanceof DynamicRegion dynamicRegion) {
|
||||||
RegionConstructorData constructorData = DynamicRegionSystem.constructorDataMap.get(region.getClass());
|
RegionConstructorData constructorData = DynamicRegionSystem.constructorDataMap.get(region.getClass());
|
||||||
Point point = region.getArea().getMinPoint(false);
|
writeMetaFile(regionDirectory, constructorData, dynamicRegion);
|
||||||
Tile tile = Tile.fromPoint(point).get();
|
|
||||||
|
|
||||||
writeMetaFile(regionDirectory, constructorData, tile);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
writeFlagsFile(regionDirectory, region.getRegionData());
|
writeFlagsFile(regionDirectory, region.getRegionData());
|
||||||
@@ -261,17 +270,17 @@ public class DynamicRegionRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
private static void writeMetaFile(File regionDirectory, RegionConstructorData constructorData, Tile tile) {
|
private static void writeMetaFile(File regionDirectory, RegionConstructorData constructorData, DynamicRegion dynamicRegion) {
|
||||||
@Cleanup
|
@Cleanup
|
||||||
JsonWriter jsonWriter = new JsonWriter(new FileWriter(new File(regionDirectory, META_FILE_NAME)));
|
JsonWriter jsonWriter = new JsonWriter(new FileWriter(new File(regionDirectory, META_FILE_NAME)));
|
||||||
jsonWriter.setIndent(" ");
|
jsonWriter.setIndent(" ");
|
||||||
jsonWriter.beginObject();
|
jsonWriter.beginObject();
|
||||||
jsonWriter.name(META_FILE_REGION_IDENTIFIER);
|
jsonWriter.name(META_FILE_REGION_IDENTIFIER);
|
||||||
jsonWriter.value(constructorData.identifier());
|
jsonWriter.value(constructorData.identifier());
|
||||||
jsonWriter.name(META_FILE_TILE_X);
|
jsonWriter.name(META_FILES_TILES);
|
||||||
jsonWriter.value(tile.getTileX());
|
jsonWriter.beginArray();
|
||||||
jsonWriter.name(META_FILE_TILE_Z);
|
dynamicRegion.writeData(jsonWriter);
|
||||||
jsonWriter.value(tile.getTileZ());
|
jsonWriter.endArray();
|
||||||
jsonWriter.endObject();
|
jsonWriter.endObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+25
-12
@@ -29,31 +29,44 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
public abstract class MultiTileArea implements Region.Area {
|
public abstract class MultiTileArea implements Region.Area {
|
||||||
|
|
||||||
|
private final Logger LOGGER = Logger.getLogger(this.getClass().getTypeName());
|
||||||
|
|
||||||
protected final Set<Tile> tiles = new HashSet<>();
|
protected final Set<Tile> tiles = new HashSet<>();
|
||||||
|
|
||||||
protected MultiTileArea() {
|
protected MultiTileArea() {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected MultiTileArea(Set<Tile> initialTiles) {
|
protected MultiTileArea(List<Pair<Tile, Tile>> list) {
|
||||||
tiles.addAll(initialTiles);
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean addTile(Tile tile) {
|
public void addTile(Tile tile) {
|
||||||
boolean result = tiles.add(tile);
|
tiles.add(tile);
|
||||||
quantize();
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean removeTile(Tile tile) {
|
public boolean removeTile(Tile tile) {
|
||||||
boolean result = tiles.remove(tile);
|
tiles.remove(tile);
|
||||||
quantize();
|
return tiles.isEmpty();
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void quantize() {
|
public List<Pair<Tile, Tile>> quantize() {
|
||||||
long time = System.currentTimeMillis();
|
long time = System.currentTimeMillis();
|
||||||
Quantizer quantizer = new Quantizer(tiles);
|
Quantizer quantizer = new Quantizer(tiles);
|
||||||
List<Pair<Tile, Tile>> results = new ArrayList<>();
|
List<Pair<Tile, Tile>> results = new ArrayList<>();
|
||||||
@@ -62,9 +75,9 @@ public abstract class MultiTileArea implements Region.Area {
|
|||||||
if (pair == null) break;
|
if (pair == null) break;
|
||||||
results.add(pair);
|
results.add(pair);
|
||||||
}
|
}
|
||||||
// [{"min_tile_x":0,"min_tile_z":0,"max_tile_x":0,"max_tile_z":0}]
|
|
||||||
time = System.currentTimeMillis() - time;
|
time = System.currentTimeMillis() - time;
|
||||||
System.out.println(tiles.size() + ": " + results.size() + " in " + time + "ms");
|
LOGGER.info(tiles.size() + ": " + results.size() + " in " + time + "ms");
|
||||||
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+203
@@ -0,0 +1,203 @@
|
|||||||
|
/*
|
||||||
|
* 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_special;
|
||||||
|
|
||||||
|
import de.steamwar.bausystem.BauSystem;
|
||||||
|
import de.steamwar.bausystem.region.DynamicRegionSystem;
|
||||||
|
import de.steamwar.bausystem.region.Point;
|
||||||
|
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.region.dynamic.path.*;
|
||||||
|
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 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+137
@@ -0,0 +1,137 @@
|
|||||||
|
/*
|
||||||
|
* 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_special;
|
||||||
|
|
||||||
|
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.path.PathCorner;
|
||||||
|
import de.steamwar.bausystem.region.dynamic.path.PathSide;
|
||||||
|
import de.steamwar.sql.GameModeConfig;
|
||||||
|
import lombok.NonNull;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+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_special;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user