Implement rest of PlotRegionBackups

This commit is contained in:
2026-03-16 17:32:04 +01:00
parent 24029b795c
commit 824e0d7b6b
6 changed files with 112 additions and 56 deletions
@@ -25,12 +25,15 @@ import lombok.RequiredArgsConstructor;
import javax.annotation.CheckReturnValue; import javax.annotation.CheckReturnValue;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.time.format.DateTimeFormatter;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.function.Function; import java.util.function.Function;
public interface RegionBackups { public interface RegionBackups {
DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy.MM.dd' 'HH:mm:ss");
@RequiredArgsConstructor @RequiredArgsConstructor
enum BackupType { enum BackupType {
MANUAL(5), MANUAL(5),
@@ -60,6 +63,10 @@ public interface RegionBackups {
@CheckReturnValue @CheckReturnValue
public abstract boolean load(); public abstract boolean load();
@Override
public final void save() {
}
public abstract long getCreationTime(); public abstract long getCreationTime();
@Override @Override
@@ -67,6 +74,10 @@ public interface RegionBackups {
return Long.compare(getCreationTime(), o.getCreationTime()); return Long.compare(getCreationTime(), o.getCreationTime());
} }
@Override
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 'deleteRegion' for Backups!
@Override @Override
public abstract void delete(); public abstract void delete();
@@ -50,7 +50,7 @@ public class DynamicRegionCommand extends SWCommand {
// Check location! // Check location!
Class<? extends DynamicRegion> regionClass = DynamicRegionSystem.identifierDataMap.get(regionType); Class<? extends DynamicRegion> regionClass = DynamicRegionSystem.getRegionClassByIdentifier(regionType);
DynamicRegion dynamicRegion = DynamicRegionRepository.constructRegion(regionClass, UUID.randomUUID(), tile.getMinX(), tile.getMinZ()); DynamicRegion dynamicRegion = DynamicRegionRepository.constructRegion(regionClass, UUID.randomUUID(), tile.getMinX(), tile.getMinZ());
if (dynamicRegion == null) { if (dynamicRegion == null) {
// TODO: Give error to user // TODO: Give error to user
@@ -73,7 +73,7 @@ public class DynamicRegionCommand extends SWCommand {
return new TypeMapper<>() { return new TypeMapper<>() {
@Override @Override
public String map(CommandSender commandSender, String[] previousArguments, String s) { public String map(CommandSender commandSender, String[] previousArguments, String s) {
if (DynamicRegionSystem.identifierDataMap.containsKey(s)) { if (DynamicRegionSystem.hasRegionClassForIdentifier(s)) {
return s; return s;
} else { } else {
return null; return null;
@@ -82,7 +82,7 @@ public class DynamicRegionCommand extends SWCommand {
@Override @Override
public Collection<String> tabCompletes(CommandSender sender, PreviousArguments previousArguments, String s) { public Collection<String> tabCompletes(CommandSender sender, PreviousArguments previousArguments, String s) {
return DynamicRegionSystem.identifierDataMap.keySet(); return DynamicRegionSystem.allRegionIdentifiers();
} }
}; };
} }
@@ -62,8 +62,24 @@ public class DynamicRegionSystem implements RegionSystem {
regionTypeMap.getOrDefault(region.getType(), Collections.emptySet()).remove(region); regionTypeMap.getOrDefault(region.getType(), Collections.emptySet()).remove(region);
} }
public static Map<Class<? extends DynamicRegion>, RegionConstructorData> constructorDataMap = new HashMap<>(); private static Map<Class<? extends DynamicRegion>, RegionConstructorData> constructorDataMap = new HashMap<>();
public static Map<String, Class<? extends DynamicRegion>> identifierDataMap = new HashMap<>(); private static Map<String, Class<? extends DynamicRegion>> identifierDataMap = new HashMap<>();
public static RegionConstructorData getRegionConstructorByRegionClass(Class<?> regionClass) {
return constructorDataMap.get(regionClass);
}
public static Class<? extends DynamicRegion> getRegionClassByIdentifier(String identifier) {
return identifierDataMap.get(identifier);
}
public static boolean hasRegionClassForIdentifier(String identifier) {
return identifierDataMap.containsKey(identifier);
}
public static Set<String> allRegionIdentifiers() {
return Collections.unmodifiableSet(identifierDataMap.keySet());
}
@Override @Override
public void load() { public void load() {
@@ -132,7 +132,7 @@ public class DynamicRegionRepository {
} }
// TODO: Maybe add static method to DynamicRegionSystem // TODO: Maybe add static method to DynamicRegionSystem
Class<? extends DynamicRegion> regionClass = DynamicRegionSystem.identifierDataMap.get(identifier); Class<? extends DynamicRegion> regionClass = DynamicRegionSystem.getRegionClassByIdentifier(identifier);
if (regionClass == null) { if (regionClass == null) {
RegionSystem.LOGGER.log(Level.SEVERE, "Failed to read region metadata file (region no longer exists)"); RegionSystem.LOGGER.log(Level.SEVERE, "Failed to read region metadata file (region no longer exists)");
continue; continue;
@@ -172,20 +172,28 @@ public class DynamicRegionRepository {
} }
} }
public static File getRegionDirectory(Region region) {
return new File(REGION_DATA_FOLDER, region.getID().toString());
}
public static File getBackupsTypeDirectory(Region region, RegionBackups.BackupType backupType) {
File regionDirectory = getRegionDirectory(region);
File backupsDirectory = new File(regionDirectory, BACKUPS_DIR_NAME);
return new File(backupsDirectory, backupType.name());
}
public static File getBackupDirectory(Region region, RegionBackups.Backup backup) {
return new File(getBackupsTypeDirectory(region, backup.getType()), backup.getName());
}
public static void loadRegionData(Region region, RegionData regionData) { public static void loadRegionData(Region region, RegionData regionData) {
File regionDirectory = new File(REGION_DATA_FOLDER, region.getID().toString()); File regionDirectory = getRegionDirectory(region);
if (!regionDirectory.exists()) return; if (!regionDirectory.exists()) return;
loadRegionData(regionDirectory, regionData); loadRegionData(regionDirectory, regionData);
} }
public static void loadRegionData(Region region, RegionBackups.Backup backup, RegionData regionData) { public static void loadRegionData(Region region, RegionBackups.Backup backup, RegionData regionData) {
File regionDirectory = new File(REGION_DATA_FOLDER, region.getID().toString()); File backupDirectory = getBackupDirectory(region, backup);
if (!regionDirectory.exists()) return;
File backupsDirectory = new File(regionDirectory, BACKUPS_DIR_NAME);
if (!backupsDirectory.exists()) return;
File backupsTypeDirectory = new File(backupsDirectory, backup.getType().name());
if (!backupsTypeDirectory.exists()) return;
File backupDirectory = new File(backupsTypeDirectory, backup.getName());
if (!backupDirectory.exists()) return; if (!backupDirectory.exists()) return;
loadRegionData(backupDirectory, regionData); loadRegionData(backupDirectory, regionData);
} }
@@ -234,7 +242,7 @@ public class DynamicRegionRepository {
} }
if (!region.getType().isGlobal()) { if (!region.getType().isGlobal()) {
RegionConstructorData constructorData = DynamicRegionSystem.constructorDataMap.get(region.getClass()); RegionConstructorData constructorData = DynamicRegionSystem.getRegionConstructorByRegionClass(region.getClass());
Point point = region.getArea().getMinPoint(false); Point point = region.getArea().getMinPoint(false);
Tile tile = Tile.fromPoint(point).get(); Tile tile = Tile.fromPoint(point).get();
@@ -242,7 +250,14 @@ public class DynamicRegionRepository {
} }
writeFlagsFile(regionDirectory, region.getRegionData()); writeFlagsFile(regionDirectory, region.getRegionData());
// TODO: Write backups -> Directly on create! }
public static void saveBackup(Region region, RegionBackups.Backup backup) {
File backupDirectory = getBackupDirectory(region, backup);
if (!backupDirectory.exists()) {
backupDirectory.mkdirs();
}
writeFlagsFile(backupDirectory, backup.getRegionData());
} }
@SneakyThrows @SneakyThrows
@@ -261,8 +276,8 @@ public class DynamicRegionRepository {
} }
@SneakyThrows @SneakyThrows
private static void writeFlagsFile(File regionDirectory, RegionData regionData) { private static void writeFlagsFile(File directory, RegionData regionData) {
JsonWriter jsonWriter = new JsonWriter(new FileWriter(new File(regionDirectory, FLAG_FILE_NAME))); JsonWriter jsonWriter = new JsonWriter(new FileWriter(new File(directory, FLAG_FILE_NAME)));
jsonWriter.setIndent(" "); jsonWriter.setIndent(" ");
jsonWriter.beginObject(); jsonWriter.beginObject();
@@ -285,12 +300,15 @@ public class DynamicRegionRepository {
} }
public static void deleteRegion(Region region) { public static void deleteRegion(Region region) {
File regionDirectory = new File(REGION_DATA_FOLDER, region.getID().toString()); deleteDir(getRegionDirectory(region));
deleteDir(regionDirectory); }
public static void deleteBackup(Region region, RegionBackups.Backup backup) {
deleteDir(getBackupDirectory(region, backup));
} }
@SneakyThrows @SneakyThrows
public static void deleteDir(File file) { private static void deleteDir(File file) {
Files.walkFileTree(file.toPath(), new SimpleFileVisitor<>() { Files.walkFileTree(file.toPath(), new SimpleFileVisitor<>() {
@Override @Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
@@ -20,17 +20,25 @@
package de.steamwar.bausystem.region.dynamic.modes; package de.steamwar.bausystem.region.dynamic.modes;
import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.extent.clipboard.io.BuiltInClipboardFormat;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter;
import de.steamwar.bausystem.region.RegionBackups; import de.steamwar.bausystem.region.RegionBackups;
import de.steamwar.bausystem.region.RegionData; import de.steamwar.bausystem.region.RegionData;
import de.steamwar.bausystem.region.dynamic.DynamicRegion; import de.steamwar.bausystem.region.dynamic.DynamicRegion;
import de.steamwar.bausystem.region.dynamic.DynamicRegionRepository; 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.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.io.File; import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*; import java.util.*;
import java.util.function.Function; import java.util.function.Function;
import java.util.logging.Level;
public class PlotRegionBackups implements RegionBackups { public class PlotRegionBackups implements RegionBackups {
@@ -46,7 +54,18 @@ public class PlotRegionBackups implements RegionBackups {
this.region = region; this.region = region;
this.regionDataConstructor = regionDataConstructor; this.regionDataConstructor = regionDataConstructor;
// DynamicRegionRepository.loadRegionData(); // Load all Backups of the Region
for (BackupType backupType : BackupType.values()) {
File backupsTypeDirectory = DynamicRegionRepository.getBackupsTypeDirectory(region, backupType);
if (!backupsTypeDirectory.exists()) continue;
File[] backupsDirectory = backupsTypeDirectory.listFiles();
if (backupsDirectory == null) continue;
List<Backup> backupList = backups.computeIfAbsent(backupType, __ -> new ArrayList<>());
for (File backupDirectory : backupsDirectory) {
backupList.add(new BackupImpl(backupType, region, backupDirectory));
}
}
} }
@Override @Override
@@ -60,11 +79,8 @@ public class PlotRegionBackups implements RegionBackups {
} }
// Create backup and save! // Create backup and save!
Backup backup = new BackupImpl(this, backupType, "", regionDataConstructor, region); Backup backup = new BackupImpl(backupType, region);
// Create schematic and stuff?
backup.save();
backupList.add(backup); backupList.add(backup);
return Optional.of(backup); return Optional.of(backup);
} }
@@ -90,20 +106,34 @@ public class PlotRegionBackups implements RegionBackups {
private static final String SCHEM_FILE = "backup.schem"; private static final String SCHEM_FILE = "backup.schem";
private final PlotRegionBackups parent;
private final File folder;
private final DynamicRegion region; private final DynamicRegion region;
public BackupImpl(PlotRegionBackups parent, @NonNull BackupType type, @NonNull String name, @NonNull Function<Backup, RegionData> regionDataConstructor, DynamicRegion region) { public BackupImpl(@NonNull BackupType type, DynamicRegion region) {
super(type, name, regionDataConstructor); super(type, LocalDateTime.now().format(FORMATTER), regionDataConstructor);
this.parent = parent; region.getRegionData().copyInto(regionData);
folder = new File("");
this.region = region; this.region = region;
File backupDirectory = DynamicRegionRepository.getBackupDirectory(region, this);
backupDirectory.mkdirs();
DynamicRegionRepository.saveBackup(region, this);
Clipboard clipboard = region.getArea().copy(false);
try (ClipboardWriter writer = BuiltInClipboardFormat.SPONGE_SCHEMATIC.getWriter(new FileOutputStream(new File(backupDirectory, SCHEM_FILE)))) {
writer.write(clipboard);
} catch (IOException e) {
Bukkit.getLogger().log(Level.SEVERE, e.getMessage(), e);
}
}
public BackupImpl(@NonNull BackupType type, DynamicRegion region, File backupDirectory) {
super(type, backupDirectory.getName(), regionDataConstructor);
this.region = region;
DynamicRegionRepository.loadRegionData(region, this, regionData);
} }
@Override @Override
public boolean load() { public boolean load() {
File file = new File(folder, SCHEM_FILE); File file = new File(DynamicRegionRepository.getBackupDirectory(region, this), SCHEM_FILE);
if (!file.exists()) return false; if (!file.exists()) return false;
EditSession editSession = PasteUtils.paste(file, region.getArea().getMinPoint(false), 0); EditSession editSession = PasteUtils.paste(file, region.getArea().getMinPoint(false), 0);
if (editSession == null) return false; if (editSession == null) return false;
@@ -114,24 +144,14 @@ public class PlotRegionBackups implements RegionBackups {
@Override @Override
public long getCreationTime() { public long getCreationTime() {
throw new UnsupportedOperationException(); return DynamicRegionRepository.getBackupDirectory(region, this).lastModified();
} }
@Override @Override
public void delete() { public void delete() {
parent.backups.getOrDefault(type, Collections.emptyList()) backups.getOrDefault(type, Collections.emptyList())
.remove(this); .remove(this);
DynamicRegionRepository.deleteDir(folder); DynamicRegionRepository.deleteBackup(region, this);
}
@Override
public void save() {
throw new UnsupportedOperationException();
}
@Override
public void load(RegionData regionData) {
throw new UnsupportedOperationException();
} }
} }
} }
@@ -45,7 +45,6 @@ import java.util.stream.Collectors;
public class FixedRegion implements Region { public class FixedRegion implements Region {
private static final File backupFolder = new File(Bukkit.getWorlds().get(0).getWorldFolder(), "backup"); private static final File backupFolder = new File(Bukkit.getWorlds().get(0).getWorldFolder(), "backup");
private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd' 'HH:mm:ss");
private final String name; private final String name;
private final UUID uuid; private final UUID uuid;
@@ -75,7 +74,7 @@ public class FixedRegion implements Region {
while (files.size() >= 20) files.remove(0).delete(); while (files.size() >= 20) files.remove(0).delete();
} }
final File backupFile = new File(definedBackupFolder, LocalDateTime.now().format(formatter) + ".schem"); final File backupFile = new File(definedBackupFolder, LocalDateTime.now().format(FORMATTER) + ".schem");
Point minPoint = area.getMinPoint(false); Point minPoint = area.getMinPoint(false);
Point maxPoint = area.getMaxPoint(false); Point maxPoint = area.getMaxPoint(false);
if (!FlatteningWrapper.impl.backup(minPoint, maxPoint, backupFile)) { if (!FlatteningWrapper.impl.backup(minPoint, maxPoint, backupFile)) {
@@ -138,14 +137,6 @@ public class FixedRegion implements Region {
return file.lastModified(); return file.lastModified();
} }
@Override
public void save() {
}
@Override
public void load(RegionData regionData) {
}
@Override @Override
public void delete() { public void delete() {
file.delete(); file.delete();