From 6ba7d7917501022e1cd1f0dc41bd0fca403b2336 Mon Sep 17 00:00:00 2001 From: Hare Date: Tue, 9 Dec 2025 18:50:24 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=E3=82=BB=E3=82=AF=E3=82=BF=E3=83=BC?= =?UTF-8?q?=E3=81=AE=E8=A8=AD=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitmodules | 3 + Faction | 2 +- Lands | 2 +- .../hcu/landsector/LandSectorPlugin.kt | 68 ++++++++++++++++ .../landsector/command/LandSectorCommand.kt | 38 +++++++++ .../hcu/landsector/database/SectorsTable.kt | 14 ++++ .../hcu/landsector/listener/SectorListener.kt | 80 +++++++++++++++++++ .../hareworks/hcu/landsector/model/Sector.kt | 10 +++ .../hcu/landsector/service/SectorService.kt | 54 +++++++++++++ bin/main/paper-plugin.yml | 21 +++++ build.gradle.kts | 6 ++ hcu-core | 2 +- .../hcu/landsector/LandSectorPlugin.kt | 48 ++++++++++- .../landsector/command/LandSectorCommand.kt | 38 +++++++++ .../hcu/landsector/database/SectorsTable.kt | 14 ++++ .../hcu/landsector/listener/SectorListener.kt | 80 +++++++++++++++++++ .../hareworks/hcu/landsector/model/Sector.kt | 10 +++ .../hcu/landsector/service/SectorService.kt | 54 +++++++++++++ 18 files changed, 540 insertions(+), 4 deletions(-) create mode 100644 bin/main/net/hareworks/hcu/landsector/LandSectorPlugin.kt create mode 100644 bin/main/net/hareworks/hcu/landsector/command/LandSectorCommand.kt create mode 100644 bin/main/net/hareworks/hcu/landsector/database/SectorsTable.kt create mode 100644 bin/main/net/hareworks/hcu/landsector/listener/SectorListener.kt create mode 100644 bin/main/net/hareworks/hcu/landsector/model/Sector.kt create mode 100644 bin/main/net/hareworks/hcu/landsector/service/SectorService.kt create mode 100644 bin/main/paper-plugin.yml create mode 100644 src/main/kotlin/net/hareworks/hcu/landsector/command/LandSectorCommand.kt create mode 100644 src/main/kotlin/net/hareworks/hcu/landsector/database/SectorsTable.kt create mode 100644 src/main/kotlin/net/hareworks/hcu/landsector/listener/SectorListener.kt create mode 100644 src/main/kotlin/net/hareworks/hcu/landsector/model/Sector.kt create mode 100644 src/main/kotlin/net/hareworks/hcu/landsector/service/SectorService.kt diff --git a/.gitmodules b/.gitmodules index 82db0bb..812c4f8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,12 @@ [submodule "hcu-core"] path = hcu-core url = git@gitea.hareworks.net:hcu/hcu-core.git + branch = master [submodule "Faction"] path = Faction url = git@gitea.hareworks.net:hcu/Faction.git + branch = master [submodule "Lands"] path = Lands url = git@gitea.hareworks.net:hcu/Lands.git + branch = master diff --git a/Faction b/Faction index ac72167..661c517 160000 --- a/Faction +++ b/Faction @@ -1 +1 @@ -Subproject commit ac7216788c98dcdd35392e4c9aeb8e04b0b82cb1 +Subproject commit 661c5173a2099c89c40fcdc031452e73170b8402 diff --git a/Lands b/Lands index 1c809b3..b4c17de 160000 --- a/Lands +++ b/Lands @@ -1 +1 @@ -Subproject commit 1c809b3def8cd9acacbdd2b666f99bf89c728e64 +Subproject commit b4c17de746ef73fb192299c84f9202115548d4dc diff --git a/bin/main/net/hareworks/hcu/landsector/LandSectorPlugin.kt b/bin/main/net/hareworks/hcu/landsector/LandSectorPlugin.kt new file mode 100644 index 0000000..f954370 --- /dev/null +++ b/bin/main/net/hareworks/hcu/landsector/LandSectorPlugin.kt @@ -0,0 +1,68 @@ +package net.hareworks.hcu.landsector + +import net.hareworks.hcu.core.Main +import net.hareworks.hcu.core.player.PlayerIdService +import net.hareworks.hcu.landsector.command.LandSectorCommand +import net.hareworks.hcu.landsector.listener.SectorListener +import net.hareworks.hcu.landsector.service.SectorService +import org.bukkit.plugin.java.JavaPlugin + +class LandSectorPlugin : JavaPlugin() { + + companion object { + lateinit var instance: LandSectorPlugin + private set + } + + var sectorService: SectorService? = null + + override fun onEnable() { + instance = this + + // Register commands + LandSectorCommand(this).register() + + // Defer service init + server.scheduler.runTaskLater(this, Runnable { + initializeServices() + }, 60L) + + logger.info("LandSector plugin has been enabled!") + } + + private fun initializeServices() { + val db = try { + Main.instance.exposedDatabase() + } catch (e: Exception) { + null + } + + if (db == null) { + logger.severe("hcu-core database not ready.") + return + } + + val service = SectorService(db) + try { + service.init() + } catch (e: Exception) { + logger.severe("Failed to init DB: ${e.message}") + return + } + this.sectorService = service + + val pIdService = server.servicesManager.load(PlayerIdService::class.java) + if (pIdService == null) { + logger.severe("PlayerIdService not found") + return + } + + server.pluginManager.registerEvents(SectorListener(this, service, pIdService), this) + + logger.info("LandSector initialized with services.") + } + + override fun onDisable() { + logger.info("LandSector plugin has been disabled!") + } +} diff --git a/bin/main/net/hareworks/hcu/landsector/command/LandSectorCommand.kt b/bin/main/net/hareworks/hcu/landsector/command/LandSectorCommand.kt new file mode 100644 index 0000000..53898b3 --- /dev/null +++ b/bin/main/net/hareworks/hcu/landsector/command/LandSectorCommand.kt @@ -0,0 +1,38 @@ +package net.hareworks.hcu.landsector.command + +import net.hareworks.hcu.landsector.LandSectorPlugin +import net.hareworks.kommand_lib.kommand +import net.kyori.adventure.text.Component +import net.kyori.adventure.text.format.NamedTextColor +import org.bukkit.Material +import org.bukkit.NamespacedKey +import org.bukkit.entity.Player +import org.bukkit.inventory.ItemStack +import org.bukkit.persistence.PersistentDataType + +class LandSectorCommand(private val plugin: LandSectorPlugin) { + fun register() { + kommand(plugin) { + command("landsector") { + literal("give") { + executes { + val player = sender as? Player ?: return@executes + + val item = ItemStack(Material.BEDROCK) + val meta = item.itemMeta + meta.displayName(Component.text("Sector Core", NamedTextColor.LIGHT_PURPLE)) + meta.persistentDataContainer.set( + NamespacedKey(plugin, "component"), + PersistentDataType.STRING, + "sector_core" + ) + item.itemMeta = meta + + player.inventory.addItem(item) + sender.sendMessage(Component.text("Gave 1 Sector Core.", NamedTextColor.GREEN)) + } + } + } + } + } +} diff --git a/bin/main/net/hareworks/hcu/landsector/database/SectorsTable.kt b/bin/main/net/hareworks/hcu/landsector/database/SectorsTable.kt new file mode 100644 index 0000000..c83179f --- /dev/null +++ b/bin/main/net/hareworks/hcu/landsector/database/SectorsTable.kt @@ -0,0 +1,14 @@ +package net.hareworks.hcu.landsector.database + +import org.jetbrains.exposed.v1.core.Table + +object SectorsTable : Table("land_sectors") { + val id = integer("id").autoIncrement() + val ownerActorId = integer("owner_actor_id") + val world = varchar("world", 64) + val x = integer("x") + val y = integer("y") + val z = integer("z") + + override val primaryKey = PrimaryKey(id) +} diff --git a/bin/main/net/hareworks/hcu/landsector/listener/SectorListener.kt b/bin/main/net/hareworks/hcu/landsector/listener/SectorListener.kt new file mode 100644 index 0000000..9da20a2 --- /dev/null +++ b/bin/main/net/hareworks/hcu/landsector/listener/SectorListener.kt @@ -0,0 +1,80 @@ +package net.hareworks.hcu.landsector.listener + +import net.hareworks.hcu.landsector.LandSectorPlugin +import net.hareworks.hcu.landsector.service.SectorService +import net.hareworks.hcu.core.player.PlayerIdService +import net.kyori.adventure.text.Component +import net.kyori.adventure.text.format.NamedTextColor +import org.bukkit.Material +import org.bukkit.NamespacedKey +import org.bukkit.entity.EntityType +import org.bukkit.entity.Shulker +import org.bukkit.attribute.Attribute +import org.bukkit.event.EventHandler +import org.bukkit.event.Listener +import org.bukkit.event.block.BlockPlaceEvent +import org.bukkit.persistence.PersistentDataType + +class SectorListener( + private val plugin: LandSectorPlugin, + private val sectorService: SectorService, + private val playerIdService: PlayerIdService +) : Listener { + + @EventHandler + fun onPlace(event: BlockPlaceEvent) { + val item = event.itemInHand + val meta = item.itemMeta ?: return + val key = NamespacedKey(plugin, "component") + + if (meta.persistentDataContainer.get(key, PersistentDataType.STRING) != "sector_core") { + return + } + + val player = event.player + val playerEntry = playerIdService.find(player.uniqueId) + if (playerEntry == null) { + player.sendMessage(Component.text("Identity not found.", NamedTextColor.RED)) + event.isCancelled = true + return + } + + val block = event.blockPlaced + val loc = block.location + + // Space check + val above1 = block.getRelative(0, 1, 0) + val above2 = block.getRelative(0, 2, 0) + + if (!above1.type.isAir || !above2.type.isAir) { + player.sendMessage(Component.text("Not enough space for Sector Core.", NamedTextColor.RED)) + event.isCancelled = true + return + } + + // Spawn Shulker + val shulkerLoc = loc.clone().add(0.5, 1.0, 0.5) + val shulker = player.world.spawnEntity(shulkerLoc, EntityType.SHULKER) as Shulker + shulker.setAI(false) + shulker.isInvulnerable = true + shulker.isInvisible = true + + val param = shulker.getAttribute(Attribute.MAX_HEALTH) + param?.baseValue = 1000.0 + shulker.health = 1000.0 + + // Place top bedrock + above2.type = Material.BEDROCK + + // Record to DB (using Shulker pos as center) + sectorService.createSector( + playerEntry.actorId, + player.world.name, + above1.x, + above1.y, + above1.z + ) + + player.sendMessage(Component.text("Sector Core placed!", NamedTextColor.GREEN)) + } +} diff --git a/bin/main/net/hareworks/hcu/landsector/model/Sector.kt b/bin/main/net/hareworks/hcu/landsector/model/Sector.kt new file mode 100644 index 0000000..9c6fb83 --- /dev/null +++ b/bin/main/net/hareworks/hcu/landsector/model/Sector.kt @@ -0,0 +1,10 @@ +package net.hareworks.hcu.landsector.model + +data class Sector( + val id: Int, + val ownerActorId: Int, + val world: String, + val x: Int, + val y: Int, + val z: Int +) diff --git a/bin/main/net/hareworks/hcu/landsector/service/SectorService.kt b/bin/main/net/hareworks/hcu/landsector/service/SectorService.kt new file mode 100644 index 0000000..3cdd888 --- /dev/null +++ b/bin/main/net/hareworks/hcu/landsector/service/SectorService.kt @@ -0,0 +1,54 @@ +package net.hareworks.hcu.landsector.service + +import net.hareworks.hcu.landsector.database.SectorsTable +import net.hareworks.hcu.landsector.model.Sector +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.jdbc.Database +import org.jetbrains.exposed.v1.jdbc.SchemaUtils +import org.jetbrains.exposed.v1.jdbc.insert +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.andWhere +import org.jetbrains.exposed.v1.jdbc.transactions.transaction + +class SectorService(private val database: Database) { + + fun init() { + transaction(database) { + SchemaUtils.createMissingTablesAndColumns(SectorsTable) + } + } + + fun createSector(ownerActorId: Int, world: String, x: Int, y: Int, z: Int): Sector? { + return transaction(database) { + val id = SectorsTable.insert { + it[SectorsTable.ownerActorId] = ownerActorId + it[SectorsTable.world] = world + it[SectorsTable.x] = x + it[SectorsTable.y] = y + it[SectorsTable.z] = z + }[SectorsTable.id] + + Sector(id, ownerActorId, world, x, y, z) + } + } + + fun getSectorAt(world: String, x: Int, y: Int, z: Int): Sector? { + return transaction(database) { + SectorsTable.selectAll() + .andWhere { SectorsTable.world eq world } + .andWhere { SectorsTable.x eq x } + .andWhere { SectorsTable.y eq y } + .andWhere { SectorsTable.z eq z } + .map { + Sector( + it[SectorsTable.id], + it[SectorsTable.ownerActorId], + it[SectorsTable.world], + it[SectorsTable.x], + it[SectorsTable.y], + it[SectorsTable.z] + ) + }.singleOrNull() + } + } +} diff --git a/bin/main/paper-plugin.yml b/bin/main/paper-plugin.yml new file mode 100644 index 0000000..d92f321 --- /dev/null +++ b/bin/main/paper-plugin.yml @@ -0,0 +1,21 @@ +api-version: 1.21.10 +name: landsector +version: 1.0-SNAPSHOT +main: net.hareworks.hcu.landsector.LandSectorPlugin +description: Land sector management plugin for HCU server +authors: + - Hare-K02 +dependencies: + server: + faction: + load: BEFORE + required: true + join-classpath: true + hcu-core: + load: BEFORE + required: true + join-classpath: true + lands: + load: BEFORE + required: true + join-classpath: true diff --git a/build.gradle.kts b/build.gradle.kts index 6603a84..43c068a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -49,6 +49,12 @@ tasks { shadowJar { archiveClassifier.set("min") minimize() + dependencies { + exclude(dependency("org.jetbrains.kotlin:kotlin-stdlib")) + exclude(dependency("org.jetbrains.kotlin:kotlin-reflect")) + exclude(dependency("org.jetbrains.kotlin:kotlin-stdlib-jdk8")) + exclude(dependency("org.jetbrains.kotlin:kotlin-stdlib-jdk7")) + } } } diff --git a/hcu-core b/hcu-core index 520a378..6931466 160000 --- a/hcu-core +++ b/hcu-core @@ -1 +1 @@ -Subproject commit 520a378cd5c3da6321305d07cc0b402b73292add +Subproject commit 6931466f979e8e48141afb4297cb27aac3e2da88 diff --git a/src/main/kotlin/net/hareworks/hcu/landsector/LandSectorPlugin.kt b/src/main/kotlin/net/hareworks/hcu/landsector/LandSectorPlugin.kt index 816030f..f954370 100644 --- a/src/main/kotlin/net/hareworks/hcu/landsector/LandSectorPlugin.kt +++ b/src/main/kotlin/net/hareworks/hcu/landsector/LandSectorPlugin.kt @@ -1,5 +1,10 @@ package net.hareworks.hcu.landsector +import net.hareworks.hcu.core.Main +import net.hareworks.hcu.core.player.PlayerIdService +import net.hareworks.hcu.landsector.command.LandSectorCommand +import net.hareworks.hcu.landsector.listener.SectorListener +import net.hareworks.hcu.landsector.service.SectorService import org.bukkit.plugin.java.JavaPlugin class LandSectorPlugin : JavaPlugin() { @@ -9,11 +14,52 @@ class LandSectorPlugin : JavaPlugin() { private set } + var sectorService: SectorService? = null + override fun onEnable() { instance = this + // Register commands + LandSectorCommand(this).register() + + // Defer service init + server.scheduler.runTaskLater(this, Runnable { + initializeServices() + }, 60L) + logger.info("LandSector plugin has been enabled!") - logger.info("Dependencies: Faction, Lands") + } + + private fun initializeServices() { + val db = try { + Main.instance.exposedDatabase() + } catch (e: Exception) { + null + } + + if (db == null) { + logger.severe("hcu-core database not ready.") + return + } + + val service = SectorService(db) + try { + service.init() + } catch (e: Exception) { + logger.severe("Failed to init DB: ${e.message}") + return + } + this.sectorService = service + + val pIdService = server.servicesManager.load(PlayerIdService::class.java) + if (pIdService == null) { + logger.severe("PlayerIdService not found") + return + } + + server.pluginManager.registerEvents(SectorListener(this, service, pIdService), this) + + logger.info("LandSector initialized with services.") } override fun onDisable() { diff --git a/src/main/kotlin/net/hareworks/hcu/landsector/command/LandSectorCommand.kt b/src/main/kotlin/net/hareworks/hcu/landsector/command/LandSectorCommand.kt new file mode 100644 index 0000000..53898b3 --- /dev/null +++ b/src/main/kotlin/net/hareworks/hcu/landsector/command/LandSectorCommand.kt @@ -0,0 +1,38 @@ +package net.hareworks.hcu.landsector.command + +import net.hareworks.hcu.landsector.LandSectorPlugin +import net.hareworks.kommand_lib.kommand +import net.kyori.adventure.text.Component +import net.kyori.adventure.text.format.NamedTextColor +import org.bukkit.Material +import org.bukkit.NamespacedKey +import org.bukkit.entity.Player +import org.bukkit.inventory.ItemStack +import org.bukkit.persistence.PersistentDataType + +class LandSectorCommand(private val plugin: LandSectorPlugin) { + fun register() { + kommand(plugin) { + command("landsector") { + literal("give") { + executes { + val player = sender as? Player ?: return@executes + + val item = ItemStack(Material.BEDROCK) + val meta = item.itemMeta + meta.displayName(Component.text("Sector Core", NamedTextColor.LIGHT_PURPLE)) + meta.persistentDataContainer.set( + NamespacedKey(plugin, "component"), + PersistentDataType.STRING, + "sector_core" + ) + item.itemMeta = meta + + player.inventory.addItem(item) + sender.sendMessage(Component.text("Gave 1 Sector Core.", NamedTextColor.GREEN)) + } + } + } + } + } +} diff --git a/src/main/kotlin/net/hareworks/hcu/landsector/database/SectorsTable.kt b/src/main/kotlin/net/hareworks/hcu/landsector/database/SectorsTable.kt new file mode 100644 index 0000000..c83179f --- /dev/null +++ b/src/main/kotlin/net/hareworks/hcu/landsector/database/SectorsTable.kt @@ -0,0 +1,14 @@ +package net.hareworks.hcu.landsector.database + +import org.jetbrains.exposed.v1.core.Table + +object SectorsTable : Table("land_sectors") { + val id = integer("id").autoIncrement() + val ownerActorId = integer("owner_actor_id") + val world = varchar("world", 64) + val x = integer("x") + val y = integer("y") + val z = integer("z") + + override val primaryKey = PrimaryKey(id) +} diff --git a/src/main/kotlin/net/hareworks/hcu/landsector/listener/SectorListener.kt b/src/main/kotlin/net/hareworks/hcu/landsector/listener/SectorListener.kt new file mode 100644 index 0000000..9da20a2 --- /dev/null +++ b/src/main/kotlin/net/hareworks/hcu/landsector/listener/SectorListener.kt @@ -0,0 +1,80 @@ +package net.hareworks.hcu.landsector.listener + +import net.hareworks.hcu.landsector.LandSectorPlugin +import net.hareworks.hcu.landsector.service.SectorService +import net.hareworks.hcu.core.player.PlayerIdService +import net.kyori.adventure.text.Component +import net.kyori.adventure.text.format.NamedTextColor +import org.bukkit.Material +import org.bukkit.NamespacedKey +import org.bukkit.entity.EntityType +import org.bukkit.entity.Shulker +import org.bukkit.attribute.Attribute +import org.bukkit.event.EventHandler +import org.bukkit.event.Listener +import org.bukkit.event.block.BlockPlaceEvent +import org.bukkit.persistence.PersistentDataType + +class SectorListener( + private val plugin: LandSectorPlugin, + private val sectorService: SectorService, + private val playerIdService: PlayerIdService +) : Listener { + + @EventHandler + fun onPlace(event: BlockPlaceEvent) { + val item = event.itemInHand + val meta = item.itemMeta ?: return + val key = NamespacedKey(plugin, "component") + + if (meta.persistentDataContainer.get(key, PersistentDataType.STRING) != "sector_core") { + return + } + + val player = event.player + val playerEntry = playerIdService.find(player.uniqueId) + if (playerEntry == null) { + player.sendMessage(Component.text("Identity not found.", NamedTextColor.RED)) + event.isCancelled = true + return + } + + val block = event.blockPlaced + val loc = block.location + + // Space check + val above1 = block.getRelative(0, 1, 0) + val above2 = block.getRelative(0, 2, 0) + + if (!above1.type.isAir || !above2.type.isAir) { + player.sendMessage(Component.text("Not enough space for Sector Core.", NamedTextColor.RED)) + event.isCancelled = true + return + } + + // Spawn Shulker + val shulkerLoc = loc.clone().add(0.5, 1.0, 0.5) + val shulker = player.world.spawnEntity(shulkerLoc, EntityType.SHULKER) as Shulker + shulker.setAI(false) + shulker.isInvulnerable = true + shulker.isInvisible = true + + val param = shulker.getAttribute(Attribute.MAX_HEALTH) + param?.baseValue = 1000.0 + shulker.health = 1000.0 + + // Place top bedrock + above2.type = Material.BEDROCK + + // Record to DB (using Shulker pos as center) + sectorService.createSector( + playerEntry.actorId, + player.world.name, + above1.x, + above1.y, + above1.z + ) + + player.sendMessage(Component.text("Sector Core placed!", NamedTextColor.GREEN)) + } +} diff --git a/src/main/kotlin/net/hareworks/hcu/landsector/model/Sector.kt b/src/main/kotlin/net/hareworks/hcu/landsector/model/Sector.kt new file mode 100644 index 0000000..9c6fb83 --- /dev/null +++ b/src/main/kotlin/net/hareworks/hcu/landsector/model/Sector.kt @@ -0,0 +1,10 @@ +package net.hareworks.hcu.landsector.model + +data class Sector( + val id: Int, + val ownerActorId: Int, + val world: String, + val x: Int, + val y: Int, + val z: Int +) diff --git a/src/main/kotlin/net/hareworks/hcu/landsector/service/SectorService.kt b/src/main/kotlin/net/hareworks/hcu/landsector/service/SectorService.kt new file mode 100644 index 0000000..3cdd888 --- /dev/null +++ b/src/main/kotlin/net/hareworks/hcu/landsector/service/SectorService.kt @@ -0,0 +1,54 @@ +package net.hareworks.hcu.landsector.service + +import net.hareworks.hcu.landsector.database.SectorsTable +import net.hareworks.hcu.landsector.model.Sector +import org.jetbrains.exposed.v1.core.eq +import org.jetbrains.exposed.v1.jdbc.Database +import org.jetbrains.exposed.v1.jdbc.SchemaUtils +import org.jetbrains.exposed.v1.jdbc.insert +import org.jetbrains.exposed.v1.jdbc.selectAll +import org.jetbrains.exposed.v1.jdbc.andWhere +import org.jetbrains.exposed.v1.jdbc.transactions.transaction + +class SectorService(private val database: Database) { + + fun init() { + transaction(database) { + SchemaUtils.createMissingTablesAndColumns(SectorsTable) + } + } + + fun createSector(ownerActorId: Int, world: String, x: Int, y: Int, z: Int): Sector? { + return transaction(database) { + val id = SectorsTable.insert { + it[SectorsTable.ownerActorId] = ownerActorId + it[SectorsTable.world] = world + it[SectorsTable.x] = x + it[SectorsTable.y] = y + it[SectorsTable.z] = z + }[SectorsTable.id] + + Sector(id, ownerActorId, world, x, y, z) + } + } + + fun getSectorAt(world: String, x: Int, y: Int, z: Int): Sector? { + return transaction(database) { + SectorsTable.selectAll() + .andWhere { SectorsTable.world eq world } + .andWhere { SectorsTable.x eq x } + .andWhere { SectorsTable.y eq y } + .andWhere { SectorsTable.z eq z } + .map { + Sector( + it[SectorsTable.id], + it[SectorsTable.ownerActorId], + it[SectorsTable.world], + it[SectorsTable.x], + it[SectorsTable.y], + it[SectorsTable.z] + ) + }.singleOrNull() + } + } +}