diff --git a/proj/screen/rack.ase b/proj/screen/rack.ase index 6cc07de..a604c65 100644 Binary files a/proj/screen/rack.ase and b/proj/screen/rack.ase differ diff --git a/src/main/kotlin/net/liquidev/dawd3/block/device/DeviceBlockEntity.kt b/src/main/kotlin/net/liquidev/dawd3/block/device/DeviceBlockEntity.kt index 280ce9e..de62091 100644 --- a/src/main/kotlin/net/liquidev/dawd3/block/device/DeviceBlockEntity.kt +++ b/src/main/kotlin/net/liquidev/dawd3/block/device/DeviceBlockEntity.kt @@ -51,6 +51,22 @@ class DeviceBlockEntity( */ var shelf: UUID? = null + /** + * Order of shelves when the UI is open. + * + * This is propagated and merged between devices upon the UI being opened. + */ + val shelfOrder = mutableListOf() + + /** + * At which position the device should be sorted in the rack. Lower priorities mean earlier + * positions. + * + * Devices start out with the maximum possible priority and no shelf, which means they'll be + * appended to the end of the sidebar in an undefined order. + */ + var sortPriority = Int.MAX_VALUE + /** NBT compound keys. */ private object Nbt { const val controls = "controls" @@ -72,6 +88,8 @@ class DeviceBlockEntity( } const val shelf = "shelf" + const val shelfOrder = "shelfOrder" + const val sortPriority = "sortPriority" } override fun readNbt(nbt: NbtCompound) { @@ -131,9 +149,23 @@ class DeviceBlockEntity( outputConnections[port] = blockPosition } - if (nbt.containsUuid(Nbt.shelf)) { - shelf = nbt.getUuid(Nbt.shelf) + shelf = if (nbt.containsUuid(Nbt.shelf)) { + nbt.getUuid(Nbt.shelf) + } else { + null } + val shelfOrderNbt = nbt.getList(Nbt.shelfOrder, NbtElement.INT_ARRAY_TYPE.toInt()) + shelfOrder.clear() + for (i in 0 until shelfOrderNbt.size) { + try { + val uuid = NbtHelper.toUuid(shelfOrderNbt[i]) + shelfOrder.add(uuid) + } catch (_: IllegalArgumentException) { + // toUuid may throw an IllegalArgumentException if the UUID doesn't follow the + // expected format. In that case we just ignore the error and move on. + } + } + sortPriority = nbt.getInt(Nbt.sortPriority) } override fun writeNbt(nbt: NbtCompound) { @@ -170,6 +202,18 @@ class DeviceBlockEntity( outputConnectionsNbt.add(connectionNbt) } nbt.put(Nbt.outputConnections, outputConnectionsNbt) + + if (shelf != null) { + nbt.putUuid(Nbt.shelf, shelf) + } else { + nbt.remove(Nbt.shelf) + } + val shelfOrderNbt = NbtList() + for (shelfUuid in shelfOrder) { + shelfOrderNbt.add(NbtHelper.fromUuid(shelfUuid)) + } + nbt.put(Nbt.shelfOrder, shelfOrderNbt) + nbt.putInt(Nbt.sortPriority, sortPriority) } override fun onClientLoad(world: ClientWorld) { diff --git a/src/main/kotlin/net/liquidev/dawd3/common/lists.kt b/src/main/kotlin/net/liquidev/dawd3/common/lists.kt new file mode 100644 index 0000000..2ec761c --- /dev/null +++ b/src/main/kotlin/net/liquidev/dawd3/common/lists.kt @@ -0,0 +1,15 @@ +package net.liquidev.dawd3.common + +fun moveElement(from: MutableList, fromIndex: Int, to: MutableList, toIndex: Int) { + if (from == to && fromIndex != toIndex) { + val element = from.removeAt(fromIndex) + to.add( + if (fromIndex < toIndex) toIndex - 1 + else toIndex, + element, + ) + } else { + val element = from.removeAt(fromIndex) + to.add(toIndex, element) + } +} \ No newline at end of file diff --git a/src/main/kotlin/net/liquidev/dawd3/net/EditRack.kt b/src/main/kotlin/net/liquidev/dawd3/net/EditRack.kt new file mode 100644 index 0000000..5fb4187 --- /dev/null +++ b/src/main/kotlin/net/liquidev/dawd3/net/EditRack.kt @@ -0,0 +1,104 @@ +package net.liquidev.dawd3.net + +import net.fabricmc.fabric.api.networking.v1.PacketByteBufs +import net.fabricmc.fabric.api.networking.v1.PlayerLookup +import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking +import net.liquidev.dawd3.Mod +import net.liquidev.dawd3.block.device.DeviceBlockEntity +import net.liquidev.dawd3.net.serialization.readOptionalUuid +import net.liquidev.dawd3.net.serialization.writeOptionalUuid +import net.liquidev.dawd3.ui.widget.rack.shelves.ShelfEditing +import net.minecraft.block.Block +import net.minecraft.network.PacketByteBuf +import net.minecraft.util.Identifier +import net.minecraft.util.math.BlockPos +import java.util.* + +/** + * Assign a shelf and shelf order to a block at the given position. + * + * When sent C2S, the data of the device block entity at the given position is modified to write + * back its shelf UUID and shelf order. The server then propagates the shelf order to every block + * entity in the rack and sends this packet S2C to all witnesses of the propagation. + */ +data class EditRack( + val blockPosition: BlockPos, + val shelf: UUID?, + val shelfOrder: Array, +) { + companion object { + val id = Identifier(Mod.id, "edit_rack") + + fun registerServerReceiver() { + ServerPlayNetworking.registerGlobalReceiver(id) { server, player, _, buffer, _ -> + val packet = deserialize(buffer) + server.execute { + val blockState = player.world.getBlockState(packet.blockPosition) + val blockEntity = + player.world.getBlockEntity(packet.blockPosition) as? DeviceBlockEntity + ?: return@execute + blockEntity.shelf = packet.shelf + ShelfEditing.propagateShelfOrderData( + player.world, + packet.blockPosition, + packet.shelfOrder, + ) + blockEntity.markDirty() + + player.world.updateListeners( + packet.blockPosition, + blockState, + blockState, + Block.NOTIFY_LISTENERS, + ) + for (witness in PlayerLookup.tracking(blockEntity)) { + if (witness != player) { + ServerPlayNetworking.send(witness, id, buffer) + } + } + } + } + } + + fun registerClientReceiver() { + + } + + private fun deserialize(buffer: PacketByteBuf) = + EditRack( + blockPosition = buffer.readBlockPos(), + shelf = buffer.readOptionalUuid(), + shelfOrder = Array(buffer.readVarInt()) { buffer.readUuid() } + ) + } + + fun serialize(): PacketByteBuf = + PacketByteBufs.create() + .writeBlockPos(blockPosition) + .writeOptionalUuid(shelf) + .writeVarInt(shelfOrder.size) + .apply { + for (shelfUuid in shelfOrder) { + writeUuid(shelfUuid) + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as EditRack + + if (blockPosition != other.blockPosition) return false + if (shelf != other.shelf) return false + return shelfOrder.contentEquals(other.shelfOrder) + } + + override fun hashCode(): Int { + var result = blockPosition.hashCode() + result = 31 * result + (shelf?.hashCode() ?: 0) + result = 31 * result + shelfOrder.contentHashCode() + return result + } + +} \ No newline at end of file diff --git a/src/main/kotlin/net/liquidev/dawd3/net/Packets.kt b/src/main/kotlin/net/liquidev/dawd3/net/Packets.kt index ba1083c..b922cde 100644 --- a/src/main/kotlin/net/liquidev/dawd3/net/Packets.kt +++ b/src/main/kotlin/net/liquidev/dawd3/net/Packets.kt @@ -11,5 +11,7 @@ object Packets { fun registerServerReceivers() { TweakControl.registerServerReceiver() ControlTweaked.registerServerReceiver() + EditRack.registerServerReceiver() + ReorderRack.registerServerReceiver() } } \ No newline at end of file diff --git a/src/main/kotlin/net/liquidev/dawd3/net/ReorderRack.kt b/src/main/kotlin/net/liquidev/dawd3/net/ReorderRack.kt new file mode 100644 index 0000000..7b7bee3 --- /dev/null +++ b/src/main/kotlin/net/liquidev/dawd3/net/ReorderRack.kt @@ -0,0 +1,67 @@ +package net.liquidev.dawd3.net + +import net.fabricmc.fabric.api.networking.v1.PacketByteBufs +import net.fabricmc.fabric.api.networking.v1.PlayerLookup +import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking +import net.liquidev.dawd3.Mod +import net.liquidev.dawd3.block.device.DeviceBlockEntity +import net.minecraft.block.Block +import net.minecraft.network.PacketByteBuf +import net.minecraft.util.Identifier +import net.minecraft.util.math.BlockPos + +data class ReorderRack(val entries: List) { + data class Entry(val blockPosition: BlockPos, val sortPriority: Int) + + companion object { + val id = Identifier(Mod.id, "reorder_rack") + + fun registerServerReceiver() { + ServerPlayNetworking.registerGlobalReceiver(id) { server, player, _, buffer, _ -> + val packet = deserialize(buffer) + server.execute { + for (entry in packet.entries) { + val blockEntity = + player.world.getBlockEntity(entry.blockPosition) as? DeviceBlockEntity + ?: continue + blockEntity.sortPriority = entry.sortPriority + blockEntity.markDirty() + + val blockState = player.world.getBlockState(entry.blockPosition) + player.world.updateListeners( + entry.blockPosition, + blockState, + blockState, + Block.NOTIFY_LISTENERS + ) + for (witness in PlayerLookup.tracking(blockEntity)) { + if (witness != player) { + ServerPlayNetworking.send(witness, id, buffer) + } + } + } + } + } + } + + private fun deserialize(buffer: PacketByteBuf) = + ReorderRack( + entries = MutableList(buffer.readVarInt()) { + Entry( + blockPosition = buffer.readBlockPos(), + sortPriority = buffer.readVarInt(), + ) + } + ) + } + + fun serialize(): PacketByteBuf { + val buffer = PacketByteBufs.create() + buffer.writeVarInt(entries.size) + for (entry in entries) { + buffer.writeBlockPos(entry.blockPosition) + buffer.writeVarInt(entry.sortPriority) + } + return buffer + } +} \ No newline at end of file diff --git a/src/main/kotlin/net/liquidev/dawd3/net/serialization/bin.kt b/src/main/kotlin/net/liquidev/dawd3/net/serialization/bin.kt new file mode 100644 index 0000000..58ab8f0 --- /dev/null +++ b/src/main/kotlin/net/liquidev/dawd3/net/serialization/bin.kt @@ -0,0 +1,24 @@ +package net.liquidev.dawd3.net.serialization + +import net.minecraft.network.PacketByteBuf +import java.util.* + +val zeroUuid = UUID(0L, 0L) + +fun PacketByteBuf.readOptionalUuid(): UUID? { + val uuid = readUuid() + return if (uuid.leastSignificantBits == 0L && uuid.mostSignificantBits == 0L) { + null + } else { + uuid + } +} + +fun PacketByteBuf.writeOptionalUuid(uuid: UUID?): PacketByteBuf { + if (uuid != null) { + writeUuid(uuid) + } else { + writeUuid(zeroUuid) + } + return this +} diff --git a/src/main/kotlin/net/liquidev/dawd3/render/Render.kt b/src/main/kotlin/net/liquidev/dawd3/render/Render.kt index 45423c8..6ea393e 100644 --- a/src/main/kotlin/net/liquidev/dawd3/render/Render.kt +++ b/src/main/kotlin/net/liquidev/dawd3/render/Render.kt @@ -27,6 +27,7 @@ object Render { sprite: Sprite, ) { RenderSystem.setShaderTexture(0, atlas.asset) + RenderSystem.enableBlend() DrawableHelper.drawTexture( matrices, x.toInt(), @@ -166,6 +167,7 @@ object Render { ) { // Temporarily defined to just defer to DrawableHelper, later on we should introduce support // for rendering at floating-point coordinates. + RenderSystem.enableBlend() DrawableHelper.drawTexture( matrices, x.toInt(), @@ -365,7 +367,7 @@ object Render { tooltip(matrices, x, y, text, atlas, tooltip) } - inline fun colorized(r: Float, g: Float, b: Float, a: Float, then: () -> Unit) { + inline fun colorized(r: Float, g: Float, b: Float, a: Float, crossinline then: () -> Unit) { val (oldR, oldG, oldB, oldA) = RenderSystem.getShaderColor() RenderSystem.setShaderColor(r, g, b, a) RenderSystem.enableBlend() // sigh diff --git a/src/main/kotlin/net/liquidev/dawd3/ui/RackScreen.kt b/src/main/kotlin/net/liquidev/dawd3/ui/RackScreen.kt index 5db1095..4235478 100644 --- a/src/main/kotlin/net/liquidev/dawd3/ui/RackScreen.kt +++ b/src/main/kotlin/net/liquidev/dawd3/ui/RackScreen.kt @@ -21,7 +21,8 @@ class RackScreen( shownDevices: Iterable, ) : Screen(Text.translatable("screen.dawd3.rack.title")) { - private val rootWidget = Rack(width.toFloat(), height.toFloat(), world, shownDevices) + private val rootWidget = + Rack(width.toFloat(), height.toFloat(), world, shownDevices) override fun init() { super.init() diff --git a/src/main/kotlin/net/liquidev/dawd3/ui/widget/Widget.kt b/src/main/kotlin/net/liquidev/dawd3/ui/widget/Widget.kt index bca0763..e13a4ea 100644 --- a/src/main/kotlin/net/liquidev/dawd3/ui/widget/Widget.kt +++ b/src/main/kotlin/net/liquidev/dawd3/ui/widget/Widget.kt @@ -18,7 +18,7 @@ abstract class Widget(var x: Float, var y: Float) { abstract fun event(context: C, event: Event): M - inline fun drawInside(matrices: MatrixStack, draw: () -> Unit) { + inline fun drawInside(matrices: MatrixStack, crossinline draw: () -> Unit) { matrices.push() matrices.translate(x.toDouble(), y.toDouble(), 0.0) draw() diff --git a/src/main/kotlin/net/liquidev/dawd3/ui/widget/rack/Rack.kt b/src/main/kotlin/net/liquidev/dawd3/ui/widget/rack/Rack.kt index 3686ed8..266b152 100644 --- a/src/main/kotlin/net/liquidev/dawd3/ui/widget/rack/Rack.kt +++ b/src/main/kotlin/net/liquidev/dawd3/ui/widget/rack/Rack.kt @@ -1,6 +1,10 @@ package net.liquidev.dawd3.ui.widget.rack +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking import net.liquidev.dawd3.block.device.DeviceBlockEntity +import net.liquidev.dawd3.common.moveElement +import net.liquidev.dawd3.net.EditRack +import net.liquidev.dawd3.net.ReorderRack import net.liquidev.dawd3.render.NinePatch import net.liquidev.dawd3.render.Render import net.liquidev.dawd3.render.TextureStrip @@ -12,6 +16,7 @@ import net.minecraft.client.util.math.MatrixStack import net.minecraft.client.world.ClientWorld import net.minecraft.util.math.BlockPos import org.lwjgl.glfw.GLFW +import java.util.* class Rack( override var width: Float, @@ -20,10 +25,10 @@ class Rack( shownDevices: Iterable, ) : Widget(0f, 0f) { - private val sidebar = Sidebar(0f, 0f, 160f, 0f) + private val sidebar = Sidebar(0f, 0f, 0f, 0f) private val blockPositionsByWidget = hashMapOf() - private val shelves = MutableList(10) { Shelf(0f, 0f, width) } + private val shelves = mutableListOf() enum class DragPlace { Sidebar, @@ -33,6 +38,7 @@ class Rack( data class DraggedWindow( val window: Window, val source: DragPlace, + val indexInSource: Int, val byX: Float, val byY: Float, @@ -42,14 +48,45 @@ class Rack( private var draggedWindow: DraggedWindow? = null init { + val shelvesByUuid = hashMapOf() + for (blockPosition in shownDevices) { val blockEntity = world.getBlockEntity(blockPosition) as? DeviceBlockEntity ?: continue - val widget = blockEntity.descriptor.ui?.open(blockEntity.controls, x = 0f, y = 0f) - if (widget != null) { - sidebar.windows.add(widget) - blockPositionsByWidget[widget] = blockPosition + for (shelfUuid in blockEntity.shelfOrder) { + if (shelfUuid !in shelvesByUuid) { + val shelf = Shelf(0f, 0f, width, shelfUuid) + shelves.add(shelf) + shelvesByUuid[shelfUuid] = shelf + } } } + + for (blockPosition in shownDevices) { + val blockEntity = world.getBlockEntity(blockPosition) as? DeviceBlockEntity ?: continue + val window = blockEntity.descriptor.ui?.open(blockEntity.controls, x = 0f, y = 0f) + if (window != null) { + val shelfUuid = blockEntity.shelf + if (shelfUuid != null) { + shelvesByUuid[shelfUuid]?.windows?.add(window) + } else { + sidebar.windows.add(window) + } + blockPositionsByWidget[window] = blockPosition + } + } + removeEmptyShelvesFromEnd() + addEmptyShelfIfNotPresent() + + sortWindowsInContainer(sidebar.windows) + shelves.forEach { sortWindowsInContainer(it.windows) } + } + + private fun sortWindowsInContainer(container: MutableList) { + container.sortBy { window -> + val blockPosition = blockPositionsByWidget[window] + val blockEntity = world.getBlockEntity(blockPosition) as? DeviceBlockEntity + blockEntity?.sortPriority ?: Int.MAX_VALUE + } } private fun removeStaleWidgetsFromContainer(container: MutableList) { @@ -67,6 +104,9 @@ class Rack( for (shelf in shelves) { shelf.draw(matrices, mouseX, mouseY, deltaTime) } + if (shelves.size == 1 && shelves.last().windows.isEmpty()) { + shelves.last().drawUsageHint(matrices) + } sidebar.draw(matrices, mouseX, mouseY, deltaTime) @@ -83,7 +123,6 @@ class Rack( ) val destination = findDragDestination(mouseX, mouseY) - println(destination) when (destination?.place) { DragPlace.Sidebar -> { sidebar.drawInside(matrices) { @@ -91,7 +130,7 @@ class Rack( Sidebar.padding } else if (destination.indexInPlace == sidebar.windows.size) { val window = sidebar.windows.last() - window.y + window.height - Sidebar.spacingBetweenWindows / 2f + window.y + window.height + Sidebar.spacingBetweenWindows / 2f } else { val window = sidebar.windows[destination.indexInPlace] window.y - Sidebar.spacingBetweenWindows / 2f @@ -143,7 +182,7 @@ class Rack( source: DragPlace, sourceShelfIndex: Int = 0, ): Message { - for (window in windows) { + windows.forEachIndexed { index, window -> val blockPosition = blockPositionsByWidget[window]!! val windowRelativeEvent = event.relativeTo(window.x, window.y) val windowMessage = @@ -153,6 +192,7 @@ class Rack( DraggedWindow( window, source, + index, windowMessage.x, windowMessage.y, sourceShelfIndex @@ -192,21 +232,38 @@ class Rack( if (draggedWindow != null) { val destination = findDragDestination(event.mouseX, event.mouseY) if (destination != null) { - removeWindowFromSource( - draggedWindow.source, - draggedWindow.sourceShelfIndex, - draggedWindow.window, - ) - when (destination.place) { - DragPlace.Sidebar -> sidebar.windows.add( - destination.indexInPlace, - draggedWindow.window - ) - DragPlace.Shelf -> shelves[destination.shelfIndex].windows.add( - destination.indexInPlace, - draggedWindow.window - ) + val fromList: MutableList = when (draggedWindow.source) { + DragPlace.Sidebar -> sidebar.windows + DragPlace.Shelf -> shelves[draggedWindow.sourceShelfIndex].windows } + val toList: MutableList = when (destination.place) { + DragPlace.Sidebar -> sidebar.windows + DragPlace.Shelf -> shelves[destination.shelfIndex].windows + } + moveElement( + fromList, + fromIndex = draggedWindow.indexInSource, + toList, + toIndex = destination.indexInPlace + ) + + val blockPosition = blockPositionsByWidget[draggedWindow.window] + if (blockPosition != null) { + when (destination.place) { + DragPlace.Shelf -> { + val shelf = shelves[destination.shelfIndex] + updateBlockData(blockPosition, shelf.uuid) + } + DragPlace.Sidebar -> { + updateBlockData(blockPosition, null) + } + } + } + + removeEmptyShelvesFromEnd() + addEmptyShelfIfNotPresent() + updateBlockPriorities(fromList) + updateBlockPriorities(toList) reflow() } } @@ -216,6 +273,27 @@ class Rack( return Message.eventIgnored } + private fun removeEmptyShelvesFromEnd() { + while (shelves.size > 1 && shelves.last().windows.isEmpty()) { + shelves.removeLast() + } + } + + private fun addEmptyShelfIfNotPresent() { + if (shelves.size == 0 || shelves.last().windows.isNotEmpty()) { + shelves.add(Shelf(0f, 0f, width, UUID.randomUUID())) + } + } + + private fun updateBlockPriorities(windows: MutableList) { + val packetEntries = mutableListOf() + windows.forEachIndexed { index, window -> + val blockPosition = blockPositionsByWidget[window] ?: return@forEachIndexed + packetEntries.add(ReorderRack.Entry(blockPosition, index)) + } + ClientPlayNetworking.send(ReorderRack.id, ReorderRack(packetEntries).serialize()) + } + internal data class DragDestination( val place: DragPlace, val indexInPlace: Int, @@ -238,22 +316,8 @@ class Rack( return null } - private fun removeWindowFromSource( - source: DragPlace, - indexOfSource: Int, - window: Window, - ) { - when (source) { - DragPlace.Sidebar -> { - sidebar.windows.remove(window) - } - DragPlace.Shelf -> { - shelves[indexOfSource].windows.remove(window) - } - } - } - override fun reflow() { + sidebar.width = if (sidebar.windows.isNotEmpty()) 160f else 16f sidebar.x = width - sidebar.width sidebar.height = height sidebar.reflow() @@ -262,11 +326,21 @@ class Rack( for (shelf in shelves) { shelf.reflow() shelf.y = dy - shelf.width = width + shelf.width = width - sidebar.width dy += shelf.height } } + private fun updateBlockData(editedDevicePosition: BlockPos, newShelfUuid: UUID?) { + ClientPlayNetworking.send( + EditRack.id, EditRack( + editedDevicePosition, + shelf = newShelfUuid, + shelfOrder = shelves.map { it.uuid }.toTypedArray() + ).serialize() + ) + } + companion object { private val windowDraggingPreview = NinePatch(u = 48f, v = 0f, width = 8f, height = 8f, border = 2f) diff --git a/src/main/kotlin/net/liquidev/dawd3/ui/widget/rack/Shelf.kt b/src/main/kotlin/net/liquidev/dawd3/ui/widget/rack/Shelf.kt index 5448aec..b74c57d 100644 --- a/src/main/kotlin/net/liquidev/dawd3/ui/widget/rack/Shelf.kt +++ b/src/main/kotlin/net/liquidev/dawd3/ui/widget/rack/Shelf.kt @@ -1,5 +1,6 @@ package net.liquidev.dawd3.ui.widget.rack +import net.liquidev.dawd3.common.rgba import net.liquidev.dawd3.render.Render import net.liquidev.dawd3.render.TextureStrip import net.liquidev.dawd3.ui.Event @@ -8,9 +9,16 @@ import net.liquidev.dawd3.ui.RackScreen import net.liquidev.dawd3.ui.widget.Widget import net.liquidev.dawd3.ui.widget.Window import net.minecraft.client.util.math.MatrixStack +import net.minecraft.text.Text +import java.util.* import kotlin.math.max -class Shelf(x: Float, y: Float, override var width: Float) : Widget(x, y) { +class Shelf( + x: Float, + y: Float, + override var width: Float, + val uuid: UUID, +) : Widget(x, y) { override var height = 128f private set @@ -39,6 +47,20 @@ class Shelf(x: Float, y: Float, override var width: Float) : Widget(x, y) { diff --git a/src/main/kotlin/net/liquidev/dawd3/ui/widget/rack/shelves/ShelfEditing.kt b/src/main/kotlin/net/liquidev/dawd3/ui/widget/rack/shelves/ShelfEditing.kt new file mode 100644 index 0000000..730a2a3 --- /dev/null +++ b/src/main/kotlin/net/liquidev/dawd3/ui/widget/rack/shelves/ShelfEditing.kt @@ -0,0 +1,41 @@ +package net.liquidev.dawd3.ui.widget.rack.shelves + +import net.liquidev.dawd3.block.device.DeviceBlockEntity +import net.minecraft.block.Block +import net.minecraft.util.math.BlockPos +import net.minecraft.world.World +import java.util.* + +object ShelfEditing { + fun propagateShelfOrderData(world: World, from: BlockPos, shelfOrder: Array) { + propagateShelfOrderDataRec(world, from, HashSet(), shelfOrder) + } + + private fun propagateShelfOrderDataRec( + world: World, + position: BlockPos, + traversed: HashSet, + sourceShelfOrder: Array, + ) { + val thisBlockEntity = world.getBlockEntity(position) as? DeviceBlockEntity ?: return + if (position !in traversed) { + traversed.add(position) + + // Maybe not the fastest thing... but I don't want to share a single piece of mutable + // data between all the block entities, as that can cause weird bugs later down the line. + thisBlockEntity.shelfOrder.clear() + thisBlockEntity.shelfOrder.addAll(sourceShelfOrder) + thisBlockEntity.markDirty() + + val blockState = world.getBlockState(position) + world.updateListeners(position, blockState, blockState, Block.NOTIFY_LISTENERS) + + propagateShelfOrderDataRec(world, position.up(), traversed, sourceShelfOrder) + propagateShelfOrderDataRec(world, position.down(), traversed, sourceShelfOrder) + propagateShelfOrderDataRec(world, position.north(), traversed, sourceShelfOrder) + propagateShelfOrderDataRec(world, position.south(), traversed, sourceShelfOrder) + propagateShelfOrderDataRec(world, position.east(), traversed, sourceShelfOrder) + propagateShelfOrderDataRec(world, position.west(), traversed, sourceShelfOrder) + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/dawd3/lang/en_us.json b/src/main/resources/assets/dawd3/lang/en_us.json index c02c50b..fb33adb 100644 --- a/src/main/resources/assets/dawd3/lang/en_us.json +++ b/src/main/resources/assets/dawd3/lang/en_us.json @@ -32,7 +32,7 @@ "block.dawd3.saw_oscillator": "Saw Oscillator", "block.dawd3.sine_oscillator": "Sine Oscillator", "block.dawd3.triangle_oscillator": "Triangle Oscillator", - "screen.dawd3.rack.title": "Rack", + "screen.dawd3.rack.shelf_hint": "Drag devices here to organize them", "dawd3.control.dawd3.adsr.attack": "ATT", "dawd3.control.dawd3.adsr.decay": "DEC", "dawd3.control.dawd3.adsr.sustain": "SUS", diff --git a/src/main/resources/assets/dawd3/textures/ui/rack.png b/src/main/resources/assets/dawd3/textures/ui/rack.png index 015cb16..7b6892d 100644 Binary files a/src/main/resources/assets/dawd3/textures/ui/rack.png and b/src/main/resources/assets/dawd3/textures/ui/rack.png differ