forked from SteamWar/SteamWar
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:
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
Reference in New Issue
Block a user