Migrate builder and bau worlds to shared world records

- add SQL-backed world entities with archive and rename support
- route builder, bau, deploy, and GDPR flows through world storage
- keep legacy folder support for existing worlds
This commit is contained in:
2026-05-09 16:37:15 +02:00
parent 8ade5180cb
commit 936ee38503
6 changed files with 314 additions and 57 deletions
+181
View File
@@ -0,0 +1,181 @@
/*
* 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.sql
import de.steamwar.sql.internal.useDb
import org.jetbrains.exposed.v1.core.and
import org.jetbrains.exposed.v1.core.dao.id.EntityID
import org.jetbrains.exposed.v1.core.dao.id.UUIDTable
import org.jetbrains.exposed.v1.core.eq
import org.jetbrains.exposed.v1.core.not
import org.jetbrains.exposed.v1.dao.Entity
import org.jetbrains.exposed.v1.dao.EntityClass
import org.jetbrains.exposed.v1.javatime.CurrentTimestamp
import org.jetbrains.exposed.v1.javatime.timestamp
import java.io.File
import java.time.Instant
import java.time.temporal.ChronoUnit
import java.util.UUID
object WorldTable : UUIDTable("world") {
val name = varchar("Name", 512)
val version = integer("Version")
val type = enumeration<WorldType>("Type")
val owner = reference("Owner", SteamwarUserTable).nullable()
val deleted = bool("Deleted").default(false)
val created = timestamp("Created").defaultExpression(CurrentTimestamp)
val lastUsed = timestamp("LastUsed").defaultExpression(CurrentTimestamp)
}
enum class WorldType {
BAU,
BUILDER,
}
class SteamwarWorld(id: EntityID<UUID>) : Entity<UUID>(id) {
var name by WorldTable.name
private set
var version by WorldTable.version
private set
var type by WorldTable.type
private set
var owner by WorldTable.owner
private set
var deleted by WorldTable.deleted
private set
val created by WorldTable.created
var lastUsed by WorldTable.lastUsed
private set
val archived: Boolean
get() = !storageDirectory.exists() && archiveFile.exists()
val shouldArchive: Boolean
get() = !archived && (deleted || lastUsed.plus(7, ChronoUnit.DAYS).isBefore(Instant.now()))
val uuid: UUID
get() = id.value
val storageDirectory: File
get() = File(WORLD_STORAGE, id.value.toString())
private val archiveFile: File
get() = File(ARCHIVE_WORLD_STORAGE, "${id.value}.zip")
@JvmOverloads
fun setupAndGetStoragePath(prototype: File? = null): String {
if (archived) {
loadFromArchive()
}
val needsInitialization = !storageDirectory.exists() || storageDirectory.list()?.isEmpty() == true
if (needsInitialization) {
if (prototype != null && prototype.exists()) {
prototype.copyRecursively(storageDirectory, overwrite = true)
} else {
storageDirectory.mkdirs()
}
}
useDb {
lastUsed = Instant.now()
}
return storageDirectory.path
}
fun markDeleted() = useDb {
deleted = true
}
fun rename(newName: String) = useDb {
name = newName
}
private fun archiveWorld() = ProcessBuilder("zip", "-u9oymrqq", "$ARCHIVE_WORLD_STORAGE/${id.value}.zip", id.value.toString())
.directory(File(WORLD_STORAGE))
.inheritIO()
.start()
.waitFor()
private fun loadFromArchive() = ProcessBuilder("unzip", "-qq", "-o", "$ARCHIVE_WORLD_STORAGE/${id.value}.zip", "-d", WORLD_STORAGE)
.inheritIO()
.start()
.waitFor()
companion object : EntityClass<UUID, SteamwarWorld>(WorldTable) {
const val WORLD_STORAGE = "/worlds/storage"
const val ARCHIVE_WORLD_STORAGE = "/mnt/storage/worlds/storage"
@JvmStatic
fun getBauWorld(user: SteamwarUser, version: Int) = useDb {
find {
(WorldTable.owner eq user.id) and
(WorldTable.version eq version) and
(WorldTable.type eq WorldType.BAU) and
not(WorldTable.deleted)
}.firstOrNull()
}
@JvmStatic
@JvmOverloads
fun getOrCreateBauWorld(user: SteamwarUser, version: Int, prototype: File? = null): SteamwarWorld =
getBauWorld(user, version) ?: createWorld(user, user.userName, version, WorldType.BAU, prototype)
@JvmStatic
fun getBuilderWorld(name: String, version: Int) = useDb {
find {
(WorldTable.name eq name) and
(WorldTable.version eq version) and
(WorldTable.type eq WorldType.BUILDER) and
not(WorldTable.deleted)
}.firstOrNull()
}
@JvmStatic
fun getBuilderWorlds(version: Int) = useDb {
find {
(WorldTable.version eq version) and
(WorldTable.type eq WorldType.BUILDER) and
not(WorldTable.deleted)
}.toList()
}
@JvmStatic
@JvmOverloads
fun getOrCreateBuilderWorld(name: String, version: Int, prototype: File? = null): SteamwarWorld =
getBuilderWorld(name, version) ?: createWorld(null, name, version, WorldType.BUILDER, prototype)
@JvmStatic
@JvmOverloads
fun createWorld(user: SteamwarUser?, name: String, version: Int, type: WorldType, prototype: File? = null): SteamwarWorld {
val world = useDb { new {
this.name = name
this.version = version
this.type = type
this.owner = user?.id
} }
world.setupAndGetStoragePath(prototype)
return world
}
}
}
+1 -1
View File
@@ -37,7 +37,7 @@ if __name__ == "__main__":
with open(configfile, 'r') as file: with open(configfile, 'r') as file:
gamemode = yaml.load(file) gamemode = yaml.load(file)
builderworld = path.expanduser(f'/worlds/builder{version}/{worldname}') builderworld = sys.argv[4] if len(sys.argv) > 4 else path.expanduser(f'/worlds/builder{version}/{worldname}')
arenaworld = f'/servers/{gamemode["Server"]["Folder"]}/arenas/{worldname}' arenaworld = f'/servers/{gamemode["Server"]["Folder"]}/arenas/{worldname}'
if path.exists(arenaworld): if path.exists(arenaworld):
@@ -52,11 +52,9 @@ public class ServerStarter {
private static final String EVENT_PATH = TMP_DATA + "event/"; private static final String EVENT_PATH = TMP_DATA + "event/";
public static final String TEMP_WORLD_PATH = TMP_DATA + "arenaserver/"; public static final String TEMP_WORLD_PATH = TMP_DATA + "arenaserver/";
private static final String WORLDS_FOLDER = "/worlds"; public static final String WORLDS_BASE_PATH = SteamwarWorld.WORLD_STORAGE + "/";
public static final String WORLDS_BASE_PATH = WORLDS_FOLDER + "/userworlds"; public static final String LEGACY_WORLDS_BASE_PATH = "/worlds/userworlds";
public static final String BUILDER_BASE_PATH = WORLDS_FOLDER + "/builder"; public static final String LEGACY_BUILDER_BASE_PATH = "/worlds/builder";
private static final String WORLDS_STORAGE_BASE_PATH = "/mnt/storage/worlds/userworlds";
private File directory = null; private File directory = null;
private String worldDir = null; private String worldDir = null;
@@ -152,23 +150,15 @@ public class ServerStarter {
public ServerStarter build(ServerVersion version, UUID owner) { public ServerStarter build(ServerVersion version, UUID owner) {
this.version = version; this.version = version;
directory = version.getServerDirectory("Bau"); directory = version.getServerDirectory("Bau");
worldDir = version.getWorldFolder(WORLDS_BASE_PATH); SteamwarUser user = SteamwarUser.get(owner);
worldName = String.valueOf(SteamwarUser.get(owner).getId()); SteamwarWorld world = SteamwarWorld.getOrCreateBauWorld(user, version.getVersionSuffix());
worldDir = WORLDS_BASE_PATH;
worldName = world.getUuid().toString();
checkpoint = true; checkpoint = true;
build(owner); build(owner);
worldSetup = () -> { worldSetup = () -> world.setupAndGetStoragePath(new File(directory, "Bauwelt"));
File world = new File(worldDir, worldName);
if (!world.exists()) {
File storage = new File(version.getWorldFolder(WORLDS_STORAGE_BASE_PATH), worldName);
if(storage.exists())
node.execute("mv", storage.getPath(), world.getPath());
else
copyWorld(node, new File(directory, "Bauwelt").getPath(), world.getPath());
}
};
// Send players to existing server // Send players to existing server
startCondition = () -> { startCondition = () -> {
@@ -224,8 +214,9 @@ public class ServerStarter {
public ServerStarter builder(ServerVersion version, String map, File generator) { public ServerStarter builder(ServerVersion version, String map, File generator) {
this.version = version; this.version = version;
directory = version.getServerDirectory("Builder"); directory = version.getServerDirectory("Builder");
worldDir = version.getWorldFolder(BUILDER_BASE_PATH); SteamwarWorld world = SteamwarWorld.getOrCreateBuilderWorld(map, version.getVersionSuffix());
worldName = map; worldDir = WORLDS_BASE_PATH;
worldName = world.getUuid().toString();
serverNameProvider = port -> "*" + map; serverNameProvider = port -> "*" + map;
checkpoint = true; checkpoint = true;
constructor = (serverName, port, builder, shutdownCallback, failureCallback) -> new Builderserver(serverName, worldName, port, builder, shutdownCallback, failureCallback); constructor = (serverName, port, builder, shutdownCallback, failureCallback) -> new Builderserver(serverName, worldName, port, builder, shutdownCallback, failureCallback);
@@ -243,13 +234,15 @@ public class ServerStarter {
if(generator != null) { if(generator != null) {
worldSetup = () -> { worldSetup = () -> {
File leveldat = new File(new File(worldDir, worldName), "level.dat"); File leveldat = new File(world.setupAndGetStoragePath(), "level.dat");
try { try {
Files.copy(generator.toPath(), leveldat.toPath()); Files.copy(generator.toPath(), leveldat.toPath());
} catch (IOException e) { } catch (IOException e) {
throw new SecurityException(e); throw new SecurityException(e);
} }
}; };
} else {
worldSetup = () -> world.setupAndGetStoragePath(legacyBuilderWorld(version, map));
} }
return this; return this;
@@ -350,6 +343,22 @@ public class ServerStarter {
node.execute("cp", "-r", template, target); node.execute("cp", "-r", template, target);
} }
private static File legacyBauWorld(ServerVersion version, SteamwarUser user) {
File legacyIdWorld = new File(version.getWorldFolder(LEGACY_WORLDS_BASE_PATH), String.valueOf(user.getId()));
if(legacyIdWorld.exists())
return legacyIdWorld;
File legacyUuidWorld = new File(version.getWorldFolder(LEGACY_WORLDS_BASE_PATH), user.getUUID().toString());
if(legacyUuidWorld.exists())
return legacyUuidWorld;
return new File(version.getServerDirectory("Bau"), "Bauwelt");
}
public static File legacyBuilderWorld(ServerVersion version, String map) {
return new File(version.getWorldFolder(LEGACY_BUILDER_BASE_PATH), map);
}
private interface ServerConstructor { private interface ServerConstructor {
Subserver construct(String serverName, int port, ProcessBuilder builder, Runnable shutdownCallback, Consumer<Exception> failureCallback); Subserver construct(String serverName, int port, ProcessBuilder builder, Runnable shutdownCallback, Consumer<Exception> failureCallback);
} }
@@ -390,4 +399,4 @@ public class ServerStarter {
} }
} }
} }
@@ -34,6 +34,7 @@ import de.steamwar.network.packets.server.BaumemberUpdatePacket;
import de.steamwar.persistent.Bauserver; import de.steamwar.persistent.Bauserver;
import de.steamwar.sql.BauweltMember; import de.steamwar.sql.BauweltMember;
import de.steamwar.sql.SteamwarUser; import de.steamwar.sql.SteamwarUser;
import de.steamwar.sql.SteamwarWorld;
import de.steamwar.velocitycore.*; import de.steamwar.velocitycore.*;
import de.steamwar.velocitycore.inventory.SWInventory; import de.steamwar.velocitycore.inventory.SWInventory;
import de.steamwar.velocitycore.inventory.SWItem; import de.steamwar.velocitycore.inventory.SWItem;
@@ -41,6 +42,7 @@ import de.steamwar.velocitycore.network.NetworkSender;
import de.steamwar.velocitycore.util.BauLock; import de.steamwar.velocitycore.util.BauLock;
import de.steamwar.data.BauLockState; import de.steamwar.data.BauLockState;
import java.io.File;
import java.util.Collection; import java.util.Collection;
import java.util.function.Consumer; import java.util.function.Consumer;
@@ -220,14 +222,20 @@ public class BauCommand extends SWCommand {
public void delete(PlayerChatter sender, ServerVersion version) { public void delete(PlayerChatter sender, ServerVersion version) {
SWInventory inventory = new SWInventory(sender, 9, new Message("BAU_DELETE_GUI_NAME")); SWInventory inventory = new SWInventory(sender, 9, new Message("BAU_DELETE_GUI_NAME"));
inventory.addItem(0, new SWItem(new Message("BAU_DELETE_GUI_DELETE"), 10), click -> { inventory.addItem(0, new SWItem(new Message("BAU_DELETE_GUI_DELETE"), 10), click -> {
String world = version.getWorldFolder(ServerStarter.WORLDS_BASE_PATH) + sender.user().getId(); SteamwarWorld world = SteamwarWorld.getBauWorld(sender.user(), version.getVersionSuffix());
VelocityCore.schedule(() -> { VelocityCore.schedule(() -> {
Bauserver subserver = Bauserver.get(sender.user().getUUID()); Bauserver subserver = Bauserver.get(sender.user().getUUID());
if(subserver != null) if(subserver != null)
subserver.stop(); subserver.stop();
SubserverSystem.deleteFolder(VelocityCore.local, world); if(world != null) {
world.markDeleted();
SubserverSystem.deleteFolder(VelocityCore.local, world.getStorageDirectory().getPath());
} else {
File legacyWorld = new File(version.getWorldFolder(ServerStarter.LEGACY_WORLDS_BASE_PATH), String.valueOf(sender.user().getId()));
SubserverSystem.deleteFolder(VelocityCore.local, legacyWorld.getPath());
}
sender.system("BAU_DELETE_DELETED"); sender.system("BAU_DELETE_DELETED");
}).schedule(); }).schedule();
@@ -21,7 +21,7 @@ package de.steamwar.velocitycore.commands;
import de.steamwar.sql.GameModeConfig; import de.steamwar.sql.GameModeConfig;
import de.steamwar.linkage.Linked; import de.steamwar.linkage.Linked;
import de.steamwar.sql.SchematicType; import de.steamwar.sql.SteamwarWorld;
import de.steamwar.velocitycore.ArenaMode; import de.steamwar.velocitycore.ArenaMode;
import de.steamwar.velocitycore.ServerStarter; import de.steamwar.velocitycore.ServerStarter;
import de.steamwar.velocitycore.ServerVersion; import de.steamwar.velocitycore.ServerVersion;
@@ -34,11 +34,11 @@ import de.steamwar.messages.PlayerChatter;
import de.steamwar.sql.UserPerm; import de.steamwar.sql.UserPerm;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
@Linked @Linked
public class BuilderCloudCommand extends SWCommand { public class BuilderCloudCommand extends SWCommand {
@@ -49,13 +49,18 @@ public class BuilderCloudCommand extends SWCommand {
@Register(value = "create", description = "BUILDERCLOUD_CREATE_USAGE") @Register(value = "create", description = "BUILDERCLOUD_CREATE_USAGE")
public void create(Chatter sender, @ErrorMessage("BUILDERCLOUD_VERSION") ServerVersion version, @Mapper("map") String map, @OptionalValue("") @Mapper("generator") @AllowNull File generator) { public void create(Chatter sender, @ErrorMessage("BUILDERCLOUD_VERSION") ServerVersion version, @Mapper("map") String map, @OptionalValue("") @Mapper("generator") @AllowNull File generator) {
mapFile(version, map).mkdir(); if(mapExists(version, map)) {
sender.system("BUILDERCLOUD_EXISTING_MAP");
return;
}
SteamwarWorld.getOrCreateBuilderWorld(map, version.getVersionSuffix()).setupAndGetStoragePath();
sender.withPlayer(p -> new ServerStarter().builder(version, map, generator).send(p).start()); sender.withPlayer(p -> new ServerStarter().builder(version, map, generator).send(p).start());
} }
@Register(description = "BUILDERCLOUD_USAGE") @Register(description = "BUILDERCLOUD_USAGE")
public void start(PlayerChatter sender, @ErrorMessage("BUILDERCLOUD_VERSION") ServerVersion version, @Mapper("map") String map) { public void start(PlayerChatter sender, @ErrorMessage("BUILDERCLOUD_VERSION") ServerVersion version, @Mapper("map") String map) {
if(!mapFile(version, map).exists()) { if(!mapExists(version, map)) {
sender.system("BUILDERCLOUD_UNKNOWN_MAP"); sender.system("BUILDERCLOUD_UNKNOWN_MAP");
return; return;
} }
@@ -65,36 +70,32 @@ public class BuilderCloudCommand extends SWCommand {
@Register(value = "rename", description = "BUILDERCLOUD_RENAME_USAGE") @Register(value = "rename", description = "BUILDERCLOUD_RENAME_USAGE")
public void rename(Chatter sender, @ErrorMessage("BUILDERCLOUD_VERSION") ServerVersion version, @Mapper("map") String oldName, String newName) { public void rename(Chatter sender, @ErrorMessage("BUILDERCLOUD_VERSION") ServerVersion version, @Mapper("map") String oldName, String newName) {
File oldMap = mapFile(version, oldName); SteamwarWorld oldMap = builderWorld(version, oldName);
if(!oldMap.exists()) { if(oldMap == null) {
sender.system("BUILDERCLOUD_UNKNOWN_MAP"); sender.system("BUILDERCLOUD_UNKNOWN_MAP");
return; return;
} }
File newMap = mapFile(version, newName); if(mapExists(version, newName)) {
if(newMap.exists()) {
sender.system("BUILDERCLOUD_EXISTING_MAP"); sender.system("BUILDERCLOUD_EXISTING_MAP");
return; return;
} }
try { oldMap.rename(newName);
Files.move(oldMap.toPath(), newMap.toPath());
} catch (IOException e) {
throw new SecurityException(e);
}
sender.system("BUILDERCLOUD_RENAMED"); sender.system("BUILDERCLOUD_RENAMED");
} }
@Register(value = "deploy", description = "BUILDERCLOUD_DEPLOY_USAGE") @Register(value = "deploy", description = "BUILDERCLOUD_DEPLOY_USAGE")
public void deploy(Chatter sender, @Mapper("nonHistoricArenaMode") GameModeConfig<String, String> arenaMode, @ErrorMessage("BUILDERCLOUD_VERSION") ServerVersion version, @Mapper("map") String map) { public void deploy(Chatter sender, @Mapper("nonHistoricArenaMode") GameModeConfig<String, String> arenaMode, @ErrorMessage("BUILDERCLOUD_VERSION") ServerVersion version, @Mapper("map") String map) {
if(!mapFile(version, map).exists()) { SteamwarWorld builderWorld = builderWorld(version, map);
if(builderWorld == null) {
sender.system("BUILDERCLOUD_UNKNOWN_MAP"); sender.system("BUILDERCLOUD_UNKNOWN_MAP");
return; return;
} }
VelocityCore.schedule(() -> { VelocityCore.schedule(() -> {
VelocityCore.local.execute("deployarena.py", arenaMode.configFile.getName(), Integer.toString(version.getVersionSuffix()), map); VelocityCore.local.execute("deployarena.py", arenaMode.configFile.getName(), Integer.toString(version.getVersionSuffix()), map, builderWorld.setupAndGetStoragePath());
ArenaMode.init(); ArenaMode.init();
sender.system("BUILDERCLOUD_DEPLOY_FINISHED"); sender.system("BUILDERCLOUD_DEPLOY_FINISHED");
}).schedule(); }).schedule();
@@ -112,13 +113,23 @@ public class BuilderCloudCommand extends SWCommand {
@Override @Override
public Collection<String> tabCompletes(Chatter sender, PreviousArguments previousArguments, String s) { public Collection<String> tabCompletes(Chatter sender, PreviousArguments previousArguments, String s) {
File folder = getWorldFolder(previousArguments, 1); ServerVersion version = getVersion(previousArguments, 1);
if(version == null)
String[] files;
if(folder == null || (files = folder.list()) == null)
return Collections.emptyList(); return Collections.emptyList();
return Arrays.stream(files).filter(file -> new File(folder, file).isDirectory()).filter(file -> s.startsWith(".") || !file.startsWith(".")).toList(); Set<String> maps = new LinkedHashSet<>();
SteamwarWorld.getBuilderWorlds(version.getVersionSuffix()).stream()
.map(SteamwarWorld::getName)
.forEach(maps::add);
File legacyFolder = new File(version.getWorldFolder(ServerStarter.LEGACY_BUILDER_BASE_PATH));
String[] files = legacyFolder.list();
if(files != null)
Arrays.stream(files).filter(file -> new File(legacyFolder, file).isDirectory()).forEach(maps::add);
return maps.stream()
.filter(file -> s.startsWith(".") || !file.startsWith("."))
.toList();
} }
}; };
} }
@@ -133,7 +144,7 @@ public class BuilderCloudCommand extends SWCommand {
if(s.isEmpty()) if(s.isEmpty())
return null; return null;
File folder = getWorldFolder(previousArguments, 2); File folder = getLegacyWorldFolder(previousArguments, 2);
if(folder == null) if(folder == null)
throw new SecurityException(); throw new SecurityException();
@@ -147,7 +158,7 @@ public class BuilderCloudCommand extends SWCommand {
@Override @Override
public Collection<String> tabCompletes(Chatter sender, PreviousArguments previousArguments, String s) { public Collection<String> tabCompletes(Chatter sender, PreviousArguments previousArguments, String s) {
File folder = getWorldFolder(previousArguments, 2); File folder = getLegacyWorldFolder(previousArguments, 2);
String[] files; String[] files;
if(folder == null || (files = folder.list()) == null) if(folder == null || (files = folder.list()) == null)
@@ -158,14 +169,36 @@ public class BuilderCloudCommand extends SWCommand {
}; };
} }
private File mapFile(ServerVersion version, String map) { private SteamwarWorld builderWorld(ServerVersion version, String map) {
return new File(version.getWorldFolder(ServerStarter.BUILDER_BASE_PATH), map); SteamwarWorld world = SteamwarWorld.getBuilderWorld(map, version.getVersionSuffix());
if(world != null)
return world;
File legacyWorld = mapFile(version, map);
if(!legacyWorld.exists())
return null;
return SteamwarWorld.getOrCreateBuilderWorld(map, version.getVersionSuffix(), legacyWorld);
} }
private File getWorldFolder(PreviousArguments previousArguments, int offset) { private boolean mapExists(ServerVersion version, String map) {
ServerVersion v = ServerVersion.get(previousArguments.userArgs[previousArguments.userArgs.length - offset]); return SteamwarWorld.getBuilderWorld(map, version.getVersionSuffix()) != null || mapFile(version, map).exists();
}
private File mapFile(ServerVersion version, String map) {
return new File(version.getWorldFolder(ServerStarter.LEGACY_BUILDER_BASE_PATH), map);
}
private File getLegacyWorldFolder(PreviousArguments previousArguments, int offset) {
ServerVersion v = getVersion(previousArguments, offset);
if(v == null) if(v == null)
return null; return null;
return new File(v.getWorldFolder(ServerStarter.BUILDER_BASE_PATH)); return new File(v.getWorldFolder(ServerStarter.LEGACY_BUILDER_BASE_PATH));
}
private ServerVersion getVersion(PreviousArguments previousArguments, int offset) {
if(previousArguments.userArgs.length < offset)
return null;
return ServerVersion.get(previousArguments.userArgs[previousArguments.userArgs.length - offset]);
} }
} }
@@ -24,11 +24,16 @@ import de.steamwar.linkage.Linked;
import de.steamwar.messages.Chatter; import de.steamwar.messages.Chatter;
import de.steamwar.messages.PlayerChatter; import de.steamwar.messages.PlayerChatter;
import de.steamwar.sql.SteamwarUser; import de.steamwar.sql.SteamwarUser;
import de.steamwar.sql.SteamwarWorld;
import de.steamwar.sql.UserPerm; import de.steamwar.sql.UserPerm;
import de.steamwar.sql.internal.Statement; import de.steamwar.sql.internal.Statement;
import de.steamwar.velocitycore.ServerStarter;
import de.steamwar.velocitycore.ServerVersion;
import de.steamwar.velocitycore.VelocityCore; import de.steamwar.velocitycore.VelocityCore;
import java.io.*; import java.io.*;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
@@ -64,12 +69,21 @@ public class GDPRQuery extends SWCommand {
copy(getClass().getClassLoader().getResourceAsStream("GDPRQueryREADME.md"), out, "README.txt"); copy(getClass().getClassLoader().getResourceAsStream("GDPRQueryREADME.md"), out, "README.txt");
sender.system("GDPR_STATUS_WORLD"); sender.system("GDPR_STATUS_WORLD");
copyBauwelt(user, out, "/home/minecraft/userworlds/" + user.getUUID().toString(), "BuildWorld12"); Set<Integer> exportedVersions = new LinkedHashSet<>();
copyBauwelt(user, out, "/home/minecraft/userworlds15/" + user.getId(), "BuildWorld15"); for(ServerVersion version : ServerVersion.values()) {
int versionSuffix = version.getVersionSuffix();
if(!exportedVersions.add(versionSuffix))
continue;
SteamwarWorld world = SteamwarWorld.getBauWorld(user, versionSuffix);
if(world != null)
copyBauwelt(user, out, world.setupAndGetStoragePath(), "BuildWorld" + versionSuffix);
else
copyLegacyBauwelt(user, out, version);
}
sender.system("GDPR_STATUS_INVENTORIES"); sender.system("GDPR_STATUS_INVENTORIES");
copyPlayerdata(user, out, "/home/minecraft/userworlds", "BuildInventories12"); copyPlayerdata(user, out, SteamwarWorld.WORLD_STORAGE, "BuildInventories");
copyPlayerdata(user, out, "/home/minecraft/userworlds15", "BuildInventories15");
sender.system("GDPR_STATUS_DATABASE"); sender.system("GDPR_STATUS_DATABASE");
sqlCSV(user, out, bannedIPs, "BannedIPs.csv"); sqlCSV(user, out, bannedIPs, "BannedIPs.csv");
@@ -230,6 +244,18 @@ public class GDPRQuery extends SWCommand {
copy(playerdata, out, outDir + "/playerdata/" + user.getUUID().toString() + ".dat"); copy(playerdata, out, outDir + "/playerdata/" + user.getUUID().toString() + ".dat");
} }
private void copyLegacyBauwelt(SteamwarUser user, ZipOutputStream out, ServerVersion version) throws IOException {
File legacyIdWorld = new File(version.getWorldFolder(ServerStarter.LEGACY_WORLDS_BASE_PATH), String.valueOf(user.getId()));
if(legacyIdWorld.exists()) {
copyBauwelt(user, out, legacyIdWorld.getPath(), "BuildWorld" + version.getVersionSuffix());
return;
}
File legacyUuidWorld = new File(version.getWorldFolder(ServerStarter.LEGACY_WORLDS_BASE_PATH), user.getUUID().toString());
if(legacyUuidWorld.exists())
copyBauwelt(user, out, legacyUuidWorld.getPath(), "BuildWorld" + version.getVersionSuffix());
}
private void copyPlayerdata(SteamwarUser user, ZipOutputStream out, String inDir, String outDir) throws IOException { private void copyPlayerdata(SteamwarUser user, ZipOutputStream out, String inDir, String outDir) throws IOException {
File worlds = new File(inDir); File worlds = new File(inDir);
String path = "playerdata/" + user.getUUID().toString() + ".dat"; String path = "playerdata/" + user.getUUID().toString() + ".dat";