diff --git a/src/main/kotlin/com/coderjoe/atlas/fluid/FluidBlockData.kt b/src/main/kotlin/com/coderjoe/atlas/fluid/FluidBlockData.kt deleted file mode 100644 index d83e58d..0000000 --- a/src/main/kotlin/com/coderjoe/atlas/fluid/FluidBlockData.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.coderjoe.atlas.fluid - -import com.coderjoe.atlas.fluid.block.FluidContainer -import org.bukkit.Location -import org.bukkit.block.BlockFace -import org.bukkit.plugin.java.JavaPlugin - -data class FluidBlockData( - val blockId: String, - val world: String, - val x: Int, - val y: Int, - val z: Int, - val fluidType: String, - val facing: String? = null, - val storedAmount: Int? = null, -) { - companion object { - fun fromFluidBlock( - fluidBlock: FluidBlock, - blockId: String, - ): FluidBlockData { - val loc = fluidBlock.location - val facing = fluidBlock.facing.let { if (it == BlockFace.SELF) null else it.name } - val storedAmount = - when (fluidBlock) { - is FluidContainer -> fluidBlock.storedAmount - else -> null - } - return FluidBlockData( - blockId = blockId, - world = loc.world?.name ?: "world", - x = loc.blockX, - y = loc.blockY, - z = loc.blockZ, - fluidType = fluidBlock.storedFluid.name, - facing = facing, - storedAmount = storedAmount, - ) - } - } - - fun toLocation(plugin: JavaPlugin): Location? { - val world = plugin.server.getWorld(this.world) ?: return null - return Location(world, x.toDouble(), y.toDouble(), z.toDouble()) - } - - fun toBlockFace(): BlockFace { - return if (facing != null) { - try { - BlockFace.valueOf(facing) - } catch (_: Exception) { - BlockFace.SELF - } - } else { - BlockFace.SELF - } - } - - fun toFluidType(): FluidType { - return try { - FluidType.valueOf(fluidType) - } catch (_: Exception) { - FluidType.NONE - } - } -} diff --git a/src/main/kotlin/com/coderjoe/atlas/fluid/FluidBlockRegistry.kt b/src/main/kotlin/com/coderjoe/atlas/fluid/FluidBlockRegistry.kt index 24026d4..1354a47 100644 --- a/src/main/kotlin/com/coderjoe/atlas/fluid/FluidBlockRegistry.kt +++ b/src/main/kotlin/com/coderjoe/atlas/fluid/FluidBlockRegistry.kt @@ -9,8 +9,6 @@ class FluidBlockRegistry(plugin: JavaPlugin) : BlockRegistry(plugin) companion object { var instance: FluidBlockRegistry? = null private set - - fun locationKey(location: Location): String = BlockRegistry.locationKey(location) } init { diff --git a/src/main/kotlin/com/coderjoe/atlas/fluid/block/FluidSplitter.kt b/src/main/kotlin/com/coderjoe/atlas/fluid/block/FluidSplitter.kt index ce9134f..05e2fe2 100644 --- a/src/main/kotlin/com/coderjoe/atlas/fluid/block/FluidSplitter.kt +++ b/src/main/kotlin/com/coderjoe/atlas/fluid/block/FluidSplitter.kt @@ -26,6 +26,7 @@ class FluidSplitter(location: Location, override val facing: BlockFace) : FluidB } override val baseBlockId: String = BLOCK_ID + private var nextOutputIndex: Int = 0 override fun getVisualStateBlockId(): String = BLOCK_ID @@ -47,13 +48,16 @@ class FluidSplitter(location: Location, override val facing: BlockFace) : FluidB if (hasFluid()) { val outputFaces = ADJACENT_FACES.filter { it != facing.oppositeFace } + val faceCount = outputFaces.size - for (face in outputFaces) { + for (i in outputFaces.indices) { if (!hasFluid()) break + val face = outputFaces[(nextOutputIndex + i) % faceCount] val target = registry.getAdjacentFluidBlock(location, face) ?: continue if (!target.hasFluid()) { val fluid = removeFluid() if (target.storeFluid(fluid)) { + nextOutputIndex = (nextOutputIndex + i + 1) % faceCount plugin.logger.atlasInfo( "FluidSplitter at ${location.coordinates} " + "pushed ${fluid.name} to ${target::class.simpleName} at ${face.name}", diff --git a/src/main/kotlin/com/coderjoe/atlas/power/PowerBlockData.kt b/src/main/kotlin/com/coderjoe/atlas/power/PowerBlockData.kt deleted file mode 100644 index fd9c8eb..0000000 --- a/src/main/kotlin/com/coderjoe/atlas/power/PowerBlockData.kt +++ /dev/null @@ -1,54 +0,0 @@ -package com.coderjoe.atlas.power - -import com.coderjoe.atlas.utility.block.SmallDrill -import org.bukkit.Location -import org.bukkit.block.BlockFace - -data class PowerBlockData( - val blockId: String, - val world: String, - val x: Int, - val y: Int, - val z: Int, - val currentPower: Int, - val facing: String? = null, - val enabled: Boolean? = null, -) { - companion object { - fun fromPowerBlock( - powerBlock: PowerBlock, - blockId: String, - ): PowerBlockData { - val loc = powerBlock.location - val facing = powerBlock.facing.let { if (it == BlockFace.SELF) null else it.name } - val enabled = if (powerBlock is SmallDrill) powerBlock.enabled else null - return PowerBlockData( - blockId = blockId, - world = loc.world?.name ?: "world", - x = loc.blockX, - y = loc.blockY, - z = loc.blockZ, - currentPower = powerBlock.currentPower, - facing = facing, - enabled = enabled, - ) - } - } - - fun toLocation(plugin: org.bukkit.plugin.java.JavaPlugin): Location? { - val world = plugin.server.getWorld(this.world) ?: return null - return Location(world, x.toDouble(), y.toDouble(), z.toDouble()) - } - - fun toBlockFace(): BlockFace { - return if (facing != null) { - try { - BlockFace.valueOf(facing) - } catch (_: Exception) { - BlockFace.SELF - } - } else { - BlockFace.SELF - } - } -} diff --git a/src/main/kotlin/com/coderjoe/atlas/power/PowerBlockRegistry.kt b/src/main/kotlin/com/coderjoe/atlas/power/PowerBlockRegistry.kt index 683977b..9f52378 100644 --- a/src/main/kotlin/com/coderjoe/atlas/power/PowerBlockRegistry.kt +++ b/src/main/kotlin/com/coderjoe/atlas/power/PowerBlockRegistry.kt @@ -9,8 +9,6 @@ class PowerBlockRegistry(plugin: JavaPlugin) : BlockRegistry(plugin) companion object { var instance: PowerBlockRegistry? = null private set - - fun locationKey(location: Location): String = BlockRegistry.locationKey(location) } init { diff --git a/src/main/kotlin/com/coderjoe/atlas/power/block/PowerSplitter.kt b/src/main/kotlin/com/coderjoe/atlas/power/block/PowerSplitter.kt index 2063391..2169db9 100644 --- a/src/main/kotlin/com/coderjoe/atlas/power/block/PowerSplitter.kt +++ b/src/main/kotlin/com/coderjoe/atlas/power/block/PowerSplitter.kt @@ -26,6 +26,7 @@ class PowerSplitter(location: Location, override val facing: BlockFace) : PowerB override val baseBlockId: String = BLOCK_ID override val updateIntervalTicks: Long = 20L + private var nextOutputIndex: Int = 0 override fun getVisualStateBlockId(): String = BLOCK_ID @@ -48,14 +49,18 @@ class PowerSplitter(location: Location, override val facing: BlockFace) : PowerB if (hasPower()) { val outputFaces = ADJACENT_FACES.filter { it != facing.oppositeFace } + val faceCount = outputFaces.size + var lastPushOffset = -1 - for (face in outputFaces) { + for (i in outputFaces.indices) { if (!hasPower()) break + val face = outputFaces[(nextOutputIndex + i) % faceCount] val target = registry.getAdjacentPowerBlock(location, face) ?: continue if (target.canAcceptPower()) { val pushed = removePower(1) if (pushed > 0) { target.addPower(pushed) + lastPushOffset = i plugin.logger.atlasInfo( "PowerSplitter at ${location.coordinates} " + "pushed $pushed power to ${target::class.simpleName} at ${face.name}", @@ -63,6 +68,9 @@ class PowerSplitter(location: Location, override val facing: BlockFace) : PowerB } } } + if (lastPushOffset >= 0) { + nextOutputIndex = (nextOutputIndex + lastPushOffset + 1) % faceCount + } } updatePoweredState() diff --git a/src/main/kotlin/com/coderjoe/atlas/power/block/SmallSolarPanel.kt b/src/main/kotlin/com/coderjoe/atlas/power/block/SmallSolarPanel.kt index ddb3c5e..6b42806 100644 --- a/src/main/kotlin/com/coderjoe/atlas/power/block/SmallSolarPanel.kt +++ b/src/main/kotlin/com/coderjoe/atlas/power/block/SmallSolarPanel.kt @@ -46,11 +46,6 @@ class SmallSolarPanel(location: Location) : PowerBlock(location, maxStorage = 4) "generated $generated power (now $currentPower/$maxStorage)", ) } - } else { - plugin.logger.atlasInfo( - "SmallSolarPanel at ${location.coordinates} " + - "is not generating power because it is not daytime.", - ) } } } diff --git a/src/main/kotlin/com/coderjoe/atlas/transport/TransportBlockRegistry.kt b/src/main/kotlin/com/coderjoe/atlas/transport/TransportBlockRegistry.kt index 5c93db1..b826782 100644 --- a/src/main/kotlin/com/coderjoe/atlas/transport/TransportBlockRegistry.kt +++ b/src/main/kotlin/com/coderjoe/atlas/transport/TransportBlockRegistry.kt @@ -9,8 +9,6 @@ class TransportBlockRegistry(plugin: JavaPlugin) : BlockRegistry companion object { var instance: TransportBlockRegistry? = null private set - - fun locationKey(location: Location): String = BlockRegistry.locationKey(location) } init { diff --git a/src/test/kotlin/com/coderjoe/atlas/TestHelper.kt b/src/test/kotlin/com/coderjoe/atlas/TestHelper.kt index 69accb4..4a9ac08 100644 --- a/src/test/kotlin/com/coderjoe/atlas/TestHelper.kt +++ b/src/test/kotlin/com/coderjoe/atlas/TestHelper.kt @@ -129,7 +129,7 @@ object TestHelper { @Suppress("UNCHECKED_CAST") val blockIds = blockIdsField.get(registry) as java.util.concurrent.ConcurrentHashMap - val key = PowerBlockRegistry.locationKey(block.location) + val key = BlockRegistry.locationKey(block.location) blocks[key] = block blockIds[key] = blockId } @@ -149,7 +149,7 @@ object TestHelper { @Suppress("UNCHECKED_CAST") val blockIds = blockIdsField.get(registry) as java.util.concurrent.ConcurrentHashMap - val key = TransportBlockRegistry.locationKey(block.location) + val key = BlockRegistry.locationKey(block.location) blocks[key] = block blockIds[key] = blockId } @@ -169,7 +169,7 @@ object TestHelper { @Suppress("UNCHECKED_CAST") val blockIds = blockIdsField.get(registry) as java.util.concurrent.ConcurrentHashMap - val key = FluidBlockRegistry.locationKey(block.location) + val key = BlockRegistry.locationKey(block.location) blocks[key] = block blockIds[key] = blockId } diff --git a/src/test/kotlin/com/coderjoe/atlas/fluid/FluidBlockDataTest.kt b/src/test/kotlin/com/coderjoe/atlas/fluid/FluidBlockDataTest.kt deleted file mode 100644 index ccd088a..0000000 --- a/src/test/kotlin/com/coderjoe/atlas/fluid/FluidBlockDataTest.kt +++ /dev/null @@ -1,117 +0,0 @@ -package com.coderjoe.atlas.fluid - -import com.coderjoe.atlas.TestHelper -import com.coderjoe.atlas.fluid.block.FluidPipe -import com.coderjoe.atlas.fluid.block.FluidPump -import org.bukkit.block.BlockFace -import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertNotNull -import org.junit.jupiter.api.Assertions.assertNull -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test - -class FluidBlockDataTest { - @BeforeEach - fun setup() { - TestHelper.setup() - } - - @AfterEach - fun teardown() { - TestHelper.teardown() - } - - @Test - fun `fromFluidBlock for FluidPump`() { - val pump = FluidPump(TestHelper.createLocation(1.0, 2.0, 3.0)) - pump.storeFluid(FluidType.WATER) - val data = FluidBlockData.fromFluidBlock(pump, "atlas:fluid_pump") - - assertEquals("atlas:fluid_pump", data.blockId) - assertEquals("world", data.world) - assertEquals(1, data.x) - assertEquals(2, data.y) - assertEquals(3, data.z) - assertEquals("WATER", data.fluidType) - assertNull(data.facing) - } - - @Test - fun `fromFluidBlock for FluidPipe captures facing`() { - val pipe = FluidPipe(TestHelper.createLocation(), BlockFace.EAST) - val data = FluidBlockData.fromFluidBlock(pipe, "atlas:fluid_pipe") - assertEquals("EAST", data.facing) - } - - @Test - fun `toBlockFace with valid string`() { - val data = FluidBlockData("id", "world", 0, 0, 0, "NONE", facing = "WEST") - assertEquals(BlockFace.WEST, data.toBlockFace()) - } - - @Test - fun `toBlockFace with null returns SELF`() { - val data = FluidBlockData("id", "world", 0, 0, 0, "NONE", facing = null) - assertEquals(BlockFace.SELF, data.toBlockFace()) - } - - @Test - fun `toBlockFace with invalid returns SELF`() { - val data = FluidBlockData("id", "world", 0, 0, 0, "NONE", facing = "INVALID") - assertEquals(BlockFace.SELF, data.toBlockFace()) - } - - @Test - fun `toFluidType WATER`() { - val data = FluidBlockData("id", "world", 0, 0, 0, "WATER") - assertEquals(FluidType.WATER, data.toFluidType()) - } - - @Test - fun `toFluidType LAVA`() { - val data = FluidBlockData("id", "world", 0, 0, 0, "LAVA") - assertEquals(FluidType.LAVA, data.toFluidType()) - } - - @Test - fun `toFluidType NONE`() { - val data = FluidBlockData("id", "world", 0, 0, 0, "NONE") - assertEquals(FluidType.NONE, data.toFluidType()) - } - - @Test - fun `toFluidType invalid defaults to NONE`() { - val data = FluidBlockData("id", "world", 0, 0, 0, "INVALID") - assertEquals(FluidType.NONE, data.toFluidType()) - } - - @Test - fun `toLocation with valid world`() { - val data = FluidBlockData("id", "world", 5, 64, 10, "NONE") - val loc = data.toLocation(TestHelper.mockPlugin) - assertNotNull(loc) - assertEquals(5.0, loc!!.x) - assertEquals(64.0, loc.y) - assertEquals(10.0, loc.z) - } - - @Test - fun `toLocation with invalid world returns null`() { - val data = FluidBlockData("id", "nonexistent", 0, 0, 0, "NONE") - assertNull(data.toLocation(TestHelper.mockPlugin)) - } - - @Test - fun `round-trip FluidPipe preserves all fields`() { - val pipe = FluidPipe(TestHelper.createLocation(1.0, 2.0, 3.0), BlockFace.NORTH) - pipe.storeFluid(FluidType.LAVA) - val data = FluidBlockData.fromFluidBlock(pipe, "atlas:fluid_pipe") - - assertEquals("atlas:fluid_pipe", data.blockId) - assertEquals("NORTH", data.facing) - assertEquals("LAVA", data.fluidType) - assertEquals(BlockFace.NORTH, data.toBlockFace()) - assertEquals(FluidType.LAVA, data.toFluidType()) - } -} diff --git a/src/test/kotlin/com/coderjoe/atlas/fluid/FluidBlockListenerTest.kt b/src/test/kotlin/com/coderjoe/atlas/fluid/FluidBlockListenerTest.kt index a14c446..a28801c 100644 --- a/src/test/kotlin/com/coderjoe/atlas/fluid/FluidBlockListenerTest.kt +++ b/src/test/kotlin/com/coderjoe/atlas/fluid/FluidBlockListenerTest.kt @@ -2,6 +2,7 @@ package com.coderjoe.atlas.fluid import com.coderjoe.atlas.TestHelper import com.coderjoe.atlas.core.AtlasBlockListener +import com.coderjoe.atlas.core.BlockRegistry import com.coderjoe.atlas.core.BlockSystem import com.coderjoe.atlas.fluid.block.FluidPump import io.mockk.every @@ -46,7 +47,7 @@ class FluidBlockListenerTest { @Test fun `onBlockPlace skips when in updatingLocations`() { val loc = TestHelper.createLocation() - val key = FluidBlockRegistry.locationKey(loc) + val key = BlockRegistry.locationKey(loc) registry.updatingLocations.add(key) val block = mockk(relaxed = true) @@ -61,7 +62,7 @@ class FluidBlockListenerTest { @Test fun `onBlockBreak skips when in updatingLocations`() { val loc = TestHelper.createLocation() - val key = FluidBlockRegistry.locationKey(loc) + val key = BlockRegistry.locationKey(loc) registry.updatingLocations.add(key) val block = mockk(relaxed = true) diff --git a/src/test/kotlin/com/coderjoe/atlas/fluid/FluidBlockRegistryTest.kt b/src/test/kotlin/com/coderjoe/atlas/fluid/FluidBlockRegistryTest.kt index d2896df..db91ed8 100644 --- a/src/test/kotlin/com/coderjoe/atlas/fluid/FluidBlockRegistryTest.kt +++ b/src/test/kotlin/com/coderjoe/atlas/fluid/FluidBlockRegistryTest.kt @@ -1,6 +1,7 @@ package com.coderjoe.atlas.fluid import com.coderjoe.atlas.TestHelper +import com.coderjoe.atlas.core.BlockRegistry import com.coderjoe.atlas.fluid.block.FluidPipe import com.coderjoe.atlas.fluid.block.FluidPump import org.bukkit.block.BlockFace @@ -29,7 +30,7 @@ class FluidBlockRegistryTest { @Test fun `locationKey produces correct format`() { val loc = TestHelper.createLocation(5.0, 100.0, -3.0) - assertEquals("world:5,100,-3", FluidBlockRegistry.locationKey(loc)) + assertEquals("world:5,100,-3", BlockRegistry.locationKey(loc)) } @Test diff --git a/src/test/kotlin/com/coderjoe/atlas/fluid/FluidContainerTest.kt b/src/test/kotlin/com/coderjoe/atlas/fluid/FluidContainerTest.kt index 20d532c..db6b648 100644 --- a/src/test/kotlin/com/coderjoe/atlas/fluid/FluidContainerTest.kt +++ b/src/test/kotlin/com/coderjoe/atlas/fluid/FluidContainerTest.kt @@ -478,20 +478,4 @@ class FluidContainerTest { container.restoreState(FluidType.WATER, 99) assertEquals(FluidContainer.MAX_CAPACITY, container.storedAmount) } - - @Test - fun `FluidBlockData captures container facing and storedAmount`() { - val container = - FluidContainer(TestHelper.createLocation(), BlockFace.EAST) - repeat(5) { container.storeFluid(FluidType.WATER) } - - val data = - FluidBlockData.fromFluidBlock( - container, - "atlas:fluid_container", - ) - assertEquals("EAST", data.facing) - assertEquals(5, data.storedAmount) - assertEquals("WATER", data.fluidType) - } } diff --git a/src/test/kotlin/com/coderjoe/atlas/fluid/FluidSplitterTest.kt b/src/test/kotlin/com/coderjoe/atlas/fluid/FluidSplitterTest.kt index 5b4f2d0..fb2e643 100644 --- a/src/test/kotlin/com/coderjoe/atlas/fluid/FluidSplitterTest.kt +++ b/src/test/kotlin/com/coderjoe/atlas/fluid/FluidSplitterTest.kt @@ -172,4 +172,46 @@ class FluidSplitterTest { assertEquals("atlas:fluid_splitter", desc.baseBlockId) assertEquals("Fluid Splitter", desc.displayName) } + + @Test + fun `distributes fluid round-robin across multiple updates`() { + // Splitter facing NORTH, input from SOUTH + val splitterLoc = TestHelper.createLocation(0.0, 64.0, 0.0) + val splitter = FluidSplitter(splitterLoc, BlockFace.NORTH) + TestHelper.addToRegistry(registry, splitter, "atlas:fluid_splitter") + + // Two output pipes: east and west (both empty) + val eastLoc = TestHelper.createLocation(1.0, 64.0, 0.0) + val eastPipe = FluidPipe(eastLoc, BlockFace.EAST) + TestHelper.addToRegistry(registry, eastPipe, "atlas:fluid_pipe") + + val westLoc = TestHelper.createLocation(-1.0, 64.0, 0.0) + val westPipe = FluidPipe(westLoc, BlockFace.WEST) + TestHelper.addToRegistry(registry, westPipe, "atlas:fluid_pipe") + + // First update: give splitter fluid, push to one output + splitter.storeFluid(FluidType.WATER) + splitter.callFluidUpdate() + + val firstEast = eastPipe.hasFluid() + val firstWest = westPipe.hasFluid() + assertTrue(firstEast || firstWest, "fluid should go somewhere") + assertFalse(firstEast && firstWest, "only one should receive") + + // Drain the pipes and do it again + eastPipe.removeFluid() + westPipe.removeFluid() + splitter.storeFluid(FluidType.WATER) + splitter.callFluidUpdate() + + val secondEast = eastPipe.hasFluid() + val secondWest = westPipe.hasFluid() + assertTrue(secondEast || secondWest, "fluid should go somewhere") + + // The two rounds should have gone to different targets + assertTrue( + firstEast != secondEast, + "round-robin should alternate: firstEast=$firstEast, secondEast=$secondEast", + ) + } } diff --git a/src/test/kotlin/com/coderjoe/atlas/power/PowerBlockDataTest.kt b/src/test/kotlin/com/coderjoe/atlas/power/PowerBlockDataTest.kt deleted file mode 100644 index 447003e..0000000 --- a/src/test/kotlin/com/coderjoe/atlas/power/PowerBlockDataTest.kt +++ /dev/null @@ -1,128 +0,0 @@ -package com.coderjoe.atlas.power - -import com.coderjoe.atlas.TestHelper -import com.coderjoe.atlas.power.block.PowerCable -import com.coderjoe.atlas.power.block.SmallBattery -import com.coderjoe.atlas.power.block.SmallSolarPanel -import com.coderjoe.atlas.utility.block.SmallDrill -import org.bukkit.block.BlockFace -import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertNotNull -import org.junit.jupiter.api.Assertions.assertNull -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test - -class PowerBlockDataTest { - @BeforeEach - fun setup() { - TestHelper.setup() - } - - @AfterEach - fun teardown() { - TestHelper.teardown() - } - - @Test - fun `fromPowerBlock for SmallSolarPanel`() { - val loc = TestHelper.createLocation(10.0, 64.0, 20.0) - val panel = SmallSolarPanel(loc) - panel.currentPower = 1 - val data = PowerBlockData.fromPowerBlock(panel, "atlas:small_solar_panel") - - assertEquals("atlas:small_solar_panel", data.blockId) - assertEquals("world", data.world) - assertEquals(10, data.x) - assertEquals(64, data.y) - assertEquals(20, data.z) - assertEquals(1, data.currentPower) - assertNull(data.facing) - assertNull(data.enabled) - } - - @Test - fun `fromPowerBlock for PowerCable captures facing`() { - val cable = PowerCable(TestHelper.createLocation(), BlockFace.EAST) - val data = PowerBlockData.fromPowerBlock(cable, "atlas:power_cable") - assertEquals("EAST", data.facing) - } - - @Test - fun `fromPowerBlock for SmallDrill captures facing and enabled`() { - val drill = SmallDrill(TestHelper.createLocation(), BlockFace.NORTH) - drill.enabled = false - val data = PowerBlockData.fromPowerBlock(drill, "atlas:small_drill") - assertEquals("NORTH", data.facing) - assertEquals(false, data.enabled) - } - - @Test - fun `fromPowerBlock for SmallBattery captures facing`() { - val battery = SmallBattery(TestHelper.createLocation(), BlockFace.UP) - val data = PowerBlockData.fromPowerBlock(battery, "atlas:small_battery") - assertEquals("UP", data.facing) - } - - @Test - fun `toBlockFace with valid facing string`() { - val data = PowerBlockData("id", "world", 0, 0, 0, 0, facing = "NORTH") - assertEquals(BlockFace.NORTH, data.toBlockFace()) - } - - @Test - fun `toBlockFace with null facing returns SELF`() { - val data = PowerBlockData("id", "world", 0, 0, 0, 0, facing = null) - assertEquals(BlockFace.SELF, data.toBlockFace()) - } - - @Test - fun `toBlockFace with invalid string returns SELF`() { - val data = PowerBlockData("id", "world", 0, 0, 0, 0, facing = "INVALID") - assertEquals(BlockFace.SELF, data.toBlockFace()) - } - - @Test - fun `toLocation with valid world`() { - val data = PowerBlockData("id", "world", 5, 64, 10, 0) - val loc = data.toLocation(TestHelper.mockPlugin) - assertNotNull(loc) - assertEquals(5.0, loc!!.x) - assertEquals(64.0, loc.y) - assertEquals(10.0, loc.z) - } - - @Test - fun `toLocation with invalid world returns null`() { - val data = PowerBlockData("id", "nonexistent_world", 0, 0, 0, 0) - val loc = data.toLocation(TestHelper.mockPlugin) - assertNull(loc) - } - - @Test - fun `round-trip SmallSolarPanel preserves all fields`() { - val loc = TestHelper.createLocation(1.0, 2.0, 3.0) - val panel = SmallSolarPanel(loc) - panel.currentPower = 1 - val data = PowerBlockData.fromPowerBlock(panel, "atlas:small_solar_panel") - - assertEquals("atlas:small_solar_panel", data.blockId) - assertEquals(1, data.x) - assertEquals(2, data.y) - assertEquals(3, data.z) - assertEquals(1, data.currentPower) - } - - @Test - fun `round-trip SmallDrill preserves facing and enabled`() { - val drill = SmallDrill(TestHelper.createLocation(), BlockFace.WEST) - drill.enabled = true - drill.currentPower = 5 - val data = PowerBlockData.fromPowerBlock(drill, "atlas:small_drill") - - assertEquals("WEST", data.facing) - assertEquals(true, data.enabled) - assertEquals(5, data.currentPower) - assertEquals(BlockFace.WEST, data.toBlockFace()) - } -} diff --git a/src/test/kotlin/com/coderjoe/atlas/power/PowerBlockListenerTest.kt b/src/test/kotlin/com/coderjoe/atlas/power/PowerBlockListenerTest.kt index fa2f530..1ab1088 100644 --- a/src/test/kotlin/com/coderjoe/atlas/power/PowerBlockListenerTest.kt +++ b/src/test/kotlin/com/coderjoe/atlas/power/PowerBlockListenerTest.kt @@ -2,6 +2,7 @@ package com.coderjoe.atlas.power import com.coderjoe.atlas.TestHelper import com.coderjoe.atlas.core.AtlasBlockListener +import com.coderjoe.atlas.core.BlockRegistry import com.coderjoe.atlas.core.BlockSystem import com.coderjoe.atlas.power.block.SmallSolarPanel import io.mockk.every @@ -46,7 +47,7 @@ class PowerBlockListenerTest { @Test fun `onBlockPlace skips when location in updatingLocations`() { val loc = TestHelper.createLocation() - val key = PowerBlockRegistry.locationKey(loc) + val key = BlockRegistry.locationKey(loc) registry.updatingLocations.add(key) val block = mockk(relaxed = true) @@ -61,7 +62,7 @@ class PowerBlockListenerTest { @Test fun `onBlockBreak skips when in updatingLocations`() { val loc = TestHelper.createLocation() - val key = PowerBlockRegistry.locationKey(loc) + val key = BlockRegistry.locationKey(loc) registry.updatingLocations.add(key) val block = mockk(relaxed = true) diff --git a/src/test/kotlin/com/coderjoe/atlas/power/PowerBlockRegistryTest.kt b/src/test/kotlin/com/coderjoe/atlas/power/PowerBlockRegistryTest.kt index d2ba47c..6e33905 100644 --- a/src/test/kotlin/com/coderjoe/atlas/power/PowerBlockRegistryTest.kt +++ b/src/test/kotlin/com/coderjoe/atlas/power/PowerBlockRegistryTest.kt @@ -1,6 +1,7 @@ package com.coderjoe.atlas.power import com.coderjoe.atlas.TestHelper +import com.coderjoe.atlas.core.BlockRegistry import com.coderjoe.atlas.power.block.SmallBattery import com.coderjoe.atlas.power.block.SmallSolarPanel import org.bukkit.block.BlockFace @@ -29,7 +30,7 @@ class PowerBlockRegistryTest { @Test fun `locationKey produces correct format`() { val loc = TestHelper.createLocation(10.0, 64.0, -5.0) - val key = PowerBlockRegistry.locationKey(loc) + val key = BlockRegistry.locationKey(loc) assertEquals("world:10,64,-5", key) } diff --git a/src/test/kotlin/com/coderjoe/atlas/power/PowerSplitterTest.kt b/src/test/kotlin/com/coderjoe/atlas/power/PowerSplitterTest.kt index cc19d5f..21367c4 100644 --- a/src/test/kotlin/com/coderjoe/atlas/power/PowerSplitterTest.kt +++ b/src/test/kotlin/com/coderjoe/atlas/power/PowerSplitterTest.kt @@ -208,4 +208,49 @@ class PowerSplitterTest { splitter.callPowerUpdate() } } + + @Test + fun `distributes power round-robin across multiple updates`() { + val registry = PowerBlockRegistry(TestHelper.mockPlugin) + + // Splitter facing NORTH, input from SOUTH + val splitterLoc = TestHelper.createLocation(0.0, 64.0, 0.0) + val splitter = PowerSplitter(splitterLoc, BlockFace.NORTH) + TestHelper.addToRegistry(registry, splitter, "atlas:power_splitter") + + // Two output batteries: east and west (both can accept power) + val eastLoc = TestHelper.createLocation(1.0, 64.0, 0.0) + val eastBattery = SmallBattery(eastLoc, BlockFace.WEST) + TestHelper.addToRegistry(registry, eastBattery, "atlas:small_battery") + + val westLoc = TestHelper.createLocation(-1.0, 64.0, 0.0) + val westBattery = SmallBattery(westLoc, BlockFace.EAST) + TestHelper.addToRegistry(registry, westBattery, "atlas:small_battery") + + // Give splitter exactly 1 power and update — only one battery should get it + splitter.currentPower = 1 + splitter.callPowerUpdate() + + val firstEast = eastBattery.currentPower + val firstWest = westBattery.currentPower + assertEquals(1, firstEast + firstWest, "exactly 1 power distributed") + + // Reset and do it again — the OTHER battery should get power this time + splitter.currentPower = 1 + eastBattery.currentPower = 0 + westBattery.currentPower = 0 + splitter.callPowerUpdate() + + val secondEast = eastBattery.currentPower + val secondWest = westBattery.currentPower + assertEquals(1, secondEast + secondWest, "exactly 1 power distributed") + + // The two rounds should have gone to different targets + val firstWentEast = firstEast == 1 + val secondWentEast = secondEast == 1 + assertTrue( + firstWentEast != secondWentEast, + "round-robin should alternate: first=$firstWentEast, second=$secondWentEast", + ) + } }