Compare commits

...

8 Commits

Author SHA1 Message Date
Chaoscaot 1cb2e70ede Revert "Remove deprecated world management commands and associated SQL logic"
Pull Request Build / Build (pull_request) Failing after 58s
This reverts commit a7adfe37

Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-31 20:21:30 +02:00
Chaoscaot 8ce25ec19f Merge branch 'main' into world-system
Pull Request Build / Build (pull_request) Successful in 1m9s
2026-05-31 20:16:17 +02:00
Chaoscaot a7adfe378f Remove deprecated world management commands and associated SQL logic
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-20 16:44:32 +02:00
Chaoscaot 9aa363de6f Add world-based permissions and migration commands
Pull Request Build / Build (pull_request) Successful in 1m7s
- Switch bau logic from owner-scoped to world-scoped data
- Add CLI commands for world migration, templating, and archiving
- Extend world storage with team worlds and lock state support
2026-05-19 20:53:32 +02:00
Chaoscaot 5a5090c74d Remove unused import and fix incorrect import path in BuilderCloudCommand and World classes.
Pull Request Build / Build (pull_request) Successful in 1m8s
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-19 19:36:43 +02:00
Chaoscaot 99ca7205fb Merge branch 'main' into world-system
# Conflicts:
#	VelocityCore/src/de/steamwar/velocitycore/ServerStarter.java
#	VelocityCore/src/de/steamwar/velocitycore/commands/BauCommand.java
#	VelocityCore/src/de/steamwar/velocitycore/commands/BuilderCloudCommand.java
2026-05-19 19:30:30 +02:00
Chaoscaot e85604fdc4 Merge branch 'main' into world-system
Pull Request Build / Build (pull_request) Failing after 42s
2026-05-16 14:49:18 +02:00
Chaoscaot 936ee38503 Migrate builder and bau worlds to shared world records
SteamWarCI Build successful
- 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
2026-05-09 16:38:13 +02:00
33 changed files with 1604 additions and 259 deletions
@@ -133,7 +133,12 @@ public class BauSystem extends JavaPlugin implements Listener {
Bukkit.getWorlds().get(0).setGameRule(GameRule.SEND_COMMAND_FEEDBACK, false);
String identifier = BauServerInfo.getOwnerUser().getUUID().toString().replace("-", "");
String identifier;
if (BauServerInfo.getWorldId() != null) {
identifier = BauServerInfo.getWorldId().toString().replace("-", "");
} else {
identifier = BauServerInfo.getOwnerUser().getUUID().toString().replace("-", "");
}
WorldIdentifier.set("bau/" + Core.getVersion() + "/" + identifier);
}
@@ -224,4 +229,4 @@ public class BauSystem extends JavaPlugin implements Listener {
AtomicReference<BukkitTask> task = new AtomicReference<>();
task.set(runTaskTimer(plugin, () -> consumer.accept(task.get()), delay, period));
}
}
}
@@ -60,11 +60,16 @@ public enum Permission {
}
public boolean hasPermission(Player member) {
if (SteamwarUser.get(member.getUniqueId()).getId() == BauServer.getInstance().getOwnerID()) {
SteamwarUser steamwarUser = SteamwarUser.get(member.getUniqueId());
BauServer server = BauServer.getInstance();
if (!server.isTeamWorld() && steamwarUser.getId() == server.getOwnerID()) {
return this != SPECTATOR;
}
BauweltMember bauweltMember = BauweltMember.getBauMember(BauServer.getInstance().getOwner(), member.getUniqueId());
if (server.isTeamWorld() && steamwarUser.getTeam() == server.getTeamID()) {
return this != SPECTATOR;
}
BauweltMember bauweltMember = BauweltMember.getBauMember(server.getWorldID(), steamwarUser.getId());
if (bauweltMember == null) return this == SPECTATOR;
return permissionPredicate.test(bauweltMember);
}
}
}
@@ -34,16 +34,41 @@ public class BauServer {
}
private Integer owner;
private UUID world;
private Integer team;
public UUID getOwner() {
return SteamwarUser.byId(getOwnerID()).getUUID();
Integer ownerId = getOwnerID();
return ownerId == null ? null : SteamwarUser.byId(ownerId).getUUID();
}
public int getOwnerID() {
public Integer getOwnerID() {
//Lazy loading to improve startup time of the server in 1.15
if (owner == null) {
owner = BauServerInfo.getOwnerId();
}
return owner;
}
}
public boolean hasOwner() {
return getOwnerID() != null;
}
public UUID getWorldID() {
if (world == null) {
world = BauServerInfo.getWorldId();
}
return world;
}
public Integer getTeamID() {
if (team == null) {
team = BauServerInfo.getTeamId();
}
return team;
}
public boolean isTeamWorld() {
return getTeamID() != null;
}
}
@@ -47,7 +47,7 @@ public class BauInfoBauGuiItem extends BauGuiItem {
@Override
public ItemStack getItem(Player player) {
SWItem itemStack;
if (!player.getName().endsWith("")) {
if (!player.getName().endsWith("") && !BauServer.getInstance().isTeamWorld()) {
itemStack = SWItem.getPlayerSkull(SteamwarUser.get(BauServer.getInstance().getOwner()).getUserName());
} else {
itemStack = new SWItem(Material.PLAYER_HEAD, "");
@@ -47,7 +47,9 @@ public class InfoCommand extends SWCommand {
@Register(description = "BAU_INFO_COMMAND_HELP")
public void genericCommand(Player p) {
BauSystem.MESSAGE.send("BAU_INFO_COMMAND_OWNER", p, SteamwarUser.byId(bauServer.getOwnerID()).getUserName());
if (!bauServer.isTeamWorld()) {
BauSystem.MESSAGE.send("BAU_INFO_COMMAND_OWNER", p, SteamwarUser.byId(bauServer.getOwnerID()).getUserName());
}
Region region = Region.getRegion(p.getLocation());
for (Flag flag : Flag.getFlags()) {
if (!region.getRegionData().has(flag).isApplicable()) continue;
@@ -58,7 +60,7 @@ public class InfoCommand extends SWCommand {
}
if (Permission.BUILD.hasPermission(p)) {
List<BauweltMember> members = BauweltMember.getMembers(bauServer.getOwnerID());
List<BauweltMember> members = BauweltMember.getWorldMembers(bauServer.getWorldID());
Map<Permission, List<BauweltMember>> memberByPermission = new HashMap<>();
members.forEach(member -> {
if (Permission.SUPERVISOR.hasPermission(member)) {
@@ -20,6 +20,7 @@
package de.steamwar.bausystem.features.region;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.Permission;
import de.steamwar.bausystem.config.BauServer;
import de.steamwar.bausystem.region.Region;
import de.steamwar.bausystem.region.RegionUtils;
@@ -83,6 +84,9 @@ public class ColorCommand extends SWCommand {
@ClassValidator(value = Player.class, local = true)
public TypeValidator<Player> validator() {
return (commandSender, player, messageSender) -> {
if (bauServer.isTeamWorld()) {
return !messageSender.send(!Permission.SUPERVISOR.hasPermission(player), "NO_PERMISSION");
}
return !messageSender.send(!bauServer.getOwner().equals(player.getUniqueId()), "NO_PERMISSION");
};
}
@@ -68,7 +68,7 @@ public class ResetCommand extends SWCommand {
Region region = regionCheck(p);
if (region == null) return;
if (!p.getUniqueId().equals(bauServer.getOwner())) {
if (bauServer.hasOwner() && !p.getUniqueId().equals(bauServer.getOwner())) {
if (Punishment.isPunished(SteamwarUser.get(bauServer.getOwner()), Punishment.PunishmentType.NoSchemReceiving, punishment -> BauSystem.MESSAGE.send("REGION_TB_NO_SCHEMRECEIVING", p, punishment.getEndTime()))) {
return;
}
@@ -91,7 +91,7 @@ public class TestblockCommand extends SWCommand {
}
}
if (!p.getUniqueId().equals(bauServer.getOwner())) {
if (bauServer.hasOwner() && !p.getUniqueId().equals(bauServer.getOwner())) {
if (Punishment.isPunished(SteamwarUser.get(bauServer.getOwner()), Punishment.PunishmentType.NoSchemReceiving, punishment -> BauSystem.MESSAGE.send("REGION_TB_NO_SCHEMRECEIVING", p, punishment.getEndTime()))) {
return;
}
@@ -20,6 +20,7 @@
package de.steamwar.bausystem.features.world;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.Permission;
import de.steamwar.bausystem.config.BauServer;
import de.steamwar.core.CRIUWakeupEvent;
import de.steamwar.linkage.Linked;
@@ -36,12 +37,13 @@ public class AntiBauAddMemberFix implements Listener {
@EventHandler(priority = EventPriority.LOW)
public void onPlayerJoin(PlayerJoinEvent event) {
if (BauSystem.DEV_SERVER) return;
if (event.getPlayer().getUniqueId().equals(BauServer.getInstance().getOwner())) {
if (Permission.SUPERVISOR.hasPermission(event.getPlayer())) {
return;
}
if (BauweltMember.getBauMember(BauServer.getInstance().getOwner(), event.getPlayer().getUniqueId()) == null) {
if (BauweltMember.getBauMember(BauServer.getInstance().getWorldID(), event.getPlayer().getUniqueId()) == null) {
event.getPlayer().kickPlayer("");
throw new SecurityException("The player " + event.getPlayer().getName() + " joined on the server of " + SteamwarUser.byId(BauServer.getInstance().getOwnerID()).getUserName() + " without being added!");
String owner = BauServer.getInstance().isTeamWorld() ? "team " + BauServer.getInstance().getTeamID() : SteamwarUser.byId(BauServer.getInstance().getOwnerID()).getUserName();
throw new SecurityException("The player " + event.getPlayer().getName() + " joined on the server of " + owner + " without being added!");
}
}
@@ -33,7 +33,7 @@ public class AxiomPermissionCheck implements Listener {
@EventHandler
public void onAxiomHandshake(AxiomHandshakeEvent event) {
if (Permission.SUPERVISOR.hasPermission(event.getPlayer()) || BauServer.getInstance().getOwner().equals(event.getPlayer().getUniqueId())) {
if (Permission.SUPERVISOR.hasPermission(event.getPlayer()) || (BauServer.getInstance().hasOwner() && BauServer.getInstance().getOwner().equals(event.getPlayer().getUniqueId()))) {
event.setMaxBufferSize(Short.MAX_VALUE);
return;
}
@@ -28,7 +28,7 @@ import de.steamwar.data.BauLockState;
import de.steamwar.linkage.Linked;
import de.steamwar.network.packets.PacketHandler;
import de.steamwar.network.packets.server.BaulockUpdatePacket;
import de.steamwar.sql.UserConfig;
import de.steamwar.sql.SteamwarWorld;
import lombok.Getter;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
@@ -37,13 +37,13 @@ import org.bukkit.event.Listener;
@Linked
public class BauLockStateScoreboard extends PacketHandler implements ScoreboardElement, Listener {
private static final String BAU_LOCK_CONFIG_NAME = "baulockstate";
@Getter
private BauLockState lockState = loadLockState();
private BauLockState loadLockState() {
String state = UserConfig.getConfig(BauServer.getInstance().getOwner(), BAU_LOCK_CONFIG_NAME);
if (BauServer.getInstance().isTeamWorld()) return BauLockState.OPEN;
SteamwarWorld world = SteamwarWorld.getWorld(BauServer.getInstance().getWorldID());
String state = world == null ? null : world.getLockState();
return state == null ? BauLockState.OPEN : BauLockState.valueOf(state);
}
@@ -69,6 +69,7 @@ public class BauLockStateScoreboard extends PacketHandler implements ScoreboardE
@Override
public String get(Region region, Player p) {
if (BauServer.getInstance().isTeamWorld()) return null;
if (!BauServer.getInstance().getOwner().equals(p.getUniqueId())) {
return null;
}
@@ -42,7 +42,11 @@ public class KickallCommand extends SWCommand {
if (!Permission.OWNER.hasPermission(player)) return;
Bukkit.getOnlinePlayers().forEach(p -> {
if (!bauServer.getOwner().equals(p.getUniqueId())) p.kickPlayer("");
if (bauServer.isTeamWorld()) {
if (!Permission.SUPERVISOR.hasPermission(p)) p.kickPlayer("");
} else if (!bauServer.getOwner().equals(p.getUniqueId())) {
p.kickPlayer("");
}
});
}
}
@@ -143,17 +143,14 @@ public class SpectatorListener implements Listener {
public void onPlayerJoin(PlayerJoinEvent event) {
enableOrDisableTechhider();
if (BauSystem.DEV_SERVER) return;
if (event.getPlayer().getUniqueId().equals(BauServer.getInstance().getOwner())) {
if (Permission.SUPERVISOR.hasPermission(event.getPlayer())) {
return;
}
BauweltMember bauweltMember = BauweltMember.getBauMember(BauServer.getInstance().getOwner(), event.getPlayer().getUniqueId());
BauweltMember bauweltMember = BauweltMember.getBauMember(BauServer.getInstance().getWorldID(), event.getPlayer().getUniqueId());
if (bauweltMember == null) {
event.getPlayer().kickPlayer("");
return;
}
if (Permission.SUPERVISOR.hasPermission(event.getPlayer())) {
return;
}
if (!anySupervisorOnline(null)) {
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
event.getPlayer().kickPlayer("");
+11 -1
View File
@@ -11,13 +11,23 @@ import de.steamwar.commands.profiler.ProfilerCommand
import de.steamwar.commands.user.UserCommand
import de.steamwar.commands.user.UserInfoCommand
import de.steamwar.commands.user.UserSearchCommand
import de.steamwar.commands.world.MigrationCommand
import de.steamwar.commands.world.SaveToStorageCommand
import de.steamwar.commands.world.TemplateCommand
import de.steamwar.commands.world.TemplateCreateCommand
import de.steamwar.commands.world.WorldCommand
fun main(args: Array<String>) =
SteamWar()
.subcommands(
DatabaseCommand().subcommands(InfoCommand(), ResetCommand()),
UserCommand().subcommands(UserInfoCommand(), UserSearchCommand()),
WorldCommand().subcommands(
MigrationCommand(),
SaveToStorageCommand(),
TemplateCommand().subcommands(TemplateCreateCommand())
),
DevCommand(),
ProfilerCommand()
)
.main(args)
.main(args)
+243
View File
@@ -0,0 +1,243 @@
package de.steamwar.commands.world
import com.github.ajalt.clikt.core.CliktCommand
import de.steamwar.db.Database
import de.steamwar.sql.SteamwarUser
import de.steamwar.sql.SteamwarWorld
import de.steamwar.sql.UserConfig
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
import java.io.File
import java.sql.SQLException
import java.util.UUID
class MigrationCommand : CliktCommand(name = "migrate") {
private val userWorldPattern = Regex("""userworlds(\d+)""")
private val builderWorldPattern = Regex("""builder(\d+)""")
private val trailingVersionPattern = Regex("""(\d+)$""")
override fun run() {
Database.ensureConnected()
val bau = migrateBauWorlds()
val builder = migrateBuilderWorlds()
val arena = migrateArenaWorlds()
migrateBauLockConfig()
migrateBauweltMembers()
echo("Imported ${bau.imported} build worlds, skipped ${bau.skipped}, missing users ${bau.missingOwner}.")
echo("Imported ${builder.imported} builder worlds, skipped ${builder.skipped}.")
echo("Imported ${arena.imported} arena worlds, skipped ${arena.skipped}.")
echo("World migration completed.")
}
private fun migrateBauWorlds(): MigrationStats {
val stats = MigrationStats()
val worldsRoot = File("/worlds")
worldsRoot.listFiles { file -> file.isDirectory && userWorldPattern.matches(file.name) }
?.forEach { versionFolder ->
val version = userWorldPattern.matchEntire(versionFolder.name)!!.groupValues[1].toInt()
versionFolder.listFiles { file -> file.isDirectory && isWorldDirectory(file) }
?.sortedBy { it.name }
?.forEach { legacyWorld ->
val owner = legacyWorld.ownerUser()
if (owner == null) {
stats.missingOwner++
echo("Skipped build world ${legacyWorld.path}: no matching user.")
return@forEach
}
val lockState = UserConfig.getConfig(owner.id.value, BAU_LOCK_CONFIG_NAME)
val world = SteamwarWorld.getOrCreateBauWorld(owner, owner.userName, version, legacyWorld)
if (world.lockState == null && lockState != null) {
world.changeLockState(lockState)
}
stats.imported++
}
} ?: run {
stats.skipped++
echo("Skipped build worlds: /worlds does not exist.")
}
return stats
}
private fun migrateBuilderWorlds(): MigrationStats {
val stats = MigrationStats()
val worldsRoot = File("/worlds")
worldsRoot.listFiles { file -> file.isDirectory && builderWorldPattern.matches(file.name) }
?.forEach { versionFolder ->
val version = builderWorldPattern.matchEntire(versionFolder.name)!!.groupValues[1].toInt()
versionFolder.listFiles { file -> file.isDirectory && isWorldDirectory(file) }
?.sortedBy { it.name }
?.forEach { legacyWorld ->
SteamwarWorld.getOrCreateBuilderWorld(legacyWorld.name, version, legacyWorld)
stats.imported++
}
} ?: run {
stats.skipped++
echo("Skipped builder worlds: /worlds does not exist.")
}
return stats
}
private fun migrateArenaWorlds(): MigrationStats {
val stats = MigrationStats()
val configs = parseGameModeConfigs(File("/configs/GameModes"))
if (configs.isEmpty()) {
stats.skipped++
echo("Skipped arena worlds: no game mode configs found in /configs/GameModes.")
return stats
}
for (config in configs) {
val version = trailingVersionPattern.find(config.serverFolder)?.value?.toIntOrNull()
if (version == null) {
stats.skipped++
echo("Skipped arena mode ${config.modeName}: server folder '${config.serverFolder}' has no version suffix.")
continue
}
val arenaRoot = File("/servers/${config.serverFolder}/arenas")
for (map in config.maps) {
val legacyWorld = File(arenaRoot, map)
if (!isWorldDirectory(legacyWorld)) {
stats.skipped++
continue
}
SteamwarWorld.getOrCreateArenaWorld(config.modeName, map, version, legacyWorld)
stats.imported++
}
}
return stats
}
private fun migrateBauLockConfig() {
transaction(Database.db) {
exec(
"""
UPDATE `world` w
JOIN `UserConfig` uc ON uc.`User` = w.`Owner` AND uc.`Config` = 'baulockstate'
SET w.`LockState` = uc.`Value`
WHERE w.`Owner` IS NOT NULL AND w.`LockState` IS NULL
""".trimIndent()
)
}
}
private fun migrateBauweltMembers() {
if (!columnExists("BauweltMember", "WorldID") || !columnExists("BauweltMember", "BauweltID")) {
echo("Skipped BauweltMember data migration: expected WorldID and BauweltID columns are not both present.")
return
}
execIgnore(
"""
INSERT IGNORE INTO `BauweltMember` (`WorldID`, `MemberID`, `Build`, `WorldEdit`, `World`)
SELECT w.`id`, bm.`MemberID`, bm.`Build`, bm.`WorldEdit`, bm.`World`
FROM `BauweltMember` bm
JOIN `world` w ON w.`Owner` = bm.`BauweltID` AND w.`Deleted` = 0
""".trimIndent(),
"Skipped BauweltMember data migration"
)
}
private fun parseGameModeConfigs(configRoot: File): List<ArenaMigrationConfig> {
if (!configRoot.isDirectory) return emptyList()
return configRoot.listFiles { file -> file.isFile && file.extension == "yml" && !file.name.endsWith(".kits.yml") }
?.mapNotNull { file ->
val server = parseServerBlock(file)
val folder = server.folder ?: return@mapNotNull null
ArenaMigrationConfig(file.nameWithoutExtension, folder, server.maps)
}
?: emptyList()
}
private fun parseServerBlock(file: File): ParsedServerBlock {
var inServer = false
var inMaps = false
var folder: String? = null
val maps = mutableListOf<String>()
file.readLines().forEach { line ->
val trimmed = line.trim()
if (trimmed.isEmpty() || trimmed.startsWith("#")) return@forEach
val indent = line.indexOfFirst { !it.isWhitespace() }.let { if (it == -1) 0 else it }
if (indent == 0) {
inServer = trimmed == "Server:"
inMaps = false
return@forEach
}
if (!inServer) return@forEach
when {
indent <= 2 && trimmed.startsWith("Folder:") -> folder = trimmed.substringAfter(":").cleanYamlValue()
indent <= 2 && trimmed.startsWith("Maps:") -> {
val value = trimmed.substringAfter(":").trim()
inMaps = true
if (value.startsWith("[") && value.endsWith("]")) {
maps += value.removePrefix("[").removeSuffix("]")
.split(",")
.map { it.cleanYamlValue() }
.filter { it.isNotEmpty() }
inMaps = false
}
}
inMaps && trimmed.startsWith("-") -> maps += trimmed.removePrefix("-").cleanYamlValue()
inMaps && indent <= 2 -> inMaps = false
}
}
return ParsedServerBlock(folder, maps)
}
private fun File.ownerUser(): SteamwarUser? {
name.toIntOrNull()?.let { return SteamwarUser.byId(it) }
return runCatching { SteamwarUser.get(UUID.fromString(name)) }.getOrNull()
}
private fun isWorldDirectory(file: File): Boolean =
file.isDirectory && (File(file, "level.dat").isFile || File(file, "region").isDirectory)
private fun String.cleanYamlValue(): String =
trim().trim('"').trim('\'')
private fun columnExists(table: String, column: String): Boolean =
transaction(Database.db) {
exec("SHOW COLUMNS FROM `$table` LIKE '$column'") { result -> result.next() } ?: false
}
private fun execIgnore(sql: String, prefix: String) {
try {
transaction(Database.db) {
exec(sql)
}
} catch (e: SQLException) {
echo("$prefix: ${e.message}")
}
}
private data class MigrationStats(
var imported: Int = 0,
var skipped: Int = 0,
var missingOwner: Int = 0,
)
private data class ArenaMigrationConfig(
val modeName: String,
val serverFolder: String,
val maps: List<String>,
)
private data class ParsedServerBlock(
val folder: String?,
val maps: List<String>,
)
private companion object {
private const val BAU_LOCK_CONFIG_NAME = "baulockstate"
}
}
@@ -0,0 +1,30 @@
package de.steamwar.commands.world
import com.github.ajalt.clikt.core.CliktCommand
import de.steamwar.db.Database
import de.steamwar.sql.SteamwarWorld
import java.time.LocalTime
class SaveToStorageCommand : CliktCommand(name = "saveToStorage") {
override fun run() {
Database.ensureConnected()
var archived = 0
for (world in SteamwarWorld.getWorldsToArchive()) {
if (!beforeCutoff()) {
echo("Stopping before 04:00. Archived $archived worlds.")
return
}
if (world.archiveIfNeeded()) {
archived++
echo("Archived ${world.uuid} (${world.type}/${world.name}).")
}
}
echo("Storage save completed. Archived $archived worlds.")
}
private fun beforeCutoff(): Boolean =
LocalTime.now().isBefore(LocalTime.of(4, 0))
}
+24
View File
@@ -0,0 +1,24 @@
package de.steamwar.commands.world
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.parameters.arguments.argument
import com.github.ajalt.clikt.parameters.types.int
import com.github.ajalt.clikt.parameters.types.path
import de.steamwar.db.Database
import de.steamwar.sql.SteamwarWorld
class TemplateCommand : CliktCommand(name = "template") {
override fun run() = Unit
}
class TemplateCreateCommand : CliktCommand(name = "create") {
private val name by argument()
private val version by argument().int()
private val source by argument().path(canBeFile = false, mustExist = true)
override fun run() {
Database.ensureConnected()
SteamwarWorld.getOrCreateTemplateWorld(name, version, source.toFile())
echo("Template world '$name' created.")
}
}
+7
View File
@@ -0,0 +1,7 @@
package de.steamwar.commands.world
import com.github.ajalt.clikt.core.CliktCommand
class WorldCommand : CliktCommand(name = "world") {
override fun run() = Unit
}
@@ -19,7 +19,6 @@
package de.steamwar.sql
import de.steamwar.sql.BauweltMemberTable.bauweltId
import de.steamwar.sql.internal.useDb
import org.jetbrains.exposed.v1.core.and
import org.jetbrains.exposed.v1.core.dao.id.CompositeID
@@ -32,45 +31,37 @@ import org.jetbrains.exposed.v1.jdbc.insertIgnore
import java.util.*
object BauweltMemberTable : CompositeIdTable("BauweltMember") {
val bauweltId = reference("BauweltID", SteamwarUserTable).index()
val worldId = reference("WorldID", WorldTable).index()
val memberId = reference("MemberID", SteamwarUserTable).index()
val build = bool("Build")
val worldEdit = bool("WorldEdit")
val world = bool("World")
override val primaryKey = PrimaryKey(bauweltId, memberId)
override val primaryKey = PrimaryKey(worldId, memberId)
init {
addIdColumn(bauweltId)
addIdColumn(worldId)
addIdColumn(memberId)
}
}
class BauweltMember(id: EntityID<CompositeID>) : CompositeEntity(id) {
companion object : CompositeEntityClass<BauweltMember>(BauweltMemberTable) {
private val cache = mutableMapOf<Int, BauweltMember>()
private val cache = mutableMapOf<Pair<UUID, Int>, BauweltMember>()
private fun cache(member: BauweltMember) =
cache.put(member.memberID, member)
cache.put(member.worldID to member.memberID, member)
@JvmStatic
fun clear() =
cache.clear()
@JvmStatic
@Deprecated("Use addMember(ownerId: Int, newMemberId: Int)")
fun addMember(ownerId: UUID, newMemberId: UUID) =
addMember(SteamwarUser.get(ownerId)!!.id, SteamwarUser.get(newMemberId)!!.id)
@JvmStatic
fun addMember(ownerId: Int, newMemberId: Int) =
addMember(EntityID(ownerId, SteamwarUserTable), EntityID(newMemberId, SteamwarUserTable))
fun addMember(ownerId: EntityID<Int>, newMemberId: EntityID<Int>) =
fun addMember(worldId: UUID, newMemberId: Int) =
useDb {
BauweltMemberTable.insertIgnore {
it[bauweltId] = ownerId
it[memberId] = newMemberId
it[BauweltMemberTable.worldId] = EntityID(worldId, WorldTable)
it[memberId] = EntityID(newMemberId, SteamwarUserTable)
it[build] = false
it[worldEdit] = false
it[world] = false
@@ -78,31 +69,32 @@ class BauweltMember(id: EntityID<CompositeID>) : CompositeEntity(id) {
}
@JvmStatic
@Deprecated("Use getBauMember(bauwelt: Int, member: Int)")
fun getBauMember(bauwelt: UUID, member: UUID) =
fun addMember(worldId: UUID, newMemberId: UUID) =
addMember(worldId, SteamwarUser.get(newMemberId)!!.id.value)
@JvmStatic
fun getBauMember(world: UUID, member: Int) =
cache[world to member]
?: useDb {
find { (BauweltMemberTable.worldId eq world) and (BauweltMemberTable.memberId eq member) }.firstOrNull()?.also { cache(it) }
}
@JvmStatic
fun getBauMember(world: UUID, member: UUID) =
getBauMember(world, SteamwarUser.get(member)!!.id.value)
@JvmStatic
fun getWorldMembers(world: UUID) =
useDb {
find { (bauweltId eq SteamwarUser.get(bauwelt)!!.id) and (BauweltMemberTable.memberId eq SteamwarUser.get(member)!!.id) }.firstOrNull()?.also { cache(it) }
find { BauweltMemberTable.worldId eq world }.toList().also { it.forEach { cache(it) } }
}
@JvmStatic
fun getBauMember(bauwelt: Int, member: Int) =
useDb {
find { (bauweltId eq bauwelt) and (BauweltMemberTable.memberId eq member) }.firstOrNull()?.also { cache(it) }
}
@JvmStatic
@Deprecated("Use getMembers(bauwelt: Int)")
fun getMembers(bauwelt: UUID) =
getMembers(SteamwarUser.get(bauwelt)!!.id.value)
@JvmStatic
fun getMembers(bauwelt: Int) =
useDb {
find { bauweltId eq bauwelt }.toList().also { it.forEach { cache(it) } }
}
fun getMembers(world: UUID) =
getWorldMembers(world)
}
val bauweltID by BauweltMemberTable.bauweltId.transform({ EntityID(it, SteamwarUserTable) }, { it.value })
val worldID by BauweltMemberTable.worldId.transform({ EntityID(it, WorldTable) }, { it.value })
val memberID by BauweltMemberTable.memberId.transform({ EntityID(it, SteamwarUserTable) }, { it.value })
private var worldEditInternal by BauweltMemberTable.worldEdit
var worldEdit: Boolean
@@ -144,4 +136,4 @@ class BauweltMember(id: EntityID<CompositeID>) : CompositeEntity(id) {
useDb {
delete()
}
}
}
@@ -50,6 +50,8 @@ enum class UserPerm {
PUNISHMENTS,
TICKET_LOG,
BUILD,
BUILD_EXTRA_WORLDS,
BUILD_UNLIMITED_WORLDS,
CHECK,
MODERATION,
ADMINISTRATION;
@@ -95,4 +97,4 @@ enum class UserPerm {
}
data class Prefix(val colorCode: String, val chatPrefix: String, val teamPrefix: Boolean = false)
}
}
+345
View File
@@ -0,0 +1,345 @@
/*
* 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.java.UUIDTable
import org.jetbrains.exposed.v1.core.eq
import org.jetbrains.exposed.v1.core.not
import org.jetbrains.exposed.v1.core.or
import org.jetbrains.exposed.v1.core.SortOrder
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 team = reference("Team", TeamTable).nullable()
val lockState = varchar("LockState", 32).nullable()
val deleted = bool("Deleted").default(false)
val created = timestamp("Created").defaultExpression(CurrentTimestamp)
val lastUsed = timestamp("LastUsed").defaultExpression(CurrentTimestamp)
}
enum class WorldType {
BAU,
BUILDER,
TEAM,
ARENA,
TEMPLATE,
}
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 team by WorldTable.team
private set
var lockState by WorldTable.lockState
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
}
fun archiveIfNeeded(): Boolean {
if (!shouldArchive) return false
File(ARCHIVE_WORLD_STORAGE).mkdirs()
return archiveWorld() == 0
}
fun changeVersion(newVersion: Int) = useDb {
version = newVersion
}
fun changeLockState(newLockState: String?) = useDb {
lockState = newLockState
}
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"
const val DEFAULT_BAU_WORLD_LIMIT = 3
const val EXTRA_BAU_WORLD_LIMIT = 10
const val TEAM_WORLD_LIMIT = 2
@JvmStatic
fun getWorld(uuid: UUID) = useDb {
findById(uuid)
}
@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
fun getBauWorld(user: SteamwarUser, name: String, version: Int) = useDb {
find {
(WorldTable.owner eq user.id) and
(WorldTable.name eq name) and
(WorldTable.version eq version) and
(WorldTable.type eq WorldType.BAU) and
not(WorldTable.deleted)
}.firstOrNull()
}
@JvmStatic
fun getBauWorlds(user: SteamwarUser) = useDb {
find {
(WorldTable.owner eq user.id) and
(WorldTable.type eq WorldType.BAU) and
not(WorldTable.deleted)
}.orderBy(WorldTable.created to SortOrder.ASC).toList()
}
@JvmStatic
fun countBauWorlds(user: SteamwarUser) = useDb {
find {
(WorldTable.owner eq user.id) and
(WorldTable.type eq WorldType.BAU) and
not(WorldTable.deleted)
}.count()
}
@JvmStatic
@JvmOverloads
fun getOrCreateBauWorld(user: SteamwarUser, version: Int, prototype: File? = null): SteamwarWorld =
getBauWorld(user, version) ?: createWorld(user, user.userName, version, WorldType.BAU, prototype)
@JvmStatic
@JvmOverloads
fun getOrCreateBauWorld(user: SteamwarUser, name: String, version: Int, prototype: File? = null): SteamwarWorld =
getBauWorld(user, name, version) ?: createWorld(user, name, 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
fun getTeamWorld(team: Team, name: String, version: Int) = useDb {
find {
(WorldTable.team eq team.id) and
(WorldTable.name eq name) and
(WorldTable.version eq version) and
(WorldTable.type eq WorldType.TEAM) and
not(WorldTable.deleted)
}.firstOrNull()
}
@JvmStatic
fun getTeamWorlds(team: Team) = useDb {
find {
(WorldTable.team eq team.id) and
(WorldTable.type eq WorldType.TEAM) and
not(WorldTable.deleted)
}.orderBy(WorldTable.created to SortOrder.ASC).toList()
}
@JvmStatic
@JvmOverloads
fun getOrCreateTeamWorld(team: Team, name: String, version: Int, prototype: File? = null): SteamwarWorld =
getTeamWorld(team, name, version) ?: createWorld(null, name, version, WorldType.TEAM, prototype, team)
@JvmStatic
fun countTeamWorlds(team: Team) = useDb {
find {
(WorldTable.team eq team.id) and
(WorldTable.type eq WorldType.TEAM) and
not(WorldTable.deleted)
}.count()
}
@JvmStatic
fun getArenaWorld(mode: String, map: String, version: Int) = useDb {
find {
(WorldTable.name eq arenaWorldName(mode, map)) and
(WorldTable.version eq version) and
(WorldTable.type eq WorldType.ARENA) and
not(WorldTable.deleted)
}.firstOrNull()
}
@JvmStatic
@JvmOverloads
fun getOrCreateArenaWorld(mode: String, map: String, version: Int, prototype: File? = null): SteamwarWorld =
getArenaWorld(mode, map, version) ?: createWorld(null, arenaWorldName(mode, map), version, WorldType.ARENA, prototype)
@JvmStatic
fun getTemplateWorld(name: String, version: Int) = useDb {
find {
(WorldTable.name eq name) and
(WorldTable.version eq version) and
(WorldTable.type eq WorldType.TEMPLATE) and
not(WorldTable.deleted)
}.firstOrNull()
}
@JvmStatic
fun getTemplateWorlds() = useDb {
find {
(WorldTable.type eq WorldType.TEMPLATE) and
not(WorldTable.deleted)
}.orderBy(WorldTable.name to SortOrder.ASC).toList()
}
@JvmStatic
fun getTemplateWorlds(version: Int) = useDb {
find {
(WorldTable.version eq version) and
(WorldTable.type eq WorldType.TEMPLATE) and
not(WorldTable.deleted)
}.orderBy(WorldTable.name to SortOrder.ASC).toList()
}
@JvmStatic
@JvmOverloads
fun getOrCreateTemplateWorld(name: String, version: Int, prototype: File? = null): SteamwarWorld =
getTemplateWorld(name, version) ?: createWorld(null, name, version, WorldType.TEMPLATE, prototype)
@JvmStatic
fun getAllActiveWorlds() = useDb {
find { not(WorldTable.deleted) }.toList()
}
@JvmStatic
fun getWorldsToArchive() = useDb {
find {
not(WorldTable.deleted) or (WorldTable.deleted eq true)
}.toList().filter { it.shouldArchive }
}
private fun arenaWorldName(mode: String, map: String) = "$mode/$map"
@JvmStatic
@JvmOverloads
fun createWorld(user: SteamwarUser?, name: String, version: Int, type: WorldType, prototype: File? = null, team: Team? = null): SteamwarWorld {
val world = useDb { new {
this.name = name
this.version = version
this.type = type
this.owner = user?.id
this.team = team?.id
} }
world.setupAndGetStoragePath(prototype)
return world
}
}
}
@@ -20,14 +20,54 @@
package de.steamwar.providers;
import de.steamwar.sql.SteamwarUser;
import de.steamwar.sql.WorldType;
import org.bukkit.Bukkit;
import java.util.UUID;
public class BauServerInfo {
private static Integer bauOwner = null;
private static Integer bauTeam = null;
private static UUID bauWorld = null;
private static WorldType bauType = null;
static {
String ownerProperty = System.getProperty("bauOwner");
if (ownerProperty != null && !ownerProperty.isBlank()) {
try {
bauOwner = Integer.parseInt(ownerProperty);
} catch (NumberFormatException ignored) {
}
}
String teamProperty = System.getProperty("bauTeam");
if (teamProperty != null && !teamProperty.isBlank()) {
try {
bauTeam = Integer.parseInt(teamProperty);
} catch (NumberFormatException ignored) {
}
}
String worldProperty = System.getProperty("bauWorld");
if (worldProperty != null && !worldProperty.isBlank()) {
try {
bauWorld = UUID.fromString(worldProperty);
} catch (IllegalArgumentException ignored) {
}
}
String typeProperty = System.getProperty("bauType");
if (typeProperty != null && !typeProperty.isBlank()) {
try {
bauType = WorldType.valueOf(typeProperty);
} catch (IllegalArgumentException ignored) {
}
}
try {
bauOwner = Integer.parseInt(Bukkit.getWorlds().get(0).getName());
if (bauOwner == null) {
bauOwner = Integer.parseInt(Bukkit.getWorlds().get(0).getName());
}
} catch (NumberFormatException ignored) {
}
}
@@ -37,11 +77,23 @@ public class BauServerInfo {
}
public static boolean isBauServer() {
return bauOwner != null;
return bauOwner != null || bauWorld != null || bauTeam != null;
}
public static SteamwarUser getOwnerUser() {
if (bauOwner == null) return null;
return SteamwarUser.byId(bauOwner);
}
public static UUID getWorldId() {
return bauWorld;
}
public static Integer getTeamId() {
return bauTeam;
}
public static WorldType getWorldType() {
return bauType;
}
}
@@ -29,6 +29,7 @@ import java.util.function.Consumer;
@Getter
public class Bauserver extends Subserver {
private static final Map<UUID, Bauserver> servers = new HashMap<>();
private static final Map<UUID, Bauserver> serversByWorld = new HashMap<>();
public static Bauserver get(UUID owner) {
synchronized (servers) {
@@ -36,25 +37,43 @@ public class Bauserver extends Subserver {
}
}
public static Bauserver getByWorld(UUID world) {
synchronized (serversByWorld) {
return serversByWorld.get(world);
}
}
private final UUID owner;
private final UUID world;
public Bauserver(String serverName, UUID owner, int port, ProcessBuilder processBuilder, Runnable shutdownCallback) {
this(serverName, owner, port, processBuilder, shutdownCallback, null);
this(serverName, owner, owner, port, processBuilder, shutdownCallback, null);
}
public Bauserver(String serverName, UUID owner, int port, ProcessBuilder processBuilder, Runnable shutdownCallback, Consumer<Exception> failureCallback) {
this(serverName, owner, owner, port, processBuilder, shutdownCallback, failureCallback);
}
public Bauserver(String serverName, UUID owner, UUID world, int port, ProcessBuilder processBuilder, Runnable shutdownCallback, Consumer<Exception> failureCallback) {
super(serverName, port, processBuilder, shutdownCallback, failureCallback);
this.owner = owner;
this.world = world;
synchronized (servers) {
servers.put(owner, this);
servers.putIfAbsent(owner, this);
}
synchronized (serversByWorld) {
serversByWorld.put(world, this);
}
}
@Override
protected void unregister() {
synchronized (servers) {
servers.remove(owner);
servers.remove(owner, this);
}
synchronized (serversByWorld) {
serversByWorld.remove(world);
}
super.unregister();
}
+3 -3
View File
@@ -37,8 +37,8 @@ if __name__ == "__main__":
with open(configfile, 'r') as file:
gamemode = yaml.load(file)
builderworld = path.expanduser(f'/worlds/builder{version}/{worldname}')
arenaworld = f'/servers/{gamemode["Server"]["Folder"]}/arenas/{worldname}'
builderworld = sys.argv[4] if len(sys.argv) > 4 else path.expanduser(f'/worlds/builder{version}/{worldname}')
arenaworld = sys.argv[5] if len(sys.argv) > 5 else f'/servers/{gamemode["Server"]["Folder"]}/arenas/{worldname}'
if path.exists(arenaworld):
backupworld = path.expanduser(f'/mnt/storage/backup/arenas/{datetime.datetime.now()}-{worldname}-{version}.tar.xz')
@@ -91,7 +91,7 @@ if __name__ == "__main__":
if path.exists(arenaworld):
shutil.rmtree(arenaworld)
os.makedirs(f'{arenaworld}/backup')
os.makedirs(f'{arenaworld}/backup', exist_ok=True)
shutil.copy2(f'{builderworld}/level.dat', f'{arenaworld}/backup/level.dat')
shutil.copytree(f'{builderworld}/region', f'{arenaworld}/backup/region')
@@ -219,22 +219,22 @@ IGNORE_ALREADY = §cYou are already ignoring this player.
IGNORE_MESSAGE = §7You are now ignoring §e{0}§8.
#BauCommand
BAU_ADDMEMBER_USAGE = §8/§7build addmember §8[§eplayer§8]
BAU_ADDMEMBER_USAGE = §8/§7build addmember §8[§eworld/all§8] §8[§eplayer§8]
BAU_ADDMEMBER_SELFADD = §cYou don't have to add yourself!
BAU_ADDMEMBER_ISADDED = §cThis player is already a member of your world.
BAU_ADDMEMBER_ADDED = §aThe player was added to your world.
BAU_ADDMEMBER_ADDED_TARGET = §aYou have been added to the world of §e{0}§a.
BAU_TP_USAGE = §8/§7build tp §8[§eplayer§8]
BAU_TP_USAGE = §8/§7build tp §8[§eplayer§8] §8<§eworld§8>
BAU_TP_NOALLOWED = §cYou are not allowed to teleport to this player's world.
BAU_LOCKED_NOALLOWED = §cThe build server is currently locked.
BAU_LOCK_BLOCKED = §cYour build lock has prevented §e{0} §cfrom joining.
BAU_LOCKED_OPTIONS = §7Build server lock options§8: §cnobody§8, §eserverteam§8, §eteam_and_serverteam§8, §eteam§8, §aopen
BAU_LOCKED_OPTIONS = §8/§7build lock §8[§eworld§8] §8[§eoption§8] §7Options§8: §cnobody§8, §eserverteam§8, §eteam_and_serverteam§8, §eteam§8, §aopen
BAU_LOCKED_NOBODY = §7You have locked your build server for all players.
BAU_LOCKED_SERVERTEAM = §7You have locked your build server for all players except added server team members.
BAU_LOCKED_TEAM_AND_SERVERTEAM = §7You have locked your build server for all players except added team members and server team members.
BAU_LOCKED_TEAM = §7You have locked your build server for all players except added team members.
BAU_LOCKED_OPEN = §7You have opened your build server for all added players.
BAU_DELMEMBER_USAGE = §8/§7build delmember §8[§eplayer§8]
BAU_DELMEMBER_USAGE = §8/§7build delmember §8[§eworld/all§8] §8[§eplayer§8]
BAU_DELMEMBER_SELFDEL = §cYou cannot remove yourself!
BAU_DELMEMBER_DELETED = §cPlayer was removed.
BAU_DELMEMBER_DELETED_TARGET = §cYou were removed from the world of §e{0}.
@@ -244,9 +244,23 @@ BAU_DELETE_DELETED = §aYour world is being reset.
BAU_DELETE_GUI_NAME = §eDo you really want to delete the world?
BAU_DELETE_GUI_CANCEL = §cCancel
BAU_DELETE_GUI_DELETE = §aDelete
BAU_WORLD_GUI_TITLE = §eYour worlds
BAU_WORLD_CREATE_USAGE = §8/§7build world create §8[§ename§8] §8[§eversion§8] §8[§etemplate§8]
BAU_WORLD_START_USAGE = §8/§7build world start §8[§eworld§8]
BAU_WORLD_RENAME_USAGE = §8/§7build world rename §8[§eworld§8] §8[§enew name§8]
BAU_WORLD_DELETE_USAGE = §8/§7build world delete §8[§eworld§8]
BAU_WORLD_UPGRADE_USAGE = §8/§7build world upgrade §8[§eworld§8] §8[§eversion§8]
BAU_WORLD_VERSION = §cUnknown version.
BAU_WORLD_UNKNOWN = §cUnknown world.
BAU_WORLD_EXISTS = §cA world with this name already exists for this version.
BAU_WORLD_LIMIT = §cYou have reached your build world limit of §e{0}§c. Delete one first.
BAU_WORLD_OVER_LIMIT_DELETE = §cYou currently have more than your allowed §e{0}§c build worlds. Delete one before creating, renaming, or upgrading worlds.
BAU_WORLD_CREATED = §aWorld created.
BAU_WORLD_RENAMED = §aWorld renamed.
BAU_WORLD_UPGRADED = §aWorld version updated.
BAU_START_ALREADY = §cThis server is already starting.
BAU_MEMBER_NOMEMBER = §cThis player is no member of your world!
BAU_MEMBER_SET_USAGE = §8/§7build {0} §8[§eplayer§8]
BAU_MEMBER_SET_USAGE = §8/§7build {0} §8[§eworld/all§8] §8[§eplayer§8]
BAU_MEMBER_SET_TARGET = §7You are now a §e{1}§7 on the world of §e{0}§7.
BAU_MEMBER_SET = §7The player is now a §e{0}§7.
BAU_MEMBER_SET_SPECTATOR = spectator
@@ -524,6 +538,20 @@ TEAM_EVENT_LEFT = §7Your team no longer takes part in this event
TEAM_EVENT_JOINED = §7Your team now takes part in the event §e{0}§7!
TEAM_EVENT_HOW_TO_LEAVE = §7To cancel the participation repeat the command.
#Team Worlds
TEAM_WORLD_GUI_TITLE = §eTeam worlds
TEAM_WORLD_CREATE_USAGE = §8/§7team world create §8[§ename§8] §8[§eversion§8] §8[§etemplate§8]
TEAM_WORLD_RENAME_USAGE = §8/§7team world rename §8[§eworld§8] §8[§enew name§8]
TEAM_WORLD_UPGRADE_USAGE = §8/§7team world upgrade §8[§eworld§8] §8[§eversion§8]
TEAM_WORLD_DELETE_USAGE = §8/§7team world delete §8[§eworld§8]
TEAM_WORLD_EXISTS = §cA team world with this name already exists for this version.
TEAM_WORLD_LIMIT = §cYour team already has the maximum of §e{0}§c worlds. Delete one first.
TEAM_WORLD_OVER_LIMIT_DELETE = §cYour team currently has more than the allowed §e{0}§c worlds. Delete one before creating, renaming, or upgrading worlds.
TEAM_WORLD_CREATED = §aTeam world created.
TEAM_WORLD_RENAMED = §aTeam world renamed.
TEAM_WORLD_UPGRADED = §aTeam world version updated.
TEAM_WORLD_DELETED = §aTeam world deleted.
#Team Color
TEAM_COLOR_TITLE = Choose color
@@ -682,7 +710,7 @@ LOCK_LOCALE_CHANGED = §aLanguage saved
#Builder Cloud
BUILDERCLOUD_USAGE = §8/§7buildercloud §8[§eversion§8] §8[§emap§8]
BUILDERCLOUD_CREATE_USAGE = §8/§7buildercloud create §8[§eversion§8] §8[§emap§8] §8<§7generator§8>
BUILDERCLOUD_CREATE_USAGE = §8/§7buildercloud create §8[§eversion§8] §8[§emap§8] §8[§etemplate§8]
BUILDERCLOUD_RENAME_USAGE = §8/§7buildercloud rename §8[§eversion§8] §8[§emap§8] §8[§enew name§8]
BUILDERCLOUD_DEPLOY_USAGE = §8/§7deployarena §8[§egamemode§8] §8[§eversion§8] §8[§emap§8]
BUILDERCLOUD_DEPLOY_FINISHED = §7Map deployment finished.
@@ -201,22 +201,22 @@ IGNORE_ALREADY = §cDu ignorierst diesen Spieler bereits.
IGNORE_MESSAGE = §7Du ignorierst nun §e{0}§8.
#BauCommand
BAU_ADDMEMBER_USAGE = §8/§7bau addmember §8[§eSpieler§8]
BAU_ADDMEMBER_USAGE = §8/§7bau addmember §8[§eWelt/all§8] §8[§eSpieler§8]
BAU_ADDMEMBER_SELFADD = §cDu brauchst dich nicht selbst hinzufügen!
BAU_ADDMEMBER_ISADDED = §cDieser Spieler ist bereits Mitglied auf deiner Welt.
BAU_ADDMEMBER_ADDED = §aDer Spieler wurde zu deiner Welt hinzugefügt.
BAU_ADDMEMBER_ADDED_TARGET = §aDu wurdest zu der Welt von §e{0} §ahinzugefügt.
BAU_TP_USAGE = §8/§7bau tp §8[§eSpieler§8]
BAU_TP_USAGE = §8/§7bau tp §8[§eSpieler§8] §8<§eWelt§8>
BAU_TP_NOALLOWED = §cDu darfst dich nicht auf diese Welt teleportieren.
BAU_LOCKED_NOALLOWED = §cDer Bauserver ist momentan gesperrt.
BAU_LOCK_BLOCKED = §cDeine Bausperre hat den Beitritt von §e{0} §cverhindert.
BAU_LOCKED_OPTIONS = §7Bauserver-Sperroptionen§8: §cnobody§8, §eserverteam§8, §eteam_and_serverteam§8, §eteam§8, §aopen
BAU_LOCKED_OPTIONS = §8/§7bau lock §8[§eWelt§8] §8[§eOption§8] §7Optionen§8: §cnobody§8, §eserverteam§8, §eteam_and_serverteam§8, §eteam§8, §aopen
BAU_LOCKED_NOBODY = §7Du hast deinen Bau für alle Spieler geschlossen.
BAU_LOCKED_SERVERTEAM = §7Du hast deinen Bau für alle außer hinzugefügte Serverteammitglieder gesperrt.
BAU_LOCKED_TEAM_AND_SERVERTEAM = §7Du hast deinen Bau für alle außer hinzugefügte Teammitglieder und Serverteammitglieder gesperrt.
BAU_LOCKED_TEAM = §7Du hast deinen Bau für alle außer hinzugefügte Teammitglieder gesperrt.
BAU_LOCKED_OPEN = §7Du hast deinen Bau für alle hinzugefügten Spieler geöffnet.
BAU_DELMEMBER_USAGE = §8/§7bau delmember §8[§eSpieler§8]
BAU_DELMEMBER_USAGE = §8/§7bau delmember §8[§eWelt/all§8] §8[§eSpieler§8]
BAU_DELMEMBER_SELFDEL = §cDu kannst dich nicht selbst entfernen!
BAU_DELMEMBER_DELETED = §cDer Spieler wurde entfernt.
BAU_DELMEMBER_DELETED_TARGET = §cDu wurdest von der Welt von §e{0} §centfernt.
@@ -226,9 +226,23 @@ BAU_DELETE_DELETED = §aDeine Welt wird zurückgesetzt.
BAU_DELETE_GUI_NAME = §eWirklich Welt löschen?
BAU_DELETE_GUI_CANCEL = §cAbbrechen
BAU_DELETE_GUI_DELETE = §aLöschen
BAU_WORLD_GUI_TITLE = §eDeine Welten
BAU_WORLD_CREATE_USAGE = §8/§7bau world create §8[§eName§8] §8[§eVersion§8] §8[§eTemplate§8]
BAU_WORLD_START_USAGE = §8/§7bau world start §8[§eWelt§8]
BAU_WORLD_RENAME_USAGE = §8/§7bau world rename §8[§eWelt§8] §8[§eNeuer Name§8]
BAU_WORLD_DELETE_USAGE = §8/§7bau world delete §8[§eWelt§8]
BAU_WORLD_UPGRADE_USAGE = §8/§7bau world upgrade §8[§eWelt§8] §8[§eVersion§8]
BAU_WORLD_VERSION = §cUnbekannte Version.
BAU_WORLD_UNKNOWN = §cUnbekannte Welt.
BAU_WORLD_EXISTS = §cEine Welt mit diesem Namen existiert bereits in dieser Version.
BAU_WORLD_LIMIT = §cDu hast dein Bauwelt-Limit von §e{0}§c erreicht. Lösche zuerst eine Welt.
BAU_WORLD_OVER_LIMIT_DELETE = §cDu hast aktuell mehr als die erlaubten §e{0}§c Bauwelten. Lösche eine Welt, bevor du Welten erstellst, umbenennst oder upgradest.
BAU_WORLD_CREATED = §aWelt erstellt.
BAU_WORLD_RENAMED = §aWelt umbenannt.
BAU_WORLD_UPGRADED = §aWelt-Version aktualisiert.
BAU_START_ALREADY = §cDer Server startet bereits.
BAU_MEMBER_NOMEMBER = §cDer Spieler ist kein Mitglied deiner Welt!
BAU_MEMBER_SET_USAGE = §8/§7bau {0} §8[§eSpieler§8]
BAU_MEMBER_SET_USAGE = §8/§7bau {0} §8[§eWelt/all§8] §8[§eSpieler§8]
BAU_MEMBER_SET_TARGET = §7Du bist nun ein §e{1}§7 auf der Welt von §e{0}§7.
BAU_MEMBER_SET = §7Der Spieler ist nun §e{0}§7.
BAU_MEMBER_SET_SPECTATOR = Zuschauer
@@ -491,6 +505,20 @@ TEAM_EVENT_LEFT = §7Dein Team nimmt nicht mehr am Event teil
TEAM_EVENT_JOINED = §7Dein Team nimmt nun am Event §e{0} §7 teil!
TEAM_EVENT_HOW_TO_LEAVE = §7Um die Teilnahme abzusagen, wiederhole den Befehl.
#Team Worlds
TEAM_WORLD_GUI_TITLE = §eTeamwelten
TEAM_WORLD_CREATE_USAGE = §8/§7team world create §8[§eName§8] §8[§eVersion§8] §8[§eTemplate§8]
TEAM_WORLD_RENAME_USAGE = §8/§7team world rename §8[§eWelt§8] §8[§eNeuer Name§8]
TEAM_WORLD_UPGRADE_USAGE = §8/§7team world upgrade §8[§eWelt§8] §8[§eVersion§8]
TEAM_WORLD_DELETE_USAGE = §8/§7team world delete §8[§eWelt§8]
TEAM_WORLD_EXISTS = §cEine Teamwelt mit diesem Namen existiert bereits in dieser Version.
TEAM_WORLD_LIMIT = §cDein Team hat bereits das Maximum von §e{0}§c Welten. Lösche zuerst eine Welt.
TEAM_WORLD_OVER_LIMIT_DELETE = §cDein Team hat aktuell mehr als die erlaubten §e{0}§c Welten. Lösche eine Welt, bevor du Welten erstellst, umbenennst oder upgradest.
TEAM_WORLD_CREATED = §aTeamwelt erstellt.
TEAM_WORLD_RENAMED = §aTeamwelt umbenannt.
TEAM_WORLD_UPGRADED = §aTeamwelt-Version aktualisiert.
TEAM_WORLD_DELETED = §aTeamwelt gelöscht.
#Team Color
TEAM_COLOR_TITLE = Farbe wählen
@@ -644,7 +672,7 @@ LOCK_LOCALE_CHANGED = §aSprache gespeichert
#Builder Cloud
BUILDERCLOUD_USAGE = §8/§7buildercloud §8[§eVersion§8] §8[§eWelt§8]
BUILDERCLOUD_CREATE_USAGE = §8/§7buildercloud create §8[§eVersion§8] §8[§eWelt§8] §8<§7Generator§8>
BUILDERCLOUD_CREATE_USAGE = §8/§7buildercloud create §8[§eVersion§8] §8[§eWelt§8] §8[§eTemplate§8]
BUILDERCLOUD_RENAME_USAGE = §8/§7buildercloud rename §8[§eVersion§8] §8[§eWElt§8] §8[§eNeuer Name§8]
BUILDERCLOUD_VERSION = §cUnbekannte Version.
BUILDERCLOUD_EXISTING_MAP = §cWelt existiert bereits.
@@ -51,11 +51,9 @@ public class ServerStarter {
private static final String EVENT_PATH = TMP_DATA + "event/";
public static final String TEMP_WORLD_PATH = TMP_DATA + "arenaserver/";
private static final String WORLDS_FOLDER = "/worlds";
public static final String WORLDS_BASE_PATH = WORLDS_FOLDER + "/userworlds";
public static final String BUILDER_BASE_PATH = WORLDS_FOLDER + "/builder";
private static final String WORLDS_STORAGE_BASE_PATH = "/mnt/storage/worlds/userworlds";
public static final String WORLDS_BASE_PATH = SteamwarWorld.WORLD_STORAGE + "/";
public static final String LEGACY_WORLDS_BASE_PATH = "/worlds/userworlds";
public static final String LEGACY_BUILDER_BASE_PATH = "/worlds/builder";
private File directory = null;
private String worldDir = null;
@@ -91,7 +89,8 @@ public class ServerStarter {
gameMode = mode.configFile.getName().replace(".yml", "");
directory = new File(SERVER_PATH, mode.Server.Folder);
arguments.put("config", mode.configFile.getName());
tempWorld(SERVER_PATH + mode.Server.Folder + "/arenas/" + map);
SteamwarWorld arenaWorld = SteamwarWorld.getOrCreateArenaWorld(gameMode, map, version.getVersionSuffix());
tempWorld(arenaWorld.setupAndGetStoragePath());
startCondition = () -> {
if (playersToSend.stream().anyMatch(player -> Subserver.isArena(Subserver.getSubserver(player)))) {
playersToSend.forEach(player -> Chatter.of(player).system("FIGHT_IN_ARENA"));
@@ -153,30 +152,36 @@ public class ServerStarter {
}
public ServerStarter build(ServerVersion version, UUID owner) {
this.version = version;
SteamwarUser user = SteamwarUser.get(owner);
return build(version, SteamwarWorld.getOrCreateBauWorld(user, version.getVersionSuffix()));
}
public ServerStarter build(ServerVersion version, SteamwarWorld world) {
this.version = version;
directory = version.getServerDirectory("Bau");
worldDir = version.getWorldFolder(WORLDS_BASE_PATH);
worldName = String.valueOf(SteamwarUser.get(owner).getId());
SteamwarUser user = world.getOwner() == null ? null : SteamwarUser.byId(world.getOwner().getValue());
worldDir = WORLDS_BASE_PATH;
worldName = world.getUuid().toString();
checkpoint = true;
build(owner);
if (world.getType() == WorldType.TEAM) {
build(world.getUuid());
Team team = Team.byId(world.getTeam().getValue());
serverNameProvider = port -> team.getTeamKuerzel() + "s Bau";
arguments.put("bauTeam", String.valueOf(world.getTeam().getValue()));
} else {
build(user.getUUID(), world.getUuid());
arguments.put("bauOwner", String.valueOf(user.getId()));
}
arguments.put("bauWorld", world.getUuid().toString());
arguments.put("bauType", world.getType().name());
worldSetup = () -> {
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());
}
}
};
worldSetup = () -> world.setupAndGetStoragePath(new File(directory, "Bauwelt"));
// Send players to existing server
startCondition = () -> {
Bauserver subserver = Bauserver.get(owner);
Bauserver subserver = Bauserver.getByWorld(world.getUuid());
if (subserver != null) {
for (Player p : playersToSend) {
SubserverSystem.sendPlayer(subserver, p);
@@ -184,9 +189,13 @@ public class ServerStarter {
return false;
}
boolean atLeastOneSupervisor = playersToSend.stream().anyMatch(player -> {
if (player.getUniqueId().equals(owner)) return true;
BauweltMember bauweltMember = BauweltMember.getBauMember(owner, player.getUniqueId());
return bauweltMember.isSupervisor();
SteamwarUser playerUser = SteamwarUser.get(player.getUniqueId());
if (world.getType() == WorldType.TEAM) {
return playerUser.getTeam() == world.getTeam().getValue();
}
if (user != null && playerUser.getId() == user.getId()) return true;
BauweltMember bauweltMember = BauweltMember.getBauMember(world.getUuid(), playerUser.getId());
return bauweltMember != null && bauweltMember.isSupervisor();
});
if (!atLeastOneSupervisor) {
for (Player p : playersToSend) {
@@ -220,15 +229,20 @@ public class ServerStarter {
}
private void build(UUID owner) {
constructor = (serverName, port, builder, shutdownCallback, failureCallback) -> new Bauserver(serverName, owner, port, builder, shutdownCallback, failureCallback);
build(owner, owner);
}
private void build(UUID owner, UUID world) {
constructor = (serverName, port, builder, shutdownCallback, failureCallback) -> new Bauserver(serverName, owner, world, port, builder, shutdownCallback, failureCallback);
serverNameProvider = port -> bauServerName(SteamwarUser.get(owner));
}
public ServerStarter builder(ServerVersion version, String map, File generator) {
public ServerStarter builder(ServerVersion version, String map, File prototype) {
this.version = version;
directory = version.getServerDirectory("Builder");
worldDir = version.getWorldFolder(BUILDER_BASE_PATH);
worldName = map;
SteamwarWorld world = SteamwarWorld.getOrCreateBuilderWorld(map, version.getVersionSuffix());
worldDir = WORLDS_BASE_PATH;
worldName = world.getUuid().toString();
serverNameProvider = port -> "*" + map;
checkpoint = true;
constructor = (serverName, port, builder, shutdownCallback, failureCallback) -> new Builderserver(serverName, worldName, port, builder, shutdownCallback, failureCallback);
@@ -245,16 +259,7 @@ public class ServerStarter {
return true;
};
if (generator != null) {
worldSetup = () -> {
File leveldat = new File(new File(worldDir, worldName), "level.dat");
try {
Files.copy(generator.toPath(), leveldat.toPath());
} catch (IOException e) {
throw new SecurityException(e);
}
};
}
worldSetup = () -> world.setupAndGetStoragePath(prototype != null ? prototype : legacyBuilderWorld(version, map));
return this;
}
@@ -358,6 +363,22 @@ public class ServerStarter {
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 {
Subserver construct(String serverName, int port, ProcessBuilder builder, Runnable shutdownCallback, Consumer<Exception> failureCallback);
}
@@ -396,4 +417,4 @@ public class ServerStarter {
}
}
}
}
@@ -35,16 +35,19 @@ import de.steamwar.persistent.Bauserver;
import de.steamwar.sql.BauweltMember;
import de.steamwar.sql.GameModeConfig;
import de.steamwar.sql.SteamwarUser;
import de.steamwar.velocitycore.ServerStarter;
import de.steamwar.velocitycore.ServerVersion;
import de.steamwar.velocitycore.SubserverSystem;
import de.steamwar.velocitycore.VelocityCore;
import de.steamwar.sql.SteamwarWorld;
import de.steamwar.sql.UserPerm;
import de.steamwar.velocitycore.*;
import de.steamwar.velocitycore.inventory.SWInventory;
import de.steamwar.velocitycore.inventory.SWItem;
import de.steamwar.velocitycore.inventory.SWListInv;
import de.steamwar.velocitycore.network.NetworkSender;
import de.steamwar.velocitycore.util.BauLock;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.function.Consumer;
@Linked
@@ -64,13 +67,13 @@ public class BauCommand extends SWCommand {
}
@Register
public void toBau(PlayerChatter sender, @OptionalValue(value = "", onlyUINIG = true) ServerVersion version) {
new ServerStarter().build(version, sender.user().getUUID()).send(sender.getPlayer()).start();
public void toBau(PlayerChatter sender, @Mapper("ownBauWorld") @OptionalValue(value = "", onlyUINIG = true) @AllowNull SteamwarWorld world) {
startPersonalWorld(sender, world);
}
@Register(value = "addmember", description = "BAU_ADDMEMBER_USAGE")
public void addmember(Chatter sender, @Validator("addMemberTarget") SteamwarUser target) {
BauweltMember.addMember(sender.user().getUUID(), target.getUUID());
public void addmember(Chatter sender, @Mapper("bauWorldSelector") String world, @Validator("addMemberTarget") SteamwarUser target) {
if (!forEachSelectedWorld(sender, world, bauworld -> BauweltMember.addMember(bauworld.getUuid(), target.getId()))) return;
sender.system("BAU_ADDMEMBER_ADDED");
Chatter.of(target.getUUID()).system("BAU_ADDMEMBER_ADDED_TARGET", sender);
}
@@ -86,18 +89,31 @@ public class BauCommand extends SWCommand {
messageSender.send("BAU_ADDMEMBER_SELFADD");
return false;
}
if (BauweltMember.getBauMember(sender.user().getId(), value.getId()) != null) {
messageSender.send("BAU_ADDMEMBER_ISADDED");
return false;
}
return true;
};
}
@Register(value = "tp", description = "BAU_TP_USAGE")
@Register("teleport")
public void teleport(PlayerChatter sender, @Validator("teleportTarget") SteamwarUser worldOwner, @OptionalValue(value = "", onlyUINIG = true) ServerVersion version) {
new ServerStarter().build(version, worldOwner.getUUID()).send(sender.getPlayer()).start();
public void teleport(PlayerChatter sender, @Validator("teleportTarget") SteamwarUser worldOwner, @Mapper("targetBauWorld") @OptionalValue(value = "", onlyUINIG = true) @AllowNull SteamwarWorld world) {
if (world == null) {
world = defaultPersonalWorld(worldOwner);
}
if (world == null) {
sender.system("BAU_WORLD_UNKNOWN");
return;
}
if (sender.user().getId() != worldOwner.getId() && BauLock.isLocked(world, sender.user())) {
sender.system("BAU_LOCKED_NOALLOWED");
Chatter.of(worldOwner.getUUID()).system("BAU_LOCK_BLOCKED", sender);
return;
}
if (sender.user().getId() != worldOwner.getId() && BauweltMember.getBauMember(world.getUuid(), sender.user().getId()) == null) {
SubserverSystem.sendDeniedMessage(sender, worldOwner.getUUID());
sender.system("BAU_TP_NOALLOWED");
return;
}
new ServerStarter().build(ServerVersion.get(world.getVersion()), world).send(sender.getPlayer()).start();
}
@Validator(value = "teleportTarget", local = true)
@@ -107,17 +123,6 @@ public class BauCommand extends SWCommand {
messageSender.send("UNKNOWN_PLAYER");
return false;
}
if (sender.user().getId() != owner.getId() && BauweltMember.getBauMember(owner.getId(), sender.user().getId()) == null) {
SubserverSystem.sendDeniedMessage(sender, owner.getUUID());
messageSender.send("BAU_TP_NOALLOWED");
return false;
}
if (BauLock.isLocked(owner, sender.user())) {
messageSender.send("BAU_LOCKED_NOALLOWED");
Chatter.of(owner.getUUID()).system("BAU_LOCK_BLOCKED", sender);
return false;
}
return true;
};
}
@@ -128,39 +133,39 @@ public class BauCommand extends SWCommand {
}
@Register("setspectator")
public void setSpectator(Chatter sender, @Mapper("addedUsers") @AllowNull @OptionalValue("") SteamwarUser user) {
setPerms(sender, user, "setspectator", "BAU_MEMBER_SET_SPECTATOR", member -> {
public void setSpectator(Chatter sender, @Mapper("bauWorldSelector") String world, @Mapper("addedUsers") @AllowNull @OptionalValue("") SteamwarUser user) {
setPerms(sender, world, user, "setspectator", "BAU_MEMBER_SET_SPECTATOR", member -> {
member.setBuild(false);
member.setSupervisor(false);
});
}
@Register("setbuilder")
public void setBuilder(Chatter sender, @Mapper("addedUsers") @AllowNull @OptionalValue("") SteamwarUser user) {
setPerms(sender, user, "setbuild", "BAU_MEMBER_SET_BUILDER", member -> {
public void setBuilder(Chatter sender, @Mapper("bauWorldSelector") String world, @Mapper("addedUsers") @AllowNull @OptionalValue("") SteamwarUser user) {
setPerms(sender, world, user, "setbuild", "BAU_MEMBER_SET_BUILDER", member -> {
member.setBuild(true);
member.setSupervisor(false);
});
}
@Register("setsupervisor")
public void setSupervisor(Chatter sender, @Mapper("addedUsers") @AllowNull @OptionalValue("") SteamwarUser user) {
setPerms(sender, user, "setsupervisor", "BAU_MEMBER_SET_SUPERVISOR", member -> {
public void setSupervisor(Chatter sender, @Mapper("bauWorldSelector") String world, @Mapper("addedUsers") @AllowNull @OptionalValue("") SteamwarUser user) {
setPerms(sender, world, user, "setsupervisor", "BAU_MEMBER_SET_SUPERVISOR", member -> {
member.setBuild(true);
member.setSupervisor(true);
});
}
private void setPerms(Chatter owner, SteamwarUser user, String name, String permName, Consumer<BauweltMember> setter) {
private void setPerms(Chatter owner, String world, SteamwarUser user, String name, String permName, Consumer<BauweltMember> setter) {
if (user == null) {
owner.system("BAU_MEMBER_SET_USAGE", name);
return;
}
withMember(owner, user, target -> {
withMembers(owner, world, user, target -> {
setter.accept(target);
Bauserver bauserver = Bauserver.get(owner.user().getUUID());
Bauserver bauserver = Bauserver.getByWorld(target.getWorldID());
if (bauserver != null) {
bauserver.getRegisteredServer().getPlayersConnected().stream().findAny().ifPresent(player -> NetworkSender.send(player, new BaumemberUpdatePacket()));
}
@@ -171,11 +176,11 @@ public class BauCommand extends SWCommand {
}
@Register(value = "delmember", description = "BAU_DELMEMBER_USAGE")
public void delmember(Chatter owner, @Mapper("addedUsers") SteamwarUser user) {
withMember(owner, user, target -> {
public void delmember(Chatter owner, @Mapper("bauWorldSelector") String world, @Mapper("addedUsers") SteamwarUser user) {
withMembers(owner, world, user, target -> {
target.remove();
Bauserver bauserver = Bauserver.get(owner.user().getUUID());
Bauserver bauserver = Bauserver.getByWorld(target.getWorldID());
Chatter member = Chatter.of(user.getUUID());
member.withPlayer(player -> {
if (bauserver != null && bauserver.getRegisteredServer().getPlayersConnected().contains(player)) {
@@ -192,6 +197,29 @@ public class BauCommand extends SWCommand {
});
}
@Mapper(value = "bauWorldSelector", local = true)
public TypeMapper<String> bauWorldSelector() {
return new TypeMapper<>() {
@Override
public String map(Chatter sender, PreviousArguments previousArguments, String s) {
if ("all".equalsIgnoreCase(s)) return "all";
return SteamwarWorld.getBauWorlds(sender.user()).stream()
.map(SteamwarWorld::getName)
.filter(name -> name.equalsIgnoreCase(s))
.findFirst()
.orElse(null);
}
@Override
public Collection<String> tabCompletes(Chatter sender, PreviousArguments previousArguments, String s) {
List<String> worlds = new ArrayList<>();
worlds.add("all");
SteamwarWorld.getBauWorlds(sender.user()).stream().map(SteamwarWorld::getName).forEach(worlds::add);
return worlds;
}
};
}
@Mapper(value = "addedUsers", local = true)
public TypeMapper<SteamwarUser> addedUsers() {
return new TypeMapper<SteamwarUser>() {
@@ -202,8 +230,10 @@ public class BauCommand extends SWCommand {
@Override
public Collection<String> tabCompletes(Chatter sender, PreviousArguments previousArguments, String s) {
return BauweltMember.getMembers(sender.user().getId()).stream()
return selectedWorlds(sender, previousArguments.userArgs.length == 0 ? "all" : previousArguments.userArgs[previousArguments.userArgs.length - 1]).stream()
.flatMap(world -> BauweltMember.getWorldMembers(world.getUuid()).stream())
.map(bauweltMember -> SteamwarUser.byId(bauweltMember.getMemberID()).getUserName())
.distinct()
.toList();
}
};
@@ -213,9 +243,11 @@ public class BauCommand extends SWCommand {
public void stop(PlayerChatter sender) {
VelocityCore.schedule(() -> {
sender.system("BAU_STOPPING");
Bauserver subserver = Bauserver.get(sender.user().getUUID());
if (subserver != null) {
subserver.stop();
for (SteamwarWorld world : SteamwarWorld.getBauWorlds(sender.user())) {
Bauserver subserver = Bauserver.getByWorld(world.getUuid());
if (subserver != null) {
subserver.stop();
}
}
sender.system("BAU_STOPPED");
@@ -224,25 +256,50 @@ public class BauCommand extends SWCommand {
@Register("resetall")
@Register("delete")
public void delete(PlayerChatter sender, ServerVersion version) {
SWInventory inventory = new SWInventory(sender, 9, new Message("BAU_DELETE_GUI_NAME"));
inventory.addItem(0, new SWItem("LIME_DYE", new Message("BAU_DELETE_GUI_DELETE")), click -> {
String world = version.getWorldFolder(ServerStarter.WORLDS_BASE_PATH) + sender.user().getId();
public void delete(PlayerChatter sender, @Mapper("ownBauWorld") SteamwarWorld world) {
openDeleteWorld(sender, world);
}
VelocityCore.schedule(() -> {
Bauserver subserver = Bauserver.get(sender.user().getUUID());
if (subserver != null) {
subserver.stop();
}
@Register("worlds")
@Register("world")
public void worlds(PlayerChatter sender) {
openWorldList(sender);
}
SubserverSystem.deleteFolder(VelocityCore.local, world);
sender.system("BAU_DELETE_DELETED");
}).schedule();
@Register(value = "world create", description = "BAU_WORLD_CREATE_USAGE")
public void createWorld(PlayerChatter sender, String name, @ErrorMessage("BAU_WORLD_VERSION") ServerVersion version, @Mapper("templateWorld") SteamwarWorld template) {
SteamwarWorld world = createPersonalWorld(sender, name, version, template);
if (world != null) {
sender.system("BAU_WORLD_CREATED");
}
}
inventory.close();
});
inventory.addItem(8, new SWItem("RED_DYE", new Message("BAU_DELETE_GUI_CANCEL")), click -> inventory.close());
inventory.open();
@Register(value = "world start", description = "BAU_WORLD_START_USAGE")
public void startWorld(PlayerChatter sender, @Mapper("ownBauWorld") SteamwarWorld world) {
new ServerStarter().build(ServerVersion.get(world.getVersion()), world).send(sender.getPlayer()).start();
}
@Register(value = "world rename", description = "BAU_WORLD_RENAME_USAGE")
public void renameWorld(Chatter sender, @Mapper("ownBauWorld") SteamwarWorld world, String newName) {
if (hasTooManyPersonalWorlds(sender)) return;
if (SteamwarWorld.getBauWorld(sender.user(), newName, world.getVersion()) != null) {
sender.system("BAU_WORLD_EXISTS");
return;
}
world.rename(newName);
sender.system("BAU_WORLD_RENAMED");
}
@Register(value = "world delete", description = "BAU_WORLD_DELETE_USAGE")
public void deleteWorld(PlayerChatter sender, @Mapper("ownBauWorld") SteamwarWorld world) {
openDeleteWorld(sender, world);
}
@Register(value = "world upgrade", description = "BAU_WORLD_UPGRADE_USAGE")
public void upgradeWorld(Chatter sender, @Mapper("ownBauWorld") SteamwarWorld world, @ErrorMessage("BAU_WORLD_VERSION") ServerVersion version) {
if (hasTooManyPersonalWorlds(sender)) return;
world.changeVersion(version.getVersionSuffix());
sender.system("BAU_WORLD_UPGRADED");
}
@Register("test")
@@ -254,27 +311,244 @@ public class BauCommand extends SWCommand {
}
@Register(value = "lock", description = "BAU_LOCKED_OPTIONS")
public void lock(Chatter sender, BauLockState bauLockState) {
BauLock.setLocked(sender, bauLockState);
public void lock(Chatter sender, @Mapper("ownBauWorld") SteamwarWorld world, BauLockState bauLockState) {
BauLock.setLocked(sender, world, bauLockState);
}
@Register("unlock")
public void unlock(Chatter sender) {
BauLock.setLocked(sender, BauLockState.OPEN);
public void unlock(Chatter sender, @Mapper("ownBauWorld") SteamwarWorld world) {
BauLock.setLocked(sender, world, BauLockState.OPEN);
}
private static void withMember(Chatter owner, SteamwarUser member, Consumer<BauweltMember> function) {
private static void withMembers(Chatter owner, String worldSelector, SteamwarUser member, Consumer<BauweltMember> function) {
if (member == null) {
owner.system("UNKNOWN_PLAYER");
return;
}
BauweltMember target = BauweltMember.getBauMember(owner.user().getId(), member.getId());
if (target == null) {
owner.system("BAU_MEMBER_NOMEMBER");
List<SteamwarWorld> worlds = selectedWorlds(owner, worldSelector);
if (worlds.isEmpty()) {
owner.system("BAU_WORLD_UNKNOWN");
return;
}
function.accept(target);
boolean found = false;
for (SteamwarWorld world : worlds) {
BauweltMember target = BauweltMember.getBauMember(world.getUuid(), member.getId());
if (target == null) continue;
found = true;
function.accept(target);
}
if (!found) {
owner.system("BAU_MEMBER_NOMEMBER");
}
}
@Mapper(value = "ownBauWorld", local = true)
private TypeMapper<SteamwarWorld> ownBauWorldMapper() {
return new TypeMapper<SteamwarWorld>() {
@Override
public SteamwarWorld map(Chatter sender, PreviousArguments previousArguments, String s) {
if (s == null || s.isEmpty()) return defaultPersonalWorld(sender);
return SteamwarWorld.getBauWorlds(sender.user()).stream()
.filter(world -> world.getName().equalsIgnoreCase(s) || world.getUuid().toString().equalsIgnoreCase(s))
.findFirst()
.orElse(null);
}
@Override
public Collection<String> tabCompletes(Chatter sender, PreviousArguments previousArguments, String s) {
return SteamwarWorld.getBauWorlds(sender.user()).stream().map(SteamwarWorld::getName).toList();
}
};
}
@Mapper(value = "targetBauWorld", local = true)
private TypeMapper<SteamwarWorld> targetBauWorldMapper() {
return new TypeMapper<>() {
@Override
public SteamwarWorld map(Chatter sender, PreviousArguments previousArguments, String s) {
SteamwarUser owner = previousArguments.getAll(SteamwarUser.class).stream().findFirst().orElse(null);
if (owner == null) return null;
if (s == null || s.isEmpty()) return defaultPersonalWorld(owner);
return SteamwarWorld.getBauWorlds(owner).stream()
.filter(world -> world.getName().equalsIgnoreCase(s) || world.getUuid().toString().equalsIgnoreCase(s))
.findFirst()
.orElse(null);
}
@Override
public Collection<String> tabCompletes(Chatter sender, PreviousArguments previousArguments, String s) {
SteamwarUser owner = previousArguments.getAll(SteamwarUser.class).stream().findFirst().orElse(null);
if (owner == null) return List.of();
return SteamwarWorld.getBauWorlds(owner).stream().map(SteamwarWorld::getName).toList();
}
};
}
@Mapper(value = "templateWorld", local = true)
private TypeMapper<SteamwarWorld> templateWorldMapper() {
return new TypeMapper<>() {
@Override
public SteamwarWorld map(Chatter sender, PreviousArguments previousArguments, String s) {
ServerVersion version = previousArguments.getAll(ServerVersion.class).stream().findFirst().orElse(null);
if (version == null) return null;
return SteamwarWorld.getTemplateWorld(s, version.getVersionSuffix());
}
@Override
public Collection<String> tabCompletes(Chatter sender, PreviousArguments previousArguments, String s) {
ServerVersion version = previousArguments.getAll(ServerVersion.class).stream().findFirst().orElse(null);
if (version == null) return List.of();
return SteamwarWorld.getTemplateWorlds(version.getVersionSuffix()).stream().map(SteamwarWorld::getName).toList();
}
};
}
private void openWorldList(PlayerChatter sender) {
List<SWListInv.SWListEntry<SteamwarWorld>> entries = new ArrayList<>();
for (SteamwarWorld world : SteamwarWorld.getBauWorlds(sender.user())) {
SWItem item = new SWItem("GRASS_BLOCK", new Message("PLAIN_STRING", "§e" + world.getName()))
.addLore(new Message("PLAIN_STRING", "§7Version: §e" + world.getVersion()))
.addLore(new Message("PLAIN_STRING", "§7Left: start, right: manage"));
entries.add(new SWListInv.SWListEntry<>(item, world));
}
SWListInv<SteamwarWorld> inventory = new SWListInv<>(sender, new Message("BAU_WORLD_GUI_TITLE"), entries, (click, world) -> {
if (click.isRightClick()) {
openWorldManagement(sender, world);
} else {
new ServerStarter().build(ServerVersion.get(world.getVersion()), world).send(sender.getPlayer()).start();
}
});
inventory.open();
}
private void openWorldManagement(PlayerChatter sender, SteamwarWorld world) {
SWInventory inventory = new SWInventory(sender, 9, new Message("PLAIN_STRING", "§e" + world.getName()));
inventory.addItem(0, new SWItem("ENDER_PEARL", new Message("PLAIN_STRING", "§aStart")), click -> {
inventory.close();
new ServerStarter().build(ServerVersion.get(world.getVersion()), world).send(sender.getPlayer()).start();
});
inventory.addItem(3, new SWItem("EXPERIENCE_BOTTLE", new Message("PLAIN_STRING", "§eUpgrade to latest")), click -> {
if (hasTooManyPersonalWorlds(sender)) {
inventory.close();
return;
}
world.changeVersion(ServerVersion.PAPER_21.getVersionSuffix());
sender.system("BAU_WORLD_UPGRADED");
inventory.close();
});
inventory.addItem(8, new SWItem("BARRIER", new Message("PLAIN_STRING", "§cDelete")), click -> openDeleteWorld(sender, world));
inventory.open();
}
private void openDeleteWorld(PlayerChatter sender, SteamwarWorld world) {
SWInventory inventory = new SWInventory(sender, 9, new Message("BAU_DELETE_GUI_NAME"));
inventory.addItem(0, new SWItem("LIME_DYE", new Message("BAU_DELETE_GUI_DELETE")), click -> {
VelocityCore.schedule(() -> {
Bauserver subserver = Bauserver.getByWorld(world.getUuid());
if (subserver != null) {
subserver.stop();
}
world.markDeleted();
SubserverSystem.deleteFolder(VelocityCore.local, world.getStorageDirectory().getPath());
sender.system("BAU_DELETE_DELETED");
}).schedule();
inventory.close();
});
inventory.addItem(8, new SWItem("RED_DYE", new Message("BAU_DELETE_GUI_CANCEL")), click -> inventory.close());
inventory.open();
}
private void startPersonalWorld(PlayerChatter sender, SteamwarWorld world) {
if (world == null) {
world = defaultPersonalWorld(sender);
}
if (world == null) {
sender.system("BAU_WORLD_UNKNOWN");
return;
}
new ServerStarter().build(ServerVersion.get(world.getVersion()), world).send(sender.getPlayer()).start();
}
private static SteamwarWorld defaultPersonalWorld(Chatter sender) {
return defaultPersonalWorld(sender.user());
}
private static SteamwarWorld defaultPersonalWorld(SteamwarUser user) {
return SteamwarWorld.getBauWorlds(user).stream().findFirst().orElse(null);
}
private static List<SteamwarWorld> selectedWorlds(Chatter sender, String selector) {
List<SteamwarWorld> worlds = SteamwarWorld.getBauWorlds(sender.user());
if ("all".equalsIgnoreCase(selector)) {
return worlds;
}
String normalized = selector.toLowerCase(Locale.ROOT);
return worlds.stream()
.filter(world -> world.getName().toLowerCase(Locale.ROOT).equals(normalized))
.toList();
}
private static boolean forEachSelectedWorld(Chatter sender, String selector, Consumer<SteamwarWorld> consumer) {
List<SteamwarWorld> worlds = selectedWorlds(sender, selector);
if (worlds.isEmpty()) {
sender.system("BAU_WORLD_UNKNOWN");
return false;
}
worlds.forEach(consumer);
return true;
}
private SteamwarWorld createPersonalWorld(Chatter sender, String name, ServerVersion version, SteamwarWorld template) {
if (template == null || template.getVersion() != version.getVersionSuffix()) {
sender.system("BAU_WORLD_UNKNOWN");
return null;
}
if (sender.user().hasPerm(UserPerm.BUILD_UNLIMITED_WORLDS)) {
if (SteamwarWorld.getBauWorld(sender.user(), name, version.getVersionSuffix()) != null) {
sender.system("BAU_WORLD_EXISTS");
return null;
}
return SteamwarWorld.getOrCreateBauWorld(sender.user(), name, version.getVersionSuffix(), template.getStorageDirectory());
}
long limit = personalWorldLimit(sender);
long count = SteamwarWorld.countBauWorlds(sender.user());
if (count > limit) {
sender.system("BAU_WORLD_OVER_LIMIT_DELETE", limit);
return null;
}
if (count >= limit) {
sender.system("BAU_WORLD_LIMIT", limit);
return null;
}
if (SteamwarWorld.getBauWorld(sender.user(), name, version.getVersionSuffix()) != null) {
sender.system("BAU_WORLD_EXISTS");
return null;
}
return SteamwarWorld.getOrCreateBauWorld(sender.user(), name, version.getVersionSuffix(), template.getStorageDirectory());
}
private boolean hasTooManyPersonalWorlds(Chatter sender) {
if (sender.user().hasPerm(UserPerm.BUILD_UNLIMITED_WORLDS)) {
return false;
}
long limit = personalWorldLimit(sender);
if (SteamwarWorld.countBauWorlds(sender.user()) <= limit) {
return false;
}
sender.system("BAU_WORLD_OVER_LIMIT_DELETE", limit);
return true;
}
private long personalWorldLimit(Chatter sender) {
return sender.user().hasPerm(UserPerm.BUILD_EXTRA_WORLDS) ? SteamwarWorld.EXTRA_BAU_WORLD_LIMIT : SteamwarWorld.DEFAULT_BAU_WORLD_LIMIT;
}
}
@@ -26,6 +26,7 @@ import de.steamwar.linkage.Linked;
import de.steamwar.messages.Chatter;
import de.steamwar.messages.PlayerChatter;
import de.steamwar.sql.GameModeConfig;
import de.steamwar.sql.SteamwarWorld;
import de.steamwar.sql.UserPerm;
import de.steamwar.velocitycore.ArenaMode;
import de.steamwar.velocitycore.ServerStarter;
@@ -33,11 +34,11 @@ import de.steamwar.velocitycore.ServerVersion;
import de.steamwar.velocitycore.VelocityCore;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
@Linked
public class BuilderCloudCommand extends SWCommand {
@@ -47,14 +48,23 @@ public class BuilderCloudCommand extends SWCommand {
}
@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) {
mapFile(version, map).mkdir();
sender.withPlayer(p -> new ServerStarter().builder(version, map, generator).send(p).start());
public void create(Chatter sender, @ErrorMessage("BUILDERCLOUD_VERSION") ServerVersion version, @Mapper("map") String map, @Mapper("templateWorld") SteamwarWorld template) {
if(mapExists(version, map)) {
sender.system("BUILDERCLOUD_EXISTING_MAP");
return;
}
if (template == null || template.getVersion() != version.getVersionSuffix()) {
sender.system("BAU_WORLD_UNKNOWN");
return;
}
SteamwarWorld.getOrCreateBuilderWorld(map, version.getVersionSuffix(), template.getStorageDirectory());
sender.withPlayer(p -> new ServerStarter().builder(version, map, template.getStorageDirectory()).send(p).start());
}
@Register(description = "BUILDERCLOUD_USAGE")
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");
return;
}
@@ -64,36 +74,34 @@ public class BuilderCloudCommand extends SWCommand {
@Register(value = "rename", description = "BUILDERCLOUD_RENAME_USAGE")
public void rename(Chatter sender, @ErrorMessage("BUILDERCLOUD_VERSION") ServerVersion version, @Mapper("map") String oldName, String newName) {
File oldMap = mapFile(version, oldName);
if (!oldMap.exists()) {
SteamwarWorld oldMap = builderWorld(version, oldName);
if (oldMap == null) {
sender.system("BUILDERCLOUD_UNKNOWN_MAP");
return;
}
File newMap = mapFile(version, newName);
if (newMap.exists()) {
if (mapExists(version, newName)) {
sender.system("BUILDERCLOUD_EXISTING_MAP");
return;
}
try {
Files.move(oldMap.toPath(), newMap.toPath());
} catch (IOException e) {
throw new SecurityException(e);
}
oldMap.rename(newName);
sender.system("BUILDERCLOUD_RENAMED");
}
@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) {
if (!mapFile(version, map).exists()) {
SteamwarWorld builderWorld = builderWorld(version, map);
if (builderWorld == null) {
sender.system("BUILDERCLOUD_UNKNOWN_MAP");
return;
}
VelocityCore.schedule(() -> {
VelocityCore.local.execute("deployarena.py", arenaMode.configFile.getName(), Integer.toString(version.getVersionSuffix()), map);
String modeName = arenaMode.configFile.getName().replace(".yml", "");
SteamwarWorld arenaWorld = SteamwarWorld.getOrCreateArenaWorld(modeName, map, version.getVersionSuffix());
VelocityCore.local.execute("deployarena.py", arenaMode.configFile.getName(), Integer.toString(version.getVersionSuffix()), map, builderWorld.setupAndGetStoragePath(), arenaWorld.setupAndGetStoragePath());
ArenaMode.init();
sender.system("BUILDERCLOUD_DEPLOY_FINISHED");
}).schedule();
@@ -111,60 +119,78 @@ public class BuilderCloudCommand extends SWCommand {
@Override
public Collection<String> tabCompletes(Chatter sender, PreviousArguments previousArguments, String s) {
File folder = getWorldFolder(previousArguments, 1);
String[] files;
if (folder == null || (files = folder.list()) == null) {
ServerVersion version = getVersion(previousArguments, 1);
if (version == null) {
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();
}
};
}
@Cached(global = true)
@Mapper(value = "generator", local = true)
private TypeMapper<File> generatorTypeMapper() {
@Mapper(value = "templateWorld", local = true)
private TypeMapper<SteamwarWorld> templateWorldMapper() {
return new TypeMapper<File>() {
return new TypeMapper<>() {
@Override
public File map(Chatter sender, PreviousArguments previousArguments, String s) {
if (s.isEmpty()) return null;
File folder = getWorldFolder(previousArguments, 2);
if (folder == null) throw new SecurityException();
File generator = new File(folder, s + ".dat");
if (!generator.exists() || !generator.isFile()) {
throw new SecurityException();
}
return generator;
public SteamwarWorld map(Chatter sender, PreviousArguments previousArguments, String s) {
ServerVersion version = previousArguments.getAll(ServerVersion.class).stream().findFirst().orElse(null);
if (version == null) return null;
return SteamwarWorld.getTemplateWorld(s, version.getVersionSuffix());
}
@Override
public Collection<String> tabCompletes(Chatter sender, PreviousArguments previousArguments, String s) {
File folder = getWorldFolder(previousArguments, 2);
String[] files;
if (folder == null || (files = folder.list()) == null) {
ServerVersion version = previousArguments.getAll(ServerVersion.class).stream().findFirst().orElse(null);
if (version == null) {
return Collections.emptyList();
}
return Arrays.stream(files).filter(file -> new File(folder, file).isFile()).filter(file -> file.endsWith(".dat")).map(file -> file.substring(0, file.length() - 4)).toList();
return SteamwarWorld.getTemplateWorlds(version.getVersionSuffix()).stream()
.map(SteamwarWorld::getName)
.toList();
}
};
}
private File mapFile(ServerVersion version, String map) {
return new File(version.getWorldFolder(ServerStarter.BUILDER_BASE_PATH), map);
private SteamwarWorld builderWorld(ServerVersion version, String 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) {
ServerVersion v = ServerVersion.get(previousArguments.userArgs[previousArguments.userArgs.length - offset]);
if (v == null) return null;
return new File(v.getWorldFolder(ServerStarter.BUILDER_BASE_PATH));
private boolean mapExists(ServerVersion version, String map) {
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 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.PlayerChatter;
import de.steamwar.sql.SteamwarUser;
import de.steamwar.sql.SteamwarWorld;
import de.steamwar.sql.UserPerm;
import de.steamwar.sql.internal.Statement;
import de.steamwar.velocitycore.ServerStarter;
import de.steamwar.velocitycore.ServerVersion;
import de.steamwar.velocitycore.VelocityCore;
import java.io.*;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@@ -64,12 +69,21 @@ public class GDPRQuery extends SWCommand {
copy(getClass().getClassLoader().getResourceAsStream("GDPRQueryREADME.md"), out, "README.txt");
sender.system("GDPR_STATUS_WORLD");
copyBauwelt(user, out, "/home/minecraft/userworlds/" + user.getUUID().toString(), "BuildWorld12");
copyBauwelt(user, out, "/home/minecraft/userworlds15/" + user.getId(), "BuildWorld15");
Set<Integer> exportedVersions = new LinkedHashSet<>();
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");
copyPlayerdata(user, out, "/home/minecraft/userworlds", "BuildInventories12");
copyPlayerdata(user, out, "/home/minecraft/userworlds15", "BuildInventories15");
copyPlayerdata(user, out, SteamwarWorld.WORLD_STORAGE, "BuildInventories");
sender.system("GDPR_STATUS_DATABASE");
sqlCSV(user, out, bannedIPs, "BannedIPs.csv");
@@ -99,8 +113,8 @@ public class GDPRQuery extends SWCommand {
}
private static final Statement bannedIPs = new Statement("SELECT Timestamp, IP FROM BannedUserIPs WHERE UserID = ?");
private static final Statement bauweltMember = new Statement("SELECT BauweltID AS Bauwelt, WorldEdit, World FROM BauweltMember WHERE MemberID = ?");
private static final Statement bauweltMembers = new Statement("SELECT u.UserName AS 'User', m.WorldEdit AS WorldEdit, m.World AS World FROM BauweltMember m INNER JOIN UserData u ON m.MemberID = u.id WHERE m.BauweltID = ?");
private static final Statement bauweltMember = new Statement("SELECT WorldID AS Bauwelt, WorldEdit, World FROM BauweltMember WHERE MemberID = ?");
private static final Statement bauweltMembers = new Statement("SELECT w.Name AS World, u.UserName AS 'User', m.WorldEdit AS WorldEdit, m.World AS World FROM BauweltMember m INNER JOIN UserData u ON m.MemberID = u.id INNER JOIN world w ON m.WorldID = w.id WHERE w.Owner = ?");
private static final Statement checkedSchems = new Statement("SELECT NodeName AS Schematic, StartTime, EndTime, DeclineReason AS Result FROM CheckedSchematic WHERE NodeOwner = ? ORDER BY StartTime ASC");
private static final Statement fights = new Statement("SELECT p.Team AS Team, p.Kit AS Kit, p.Kills AS Kills, p.IsOut AS Died, f.GameMode AS GameMode, f.Server AS Server, f.StartTime AS StartTime, f.Duration AS Duration, (f.BlueLeader = p.UserID) AS IsBlueLeader, (f.RedLeader = p.UserID) AS IsRedLeader, f.Win AS Winner, f.WinCondition AS WinCondition FROM Fight f INNER JOIN FightPlayer p ON f.FightID = p.FightID WHERE p.UserID = ? ORDER BY StartTime ASC");
private static final Statement ignoredPlayers = new Statement("SELECT u.UserName AS IgnoredPlayer FROM IgnoredPlayers i INNER JOIN UserData u ON i.Ignored = u.id WHERE Ignorer = ?");
@@ -230,6 +244,18 @@ public class GDPRQuery extends SWCommand {
}
}
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 {
File worlds = new File(inDir);
String path = "playerdata/" + user.getUUID().toString() + ".dat";
@@ -30,6 +30,7 @@ import de.steamwar.messages.PlayerChatter;
import de.steamwar.sql.*;
import de.steamwar.velocitycore.VelocityCore;
import de.steamwar.velocitycore.discord.DiscordBot;
import de.steamwar.velocitycore.inventory.SWInventory;
import de.steamwar.velocitycore.inventory.SWItem;
import de.steamwar.velocitycore.inventory.SWListInv;
import de.steamwar.velocitycore.util.SteamwarPrefix;
@@ -437,6 +438,117 @@ public class TeamCommand extends SWCommand {
}
}
@Register("worlds")
@Register("world")
public void worlds(@Validator("isInTeam") PlayerChatter sender) {
List<SWListInv.SWListEntry<SteamwarWorld>> entries = SteamwarWorld.getTeamWorlds(Team.byId(sender.user().getTeam())).stream()
.map(world -> new SWListInv.SWListEntry<>(
new SWItem("GRASS_BLOCK", new Message("PLAIN_STRING", "§e" + world.getName()))
.addLore(new Message("PLAIN_STRING", "§7Version: §e" + world.getVersion()))
.addLore(new Message("PLAIN_STRING", "§7Left: start, right: manage")),
world
))
.toList();
SWListInv<SteamwarWorld> inv = new SWListInv<>(sender, new Message("TEAM_WORLD_GUI_TITLE"), entries, (click, world) -> {
if (click.isRightClick() && sender.user().isLeader()) {
openTeamWorldManagement(sender, world);
} else {
startTeamWorld(sender, world);
}
});
inv.open();
}
@Register("world")
public void world(@Validator("isInTeam") PlayerChatter sender, @Mapper("teamWorld") SteamwarWorld world) {
startTeamWorld(sender, world);
}
@Register(value = "world create", description = "TEAM_WORLD_CREATE_USAGE")
public void createWorld(@Validator("isLeader") Chatter sender, String name, @ErrorMessage("BAU_WORLD_VERSION") ServerVersion version, @Mapper("templateWorld") SteamwarWorld template) {
Team team = Team.byId(sender.user().getTeam());
if (template == null || template.getVersion() != version.getVersionSuffix()) {
sender.system("BAU_WORLD_UNKNOWN");
return;
}
if (SteamwarWorld.getTeamWorld(team, name, version.getVersionSuffix()) != null) {
sender.system("TEAM_WORLD_EXISTS");
return;
}
long count = SteamwarWorld.countTeamWorlds(team);
if (count > SteamwarWorld.TEAM_WORLD_LIMIT) {
sender.system("TEAM_WORLD_OVER_LIMIT_DELETE", SteamwarWorld.TEAM_WORLD_LIMIT);
return;
}
if (count >= SteamwarWorld.TEAM_WORLD_LIMIT) {
sender.system("TEAM_WORLD_LIMIT", SteamwarWorld.TEAM_WORLD_LIMIT);
return;
}
SteamwarWorld.getOrCreateTeamWorld(team, name, version.getVersionSuffix(), template.getStorageDirectory());
sender.system("TEAM_WORLD_CREATED");
}
@Register(value = "world rename", description = "TEAM_WORLD_RENAME_USAGE")
public void renameWorld(@Validator("isLeader") Chatter sender, @Mapper("teamWorld") SteamwarWorld world, String newName) {
Team team = Team.byId(sender.user().getTeam());
if (hasTooManyTeamWorlds(sender, team)) return;
if (SteamwarWorld.getTeamWorld(team, newName, world.getVersion()) != null) {
sender.system("TEAM_WORLD_EXISTS");
return;
}
world.rename(newName);
sender.system("TEAM_WORLD_RENAMED");
}
@Register(value = "world upgrade", description = "TEAM_WORLD_UPGRADE_USAGE")
public void upgradeWorld(@Validator("isLeader") Chatter sender, @Mapper("teamWorld") SteamwarWorld world, @ErrorMessage("BAU_WORLD_VERSION") ServerVersion version) {
if (hasTooManyTeamWorlds(sender, Team.byId(sender.user().getTeam()))) return;
world.changeVersion(version.getVersionSuffix());
sender.system("TEAM_WORLD_UPGRADED");
}
@Register(value = "world delete", description = "TEAM_WORLD_DELETE_USAGE")
public void deleteWorld(@Validator("isLeader") PlayerChatter sender, @Mapper("teamWorld") SteamwarWorld world) {
SWInventory inventory = new SWInventory(sender, 9, new Message("BAU_DELETE_GUI_NAME"));
inventory.addItem(0, new SWItem("LIME_DYE", new Message("BAU_DELETE_GUI_DELETE")), click -> {
VelocityCore.schedule(() -> {
Bauserver subserver = Bauserver.getByWorld(world.getUuid());
if (subserver != null) {
subserver.stop();
}
world.markDeleted();
SubserverSystem.deleteFolder(VelocityCore.local, world.getStorageDirectory().getPath());
sender.system("TEAM_WORLD_DELETED");
}).schedule();
inventory.close();
});
inventory.addItem(8, new SWItem("RED_DYE", new Message("BAU_DELETE_GUI_CANCEL")), click -> inventory.close());
inventory.open();
}
private void startTeamWorld(PlayerChatter sender, SteamwarWorld world) {
new ServerStarter().build(ServerVersion.get(world.getVersion()), world).send(sender.getPlayer()).start();
}
private void openTeamWorldManagement(PlayerChatter sender, SteamwarWorld world) {
SWInventory inventory = new SWInventory(sender, 9, new Message("PLAIN_STRING", "§e" + world.getName()));
inventory.addItem(0, new SWItem("ENDER_PEARL", new Message("PLAIN_STRING", "§aStart")), click -> {
inventory.close();
startTeamWorld(sender, world);
});
inventory.addItem(3, new SWItem("EXPERIENCE_BOTTLE", new Message("PLAIN_STRING", "§eUpgrade to latest")), click -> {
if (hasTooManyTeamWorlds(sender, Team.byId(sender.user().getTeam()))) {
inventory.close();
return;
}
world.changeVersion(ServerVersion.PAPER_21.getVersionSuffix());
sender.system("TEAM_WORLD_UPGRADED");
inventory.close();
});
inventory.addItem(8, new SWItem("BARRIER", new Message("PLAIN_STRING", "§cDelete")), click -> deleteWorld(sender, world));
inventory.open();
}
@Register("event")
public void event(@Validator("isLeader") Chatter sender, Event event) {
Team team = Team.byId(sender.user().getTeam());
@@ -585,6 +697,56 @@ public class TeamCommand extends SWCommand {
};
}
@Mapper(value = "teamWorld", local = true)
public TypeMapper<SteamwarWorld> teamWorldMapper() {
return new TypeMapper<>() {
@Override
public SteamwarWorld map(Chatter sender, PreviousArguments previousArguments, String s) {
Team team = Team.byId(sender.user().getTeam());
return SteamwarWorld.getTeamWorlds(team).stream()
.filter(world -> world.getName().equalsIgnoreCase(s) || world.getUuid().toString().equalsIgnoreCase(s))
.findFirst()
.orElse(null);
}
@Override
public Collection<String> tabCompletes(Chatter sender, PreviousArguments previousArguments, String s) {
if (sender.user().getTeam() == 0) return List.of();
return SteamwarWorld.getTeamWorlds(Team.byId(sender.user().getTeam())).stream()
.map(SteamwarWorld::getName)
.toList();
}
};
}
@Mapper(value = "templateWorld", local = true)
public TypeMapper<SteamwarWorld> templateWorldMapper() {
return new TypeMapper<>() {
@Override
public SteamwarWorld map(Chatter sender, PreviousArguments previousArguments, String s) {
ServerVersion version = previousArguments.getAll(ServerVersion.class).stream().findFirst().orElse(null);
if (version == null) return null;
return SteamwarWorld.getTemplateWorld(s, version.getVersionSuffix());
}
@Override
public Collection<String> tabCompletes(Chatter sender, PreviousArguments previousArguments, String s) {
ServerVersion version = previousArguments.getAll(ServerVersion.class).stream().findFirst().orElse(null);
if (version == null) return List.of();
return SteamwarWorld.getTemplateWorlds(version.getVersionSuffix()).stream().map(SteamwarWorld::getName).toList();
}
};
}
private boolean hasTooManyTeamWorlds(Chatter sender, Team team) {
if (SteamwarWorld.countTeamWorlds(team) <= SteamwarWorld.TEAM_WORLD_LIMIT) {
return false;
}
sender.system("TEAM_WORLD_OVER_LIMIT_DELETE", SteamwarWorld.TEAM_WORLD_LIMIT);
return true;
}
private boolean checkTeamName(Chatter sender, Team team, String arg) {
Team t = Team.get(arg);
if (t != null && t.getTeamId() != team.getTeamId()) {
@@ -120,17 +120,26 @@ public class TpCommand extends SWCommand {
}
} else if (Subserver.isBuild(subserver)) {
Bauserver bauserver = (Bauserver) subserver;
SteamwarWorld world = SteamwarWorld.getWorld(bauserver.getWorld());
if (world != null && world.getType() == WorldType.TEAM) {
if (sender.user().getTeam() != world.getTeam().getValue()) {
sender.system("JOIN_PLAYER_BLOCK");
return;
}
SubserverSystem.sendPlayer(subserver, sender.getPlayer());
return;
}
Player checker = VelocityCore.getProxy().getPlayer(bauserver.getOwner()).orElse(null);
if (checker != null && CheckCommand.isChecking(checker)) {
if (!sender.user().hasPerm(UserPerm.CHECK) && CheckCommand.getCheckingSchem(checker).getOwner() != sender.user().getId()) {
sender.system("JOIN_PLAYER_BLOCK");
return;
}
} else if (BauLock.isLocked(SteamwarUser.get(bauserver.getOwner()), sender.user())) {
} else if (world != null && BauLock.isLocked(world, sender.user())) {
sender.system("BAU_LOCKED_NOALLOWED");
Chatter.of(bauserver.getOwner()).system("BAU_LOCK_BLOCKED", sender);
return;
} else if (!bauserver.getOwner().equals(sender.user().getUUID()) && BauweltMember.getBauMember(bauserver.getOwner(), sender.user().getUUID()) == null) {
} else if (!bauserver.getOwner().equals(sender.user().getUUID()) && BauweltMember.getBauMember(bauserver.getWorld(), sender.user().getUUID()) == null) {
SubserverSystem.sendDeniedMessage(sender, bauserver.getOwner());
sender.system("JOIN_PLAYER_BLOCK");
return;
@@ -25,37 +25,39 @@ import de.steamwar.network.packets.server.BaulockUpdatePacket;
import de.steamwar.persistent.Bauserver;
import de.steamwar.sql.BauweltMember;
import de.steamwar.sql.SteamwarUser;
import de.steamwar.sql.UserConfig;
import de.steamwar.sql.SteamwarWorld;
import de.steamwar.sql.UserPerm;
import de.steamwar.sql.WorldType;
import de.steamwar.velocitycore.network.NetworkSender;
import lombok.experimental.UtilityClass;
@UtilityClass
public class BauLock {
private static final String BAU_LOCK_CONFIG_NAME = "baulockstate";
public static void setLocked(Chatter owner, BauLockState state) {
UserConfig.updatePlayerConfig(owner.user().getId(), BAU_LOCK_CONFIG_NAME, state == BauLockState.OPEN ? null : state.name());
public static void setLocked(Chatter owner, SteamwarWorld world, BauLockState state) {
world.changeLockState(state == BauLockState.OPEN ? null : state.name());
owner.system("BAU_LOCKED_" + state.name());
Bauserver bauserver = Bauserver.get(owner.user().getUUID());
Bauserver bauserver = Bauserver.getByWorld(world.getUuid());
if (bauserver != null) {
bauserver.getRegisteredServer().getPlayersConnected().stream().findAny().ifPresent(player -> NetworkSender.send(player, new BaulockUpdatePacket()));
}
}
public static boolean isLocked(SteamwarUser owner, SteamwarUser target) {
if (owner.getId() == target.getId()) return false;
public static boolean isLocked(SteamwarWorld world, SteamwarUser target) {
if (world.getType() != WorldType.BAU || world.getOwner() == null) return false;
SteamwarUser owner = SteamwarUser.byId(world.getOwner().getValue());
if (owner == null || owner.getId() == target.getId()) return false;
boolean locked;
String state = UserConfig.getConfig(owner.getId(), BAU_LOCK_CONFIG_NAME);
String state = world.getLockState();
switch (state == null ? BauLockState.OPEN : BauLockState.valueOf(state)) {
case NOBODY:
locked = true;
break;
case SUPERVISOR:
BauweltMember member = BauweltMember.getBauMember(owner.getId(), target.getId());
BauweltMember member = BauweltMember.getBauMember(world.getUuid(), target.getId());
locked = member == null || !member.isSupervisor();
break;
case SERVERTEAM: