Remove stability from VariantSelector

This commit is contained in:
2026-03-22 16:05:25 +01:00
parent 268f450ee8
commit 9006a34a3b
15 changed files with 73 additions and 39 deletions
@@ -22,6 +22,7 @@ package de.steamwar.bausystem.region;
import lombok.Getter; import lombok.Getter;
import lombok.NonNull; import lombok.NonNull;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.bukkit.Location;
import javax.annotation.CheckReturnValue; import javax.annotation.CheckReturnValue;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -78,9 +79,9 @@ public interface RegionBackups {
public final void load(RegionData regionData) { public final void load(RegionData regionData) {
} }
@SuppressWarnings("java:S3038") // This forces everybody to implement 'deleteRegion' for Backups! @SuppressWarnings("java:S3038") // This forces everybody to implement 'delete' for Backups!
@Override @Override
public abstract void delete(); public abstract void delete(Location location);
} }
@CheckReturnValue @CheckReturnValue
@@ -19,9 +19,11 @@
package de.steamwar.bausystem.region; package de.steamwar.bausystem.region;
import org.bukkit.Location;
public interface RegionDataStore { public interface RegionDataStore {
void save(); void save();
void load(RegionData regionData); void load(RegionData regionData);
default void delete() { default void delete(Location location) {
} }
} }
@@ -174,7 +174,7 @@ public class DynamicRegionVisualizer implements SWPlayer.Component, Listener {
player.closeInventory(); player.closeInventory();
Region region = DynamicRegionSystem.INSTANCE.get(tile); Region region = DynamicRegionSystem.INSTANCE.get(tile);
Set<Tile> tiles = DynamicRegionSystem.INSTANCE.getTilesOfRegion(region); Set<Tile> tiles = DynamicRegionSystem.INSTANCE.getTilesOfRegion(region);
region.delete(); region.delete(tile.getCenterLocation());
SWPlayer.allWithSingleComponent(DynamicRegionVisualizer.class) SWPlayer.allWithSingleComponent(DynamicRegionVisualizer.class)
.forEach(pair -> { .forEach(pair -> {
@@ -27,6 +27,7 @@ import de.steamwar.bausystem.region.dynamic.path.PathRegion;
import de.steamwar.bausystem.shared.Pair; import de.steamwar.bausystem.shared.Pair;
import lombok.Getter; import lombok.Getter;
import lombok.NonNull; import lombok.NonNull;
import org.bukkit.Location;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@@ -98,7 +99,7 @@ public abstract class DynamicRegion implements Region {
} }
@Override @Override
public void delete() { public void delete(Location location) {
if (!getType().isDeletable()) return; if (!getType().isDeletable()) return;
DynamicRegionSystem.INSTANCE.remove(this); DynamicRegionSystem.INSTANCE.remove(this);
DynamicRegionRepository.deleteRegion(this); DynamicRegionRepository.deleteRegion(this);
@@ -32,7 +32,14 @@ import java.util.function.BiConsumer;
public abstract class MultiTileArea implements Region.Area { public abstract class MultiTileArea implements Region.Area {
private final Set<Tile> tiles = new HashSet<>(); protected final Set<Tile> tiles = new HashSet<>();
protected MultiTileArea() {
}
protected MultiTileArea(Set<Tile> initialTiles) {
tiles.addAll(initialTiles);
}
public boolean addTile(Tile tile) { public boolean addTile(Tile tile) {
boolean result = tiles.add(tile); boolean result = tiles.add(tile);
@@ -24,14 +24,17 @@ import lombok.NonNull;
import java.io.File; import java.io.File;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.Month; import java.time.Month;
import java.util.*; import java.util.Arrays;
import java.util.Comparator;
import java.util.Optional;
import java.util.Random;
public abstract class VariantSelector { public abstract class VariantSelector {
private static final Random RANDOM = new Random(); private static final Random RANDOM = new Random();
public static final VariantSelector EMPTY = new VariantSelector() { public static final VariantSelector EMPTY = new VariantSelector() {
@Override @Override
public Optional<File> select(UUID regionID, int drift) { public Optional<File> select() {
return Optional.empty(); return Optional.empty();
} }
}; };
@@ -39,15 +42,15 @@ public abstract class VariantSelector {
private VariantSelector() { private VariantSelector() {
} }
public abstract Optional<File> select(UUID regionID, int drift); public abstract Optional<File> select();
public final VariantSelector or(VariantSelector other) { public final VariantSelector or(VariantSelector other) {
if (this == EMPTY) return other; if (this == EMPTY) return other;
VariantSelector self = this; VariantSelector self = this;
return new VariantSelector() { return new VariantSelector() {
@Override @Override
public Optional<File> select(UUID regionID, int drift) { public Optional<File> select() {
return self.select(regionID, drift).or(() -> other.select(regionID, drift)); return self.select().or(() -> other.select());
} }
}; };
} }
@@ -57,10 +60,10 @@ public abstract class VariantSelector {
VariantSelector self = this; VariantSelector self = this;
return new VariantSelector() { return new VariantSelector() {
@Override @Override
public Optional<File> select(UUID regionID, int drift) { public Optional<File> select() {
LocalDate date = LocalDate.now(); LocalDate date = LocalDate.now();
if (date.getDayOfMonth() == day && date.getMonth() == month) { if (date.getDayOfMonth() == day && date.getMonth() == month) {
return self.select(regionID, drift); return self.select();
} else { } else {
return Optional.empty(); return Optional.empty();
} }
@@ -75,7 +78,7 @@ public abstract class VariantSelector {
final File file = files[0]; final File file = files[0];
return new VariantSelector() { return new VariantSelector() {
@Override @Override
public Optional<File> select(UUID regionID, int drift) { public Optional<File> select() {
return Optional.of(file); return Optional.of(file);
} }
}; };
@@ -85,8 +88,7 @@ public abstract class VariantSelector {
final int filesCount = files.length; final int filesCount = files.length;
return new VariantSelector() { return new VariantSelector() {
@Override @Override
public Optional<File> select(UUID regionID, int drift) { public Optional<File> select() {
RANDOM.setSeed(regionID.getLeastSignificantBits() ^ regionID.getMostSignificantBits() ^ drift);
return Optional.of(files[RANDOM.nextInt(filesCount)]); return Optional.of(files[RANDOM.nextInt(filesCount)]);
} }
}; };
@@ -162,7 +162,7 @@ public class AreaBlock implements Region.Area {
@Override @Override
public void place(Location location, PasteBuilder pasteBuilder, boolean extension) { public void place(Location location, PasteBuilder pasteBuilder, boolean extension) {
EditSession editSession = pasteBuilder EditSession editSession = pasteBuilder
.with(PasteBuilder.ClipboardProvider.file(selector.select(region.getID(), 0).orElse(null))) .with(PasteBuilder.ClipboardProvider.file(selector.select().orElse(null)))
.pastePoint(copyPoint) .pastePoint(copyPoint)
.run(); .run();
region.getHistory() region.getHistory()
@@ -67,7 +67,7 @@ public class AreaTile implements Region.Area {
@Override @Override
public void place(Location location, PasteBuilder pasteBuilder, boolean extension) { public void place(Location location, PasteBuilder pasteBuilder, boolean extension) {
File resetFile = selector.select(regionIdentifier, 0).orElse(null); File resetFile = selector.select().orElse(null);
if (resetFile != null) PasteUtils.paste(resetFile, minPoint, 0); if (resetFile != null) PasteUtils.paste(resetFile, minPoint, 0);
} }
} }
@@ -30,6 +30,7 @@ import de.steamwar.bausystem.region.dynamic.DynamicRegionRepository;
import de.steamwar.bausystem.region.dynamic.PasteUtils; import de.steamwar.bausystem.region.dynamic.PasteUtils;
import lombok.NonNull; import lombok.NonNull;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.io.File; import java.io.File;
@@ -75,7 +76,7 @@ public class PlotRegionBackups implements RegionBackups {
// Cleanup backups if there are too many! // Cleanup backups if there are too many!
backupList.sort(Backup::compareTo); backupList.sort(Backup::compareTo);
while (backupList.size() >= backupType.maxBackups) { while (backupList.size() >= backupType.maxBackups) {
backupList.removeFirst().delete(); backupList.removeFirst().delete(null);
} }
// Create backup and save! // Create backup and save!
@@ -148,7 +149,7 @@ public class PlotRegionBackups implements RegionBackups {
} }
@Override @Override
public void delete() { public void delete(Location location) {
backups.getOrDefault(type, Collections.emptyList()) backups.getOrDefault(type, Collections.emptyList())
.remove(this); .remove(this);
DynamicRegionRepository.deleteBackup(region, this); DynamicRegionRepository.deleteBackup(region, this);
@@ -111,12 +111,6 @@ public class PathArea implements Region.Area {
.Case(Path, Path, Garden, CORNER_INNER_GARDEN, RotationCorrection.UsingOrdinal) .Case(Path, Path, Garden, CORNER_INNER_GARDEN, RotationCorrection.UsingOrdinal)
; ;
protected enum RotationCorrection {
Unchanged,
WithCorrection,
UsingOrdinal,
}
private final PathRegion region; private final PathRegion region;
private final UUID regionIdentifier; private final UUID regionIdentifier;
private final Tile tile; private final Tile tile;
@@ -177,7 +171,7 @@ public class PathArea implements Region.Area {
public void reset(PathSide side) { public void reset(PathSide side) {
File resetFile = null; File resetFile = null;
VariantSelector selector = SELECTOR_SIDE.Select(tile, side); VariantSelector selector = SELECTOR_SIDE.Select(tile, side);
if (selector != null) resetFile = selector.select(regionIdentifier, side.ordinal() + side.rotate).orElse(null); if (selector != null) resetFile = selector.select().orElse(null);
if (selector == null || resetFile == null) { if (selector == null || resetFile == null) {
if (!BauSystem.DEV_SERVER) return; if (!BauSystem.DEV_SERVER) return;
resetFile = FALLBACK_SCHEM; resetFile = FALLBACK_SCHEM;
@@ -191,7 +185,7 @@ public class PathArea implements Region.Area {
Pair<VariantSelector, RotationCorrection> pair = SELECTOR_CORNER.Select(tile, corner); Pair<VariantSelector, RotationCorrection> pair = SELECTOR_CORNER.Select(tile, corner);
VariantSelector selector = pair.getKey(); VariantSelector selector = pair.getKey();
RotationCorrection rotationCorrection = pair.getValue(); RotationCorrection rotationCorrection = pair.getValue();
if (selector != null) resetFile = selector.select(regionIdentifier, corner.side1.ordinal() * corner.side2.ordinal() + corner.side1.rotate * corner.side2.rotate).orElse(null); if (selector != null) resetFile = selector.select().orElse(null);
if (selector == null || resetFile == null) { if (selector == null || resetFile == null) {
if (!BauSystem.DEV_SERVER) return; if (!BauSystem.DEV_SERVER) return;
resetFile = FALLBACK_SCHEM; resetFile = FALLBACK_SCHEM;
@@ -216,14 +210,14 @@ public class PathArea implements Region.Area {
@Override @Override
public void place(Location location, PasteBuilder pasteBuilder, boolean extension) { public void place(Location location, PasteBuilder pasteBuilder, boolean extension) {
if (region.isGarden()) { if (region.isGarden()) {
File resetFile = GARDEN.select(regionIdentifier, 0).orElse(null); File resetFile = GARDEN.select().orElse(null);
if (resetFile != null) { if (resetFile != null) {
PasteUtils.paste(resetFile, minPoint, 0); PasteUtils.paste(resetFile, minPoint, 0);
} }
return; return;
} }
File resetFile = CENTER_NORMAL.select(regionIdentifier, 0).orElse(null); File resetFile = CENTER_NORMAL.select().orElse(null);
if (resetFile != null) { if (resetFile != null) {
PasteUtils.paste(resetFile, minPoint.add(7, 0, 7), 0); PasteUtils.paste(resetFile, minPoint.add(7, 0, 7), 0);
} }
@@ -0,0 +1,26 @@
/*
* 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;
public enum RotationCorrection {
Unchanged,
WithCorrection,
UsingOrdinal,
}
@@ -24,7 +24,7 @@ import de.steamwar.bausystem.region.dynamic.Tile;
import de.steamwar.bausystem.region.dynamic.VariantSelector; import de.steamwar.bausystem.region.dynamic.VariantSelector;
import de.steamwar.bausystem.shared.Pair; import de.steamwar.bausystem.shared.Pair;
class SelectorCorner { public class SelectorCorner {
private static final int BITS; private static final int BITS;
private static final int SELECTOR_COUNT; private static final int SELECTOR_COUNT;
@@ -36,7 +36,7 @@ class SelectorCorner {
} }
private VariantSelector[] selectors = new VariantSelector[SELECTOR_COUNT]; private VariantSelector[] selectors = new VariantSelector[SELECTOR_COUNT];
private PathArea.RotationCorrection[] rotationCorrections = new PathArea.RotationCorrection[SELECTOR_COUNT]; private RotationCorrection[] rotationCorrections = new RotationCorrection[SELECTOR_COUNT];
private int getIndex(RegionType.ConnectionType left, RegionType.ConnectionType right, RegionType.ConnectionType diagonal) { private int getIndex(RegionType.ConnectionType left, RegionType.ConnectionType right, RegionType.ConnectionType diagonal) {
int index = left.ordinal(); int index = left.ordinal();
@@ -47,18 +47,18 @@ class SelectorCorner {
public SelectorCorner Case(RegionType.ConnectionType left, RegionType.ConnectionType right, RegionType.ConnectionType diagonal, VariantSelector selector) { public SelectorCorner Case(RegionType.ConnectionType left, RegionType.ConnectionType right, RegionType.ConnectionType diagonal, VariantSelector selector) {
int index = getIndex(left, right, diagonal); int index = getIndex(left, right, diagonal);
selectors[index] = selector; selectors[index] = selector;
rotationCorrections[index] = PathArea.RotationCorrection.Unchanged; rotationCorrections[index] = RotationCorrection.Unchanged;
return this; return this;
} }
public SelectorCorner Case(RegionType.ConnectionType left, RegionType.ConnectionType right, RegionType.ConnectionType diagonal, VariantSelector selector, PathArea.RotationCorrection rotationCorrection) { public SelectorCorner Case(RegionType.ConnectionType left, RegionType.ConnectionType right, RegionType.ConnectionType diagonal, VariantSelector selector, RotationCorrection rotationCorrection) {
int index = getIndex(left, right, diagonal); int index = getIndex(left, right, diagonal);
selectors[index] = selector; selectors[index] = selector;
rotationCorrections[index] = rotationCorrection; rotationCorrections[index] = rotationCorrection;
return this; return this;
} }
public Pair<VariantSelector, PathArea.RotationCorrection> Select(Tile tile, PathCorner corner) { public Pair<VariantSelector, RotationCorrection> Select(Tile tile, PathCorner corner) {
RegionType.ConnectionType left = PathArea.getConnectionType(tile, corner.side1, null); RegionType.ConnectionType left = PathArea.getConnectionType(tile, corner.side1, null);
RegionType.ConnectionType right = PathArea.getConnectionType(tile, corner.side2, null); RegionType.ConnectionType right = PathArea.getConnectionType(tile, corner.side2, null);
RegionType.ConnectionType diagonal = PathArea.getConnectionType(tile, corner.side1, corner.side2); RegionType.ConnectionType diagonal = PathArea.getConnectionType(tile, corner.side1, corner.side2);
@@ -23,11 +23,11 @@ import de.steamwar.bausystem.region.RegionType;
import de.steamwar.bausystem.region.dynamic.Tile; import de.steamwar.bausystem.region.dynamic.Tile;
import de.steamwar.bausystem.region.dynamic.VariantSelector; import de.steamwar.bausystem.region.dynamic.VariantSelector;
class SelectorSide { public class SelectorSide {
private VariantSelector[] selectors = new VariantSelector[RegionType.ConnectionType.values().length]; private VariantSelector[] selectors = new VariantSelector[RegionType.ConnectionType.values().length];
protected SelectorSide Case(RegionType.ConnectionType type, VariantSelector selector) { public SelectorSide Case(RegionType.ConnectionType type, VariantSelector selector) {
selectors[type.ordinal()] = selector; selectors[type.ordinal()] = selector;
return this; return this;
} }
@@ -105,7 +105,7 @@ public class SpecialArea implements Region.Area {
@Override @Override
public void place(Location location, PasteBuilder pasteBuilder, boolean extension) { public void place(Location location, PasteBuilder pasteBuilder, boolean extension) {
File resetFile = resetFiles.select(regionIdentifier, 0).orElse(null); File resetFile = resetFiles.select().orElse(null);
if (resetFile != null) PasteUtils.paste(resetFile, minPoint, 0); if (resetFile != null) PasteUtils.paste(resetFile, minPoint, 0);
resetting = false; resetting = false;
} }
@@ -139,7 +139,7 @@ public class FixedRegion implements Region {
} }
@Override @Override
public void delete() { public void delete(Location location) {
file.delete(); file.delete();
} }
} }