Matter panel sorting (with buttons), cleanup nbt extensions

This commit is contained in:
DBotThePony 2023-03-06 00:20:59 +07:00
parent f4146ffea9
commit 441f358e12
Signed by: DBot
GPG Key ID: DCC23B5715498507
32 changed files with 534 additions and 221 deletions

View File

@ -638,6 +638,14 @@ private fun gui(provider: MatteryLanguageProvider) {
gui("side_mode.pull", "Pull")
gui("side_mode.push", "Push")
gui("sorting.default", "Default sorting")
gui("sorting.name", "Sort by name")
gui("sorting.id", "Sort by ID")
gui("sorting.modid", "Sort by Namespace (mod) ID")
gui("sorting.count", "Sort by amount")
gui("sorting.ascending", "Ascending")
gui("sorting.descending", "Descending")
}
}

View File

@ -643,6 +643,14 @@ private fun gui(provider: MatteryLanguageProvider) {
gui("side_mode.pull", "Автоматическое вытягивание")
gui("side_mode.push", "Автоматическое выталкивание")
gui("sorting.default", "Сортировка по умолчанию")
gui("sorting.name", "Сортировка по имени")
gui("sorting.id", "Сортировка по ID")
gui("sorting.modid", "Сортировка по пространству имён (моду)")
gui("sorting.count", "Сортировка по количеству")
gui("sorting.ascending", "Возрастающая")
gui("sorting.descending", "Убывающая")
}
}

View File

@ -2,30 +2,23 @@ package ru.dbotthepony.mc.otm.block.entity
import net.minecraft.ChatFormatting
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.chat.Component
import net.minecraft.world.item.BlockItem
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.TooltipFlag
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.entity.BlockEntityType
import net.minecraft.world.level.block.state.BlockState
import net.minecraftforge.common.capabilities.Capability
import net.minecraftforge.common.capabilities.ForgeCapabilities
import net.minecraftforge.common.util.LazyOptional
import ru.dbotthepony.mc.otm.capability.*
import ru.dbotthepony.mc.otm.capability.energy.BlockEnergyStorageImpl
import ru.dbotthepony.mc.otm.capability.energy.ItemEnergyStorageImpl
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.nbt.ifHas
import ru.dbotthepony.mc.otm.core.ifPresentK
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.nbt.map
import ru.dbotthepony.mc.otm.core.nbt.set
abstract class MatteryPoweredBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: BlockPos, p_155230_: BlockState) : MatteryDeviceBlockEntity(p_155228_, p_155229_, p_155230_) {
val batteryContainer = MatteryContainer(::setChangedLight, 1).also(::addDroppableContainer)

View File

@ -2,7 +2,7 @@ package ru.dbotthepony.mc.otm.block.entity
import net.minecraft.nbt.CompoundTag
import net.minecraftforge.common.util.INBTSerializable
import ru.dbotthepony.mc.otm.core.nbt.getEnum
import ru.dbotthepony.mc.otm.core.nbt.mapString
import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.network.FieldSynchronizer
@ -30,7 +30,7 @@ abstract class AbstractRedstoneControl : INBTSerializable<CompoundTag?> {
return
}
redstoneSetting = nbt.getEnum(SETTING_KEY)
redstoneSetting = nbt.mapString(SETTING_KEY, RedstoneSetting::valueOf, RedstoneSetting.LOW)
redstoneSignal = nbt.getInt(SIGNAL_KEY)
}

View File

@ -27,7 +27,7 @@ import ru.dbotthepony.mc.otm.registry.MBlockEntities
import ru.dbotthepony.mc.otm.registry.MItems
import ru.dbotthepony.mc.otm.registry.MRegistry
import ru.dbotthepony.mc.otm.core.math.getSphericalBlockPositions
import ru.dbotthepony.mc.otm.core.nbt.mapIf
import ru.dbotthepony.mc.otm.core.nbt.map
import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.matter.MatterManager
import ru.dbotthepony.mc.otm.triggers.BlackHoleTrigger
@ -164,7 +164,7 @@ class BlackHoleBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mattery
override fun load(tag: CompoundTag) {
super.load(tag)
mass = tag.mapIf("mass", Decimal::deserializeNBT) ?: BASELINE_MASS
mass = tag.map("mass", Decimal::deserializeNBT) ?: BASELINE_MASS
spinDirection = tag.getBoolean("spin_direction")
}

View File

@ -1,5 +1,7 @@
package ru.dbotthepony.mc.otm.block.entity.matter
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
import net.minecraft.core.BlockPos
import net.minecraft.world.level.block.state.BlockState
import ru.dbotthepony.mc.otm.menu.matter.MatterPanelMenu
@ -11,13 +13,17 @@ import java.util.HashMap
import java.util.UUID
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag
import net.minecraft.nbt.Tag
import net.minecraft.network.chat.Component
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.level.Level
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import net.minecraftforge.common.util.INBTSerializable
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.capability.matter.*
import ru.dbotthepony.mc.otm.core.nbt.getBoolean
import ru.dbotthepony.mc.otm.core.nbt.getCompoundList
import ru.dbotthepony.mc.otm.core.nbt.map
import ru.dbotthepony.mc.otm.core.nbt.mapString
import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.core.util.ItemSorter
import ru.dbotthepony.mc.otm.graph.Graph6Node
import ru.dbotthepony.mc.otm.graph.matter.IMatterGraphNode
import ru.dbotthepony.mc.otm.graph.matter.MatterNetworkGraph
@ -28,6 +34,26 @@ import java.util.stream.Stream
class MatterPanelBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
MatteryDeviceBlockEntity(MBlockEntities.MATTER_PANEL, p_155229_, p_155230_), IMatterGraphNode, IReplicationTaskProvider {
class PlayerSettings(var sorter: ItemSorter = ItemSorter.DEFAULT, var ascending: Boolean = true) : INBTSerializable<CompoundTag> {
override fun serializeNBT(): CompoundTag {
return CompoundTag().also {
it["sorter"] = sorter.name
it["ascending"] = ascending
}
}
override fun deserializeNBT(nbt: CompoundTag) {
sorter = nbt.mapString("sorter", ItemSorter::valueOf, ItemSorter.DEFAULT)
ascending = nbt.getBoolean("ascending", true)
}
}
private val playerSettings = Object2ObjectOpenHashMap<UUID, PlayerSettings>()
fun getPlayerSettings(ply: Player): PlayerSettings {
return playerSettings.computeIfAbsent(ply.uuid, Object2ObjectFunction { PlayerSettings() })
}
private val listeners = ArrayList<MatterPanelMenu>()
override val matterNode = Graph6Node<IMatterGraphNode>(this)
@ -39,9 +65,6 @@ class MatterPanelBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
listeners.remove(menu)
}
override val defaultDisplayName: Component
get() = MACHINE_NAME
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {
return MatterPanelMenu(containerID, inventory, this)
}
@ -131,13 +154,21 @@ class MatterPanelBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
list.add(task.serializeNBT())
}
nbt.put("tasks", list)
nbt["tasks"] = list
val settings = CompoundTag()
for ((uuid, value) in playerSettings) {
settings[uuid.toString()] = value.serializeNBT()
}
nbt["settings"] = settings
}
override fun load(nbt: CompoundTag) {
super.load(nbt)
_tasks.clear()
val list = nbt.getList("tasks", Tag.TAG_COMPOUND.toInt())
val list = nbt.getCompoundList("tasks")
for (tag in list) {
val task = ReplicationTask.deserializeNBT(tag)
@ -146,6 +177,15 @@ class MatterPanelBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
_tasks[task.id] = task
}
}
playerSettings.clear()
nbt.map("settings") { it: CompoundTag ->
for (k in it.allKeys) {
playerSettings.computeIfAbsent(UUID.fromString(k), Object2ObjectFunction { PlayerSettings() })
.deserializeNBT(it.getCompound(k))
}
}
}
override fun getTask(id: UUID): ReplicationTask? {
@ -184,8 +224,4 @@ class MatterPanelBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
_tasks.clear()
}
companion object {
private val MACHINE_NAME = TranslatableComponent("block.overdrive_that_matters.matter_panel")
}
}

View File

@ -35,7 +35,7 @@ import ru.dbotthepony.mc.otm.graph.storage.StorageNetworkGraph
import ru.dbotthepony.mc.otm.network.MatteryPacket
import ru.dbotthepony.mc.otm.registry.MBlockEntities
import ru.dbotthepony.mc.otm.container.set
import ru.dbotthepony.mc.otm.core.nbt.getEnum
import ru.dbotthepony.mc.otm.core.nbt.mapString
import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.menu.storage.ItemMonitorMenu
import ru.dbotthepony.mc.otm.storage.*
@ -93,9 +93,9 @@ class ItemMonitorPlayerSettings : INBTSerializable<CompoundTag>, MatteryPacket {
}
override fun deserializeNBT(nbt: CompoundTag) {
ingredientPriority = nbt.getEnum(INGREDIENT_PRIORITY_KEY)
resultTarget = nbt.getEnum(RESULT_TARGET_KEY)
craftingAmount = nbt.getEnum(QUICK_CRAFT_AMOUNT_KEY)
ingredientPriority = nbt.mapString(INGREDIENT_PRIORITY_KEY, IngredientPriority::valueOf, IngredientPriority.SYSTEM)
resultTarget = nbt.mapString(RESULT_TARGET_KEY, ResultTarget::valueOf, ResultTarget.MIXED)
craftingAmount = nbt.mapString(QUICK_CRAFT_AMOUNT_KEY, Amount::valueOf, Amount.STACK)
}
fun read(buff: FriendlyByteBuf) {

View File

@ -20,7 +20,6 @@ import ru.dbotthepony.mc.otm.core.math.BlockRotation
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.getDecimal
import ru.dbotthepony.mc.otm.core.nbt.getByteArrayList
import ru.dbotthepony.mc.otm.core.nbt.ifHas
import ru.dbotthepony.mc.otm.core.nbt.map
import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.menu.tech.EnergyCounterMenu
@ -101,7 +100,7 @@ class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mat
passed = nbt.getDecimal(PASSED_ENERGY_KEY)
ioLimit = nbt.map(IO_LIMIT_KEY, Decimal.Companion::deserializeNBT)
nbt.ifHas(POWER_HISTORY_POINTER_KEY, IntTag::class.java) {
nbt.map(POWER_HISTORY_POINTER_KEY) { it: IntTag ->
historyTick = it.asInt
}

View File

@ -11,7 +11,7 @@ import ru.dbotthepony.mc.otm.core.*
import ru.dbotthepony.mc.otm.core.math.BigInteger
import ru.dbotthepony.mc.otm.core.math.isPositive
import ru.dbotthepony.mc.otm.core.math.serializeNBT
import ru.dbotthepony.mc.otm.core.nbt.ifHas
import ru.dbotthepony.mc.otm.core.nbt.map
import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.storage.*
import java.math.BigInteger
@ -176,10 +176,7 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
storedDifferentStacks = 0
// nextID = 0L
nbt.ifHas("capacity") {
driveCapacity = BigInteger(it)
}
driveCapacity = nbt.map("capacity", ::BigInteger) ?: driveCapacity
maxDifferentStacks = nbt.getInt("max_different_stacks")
for (entry in nbt.getList("items", Tag.TAG_COMPOUND.toInt())) {

View File

@ -16,12 +16,10 @@ import net.minecraftforge.common.util.LazyOptional
import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity
import ru.dbotthepony.mc.otm.config.ConciseBalanceValues
import ru.dbotthepony.mc.otm.config.VerboseBalanceValues
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.defineDecimal
import ru.dbotthepony.mc.otm.core.nbt.map
import ru.dbotthepony.mc.otm.core.nbt.mapIf
import ru.dbotthepony.mc.otm.core.nbt.set
sealed class BlockEnergyStorageImpl(
@ -142,9 +140,9 @@ sealed class BlockEnergyStorageImpl(
override fun deserializeNBT(nbt: CompoundTag?) {
if (nbt == null) return
batteryLevel = nbt.map(ENERGY_STORED_KEY, Decimal.Companion::deserializeNBT) ?: Decimal.ZERO
maxBatteryLevelStorage = nbt.mapIf(ENERGY_STORED_MAX_KEY, Decimal.Companion::deserializeNBT)
maxInputStorage = nbt.mapIf(MAX_INPUT_KEY, Decimal.Companion::deserializeNBT)
maxOutputStorage = nbt.mapIf(MAX_OUTPUT_KEY, Decimal.Companion::deserializeNBT)
maxBatteryLevelStorage = nbt.map(ENERGY_STORED_MAX_KEY, Decimal.Companion::deserializeNBT)
maxInputStorage = nbt.map(MAX_INPUT_KEY, Decimal.Companion::deserializeNBT)
maxOutputStorage = nbt.map(MAX_OUTPUT_KEY, Decimal.Companion::deserializeNBT)
}
var resolver: LazyOptional<IMatteryEnergyStorage> = LazyOptional.of { this }

View File

@ -5,7 +5,7 @@ import ru.dbotthepony.mc.otm.OverdriveThatMatters
object WidgetLocation {
val LARGE_BUTTON = MatteryAtlas(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/widgets/large_button.png"), 72f, 18f)
val STORAGE_CONTROLS = MatteryAtlas(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/widgets/storage_controls.png"), 90f, 18f)
val STORAGE_CONTROLS = MatteryAtlas(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/widgets/storage_controls.png"), 90f, 36f)
val MISC_18 = MatteryAtlas(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/widgets/misc18.png"), 72f, 72f)
val SLOT_BACKGROUNDS = MatteryAtlas(ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/widgets/slot_backgrounds.png"), 72f, 72f)

View File

@ -34,12 +34,14 @@ object Widgets18 {
val BUTTON_DISABLED_STRETCHABLE = makeButton(buttonGrids)
val BUTTON_DISABLED = buttonGrids.next()
private val storageGrid = WidgetLocation.STORAGE_CONTROLS.grid(rows = 1, columns = 5)
private val storageGrid = WidgetLocation.STORAGE_CONTROLS.grid(rows = 2, columns = 5)
val ARROW_DOWN = storageGrid.next()
val AZ = storageGrid.next()
val COUNT = storageGrid.next()
val COLON = storageGrid.next()
val C = storageGrid.next()
val ARROW_UP = storageGrid.next()
val SORT_DEFAULT = storageGrid.next()
val SORT_ALPHABET = storageGrid.next()
val SORT_COUNT = storageGrid.next()
val SORT_MODID = storageGrid.next()
val SORT_ID = storageGrid.next()
private val miscGrid = WidgetLocation.MISC_18.grid(4, 4)

View File

@ -8,15 +8,20 @@ import net.minecraft.world.item.ItemStack
import ru.dbotthepony.mc.otm.capability.matter.IPatternState
import ru.dbotthepony.mc.otm.capability.matter.IReplicationTask
import ru.dbotthepony.mc.otm.client.render.WidgetLocation
import ru.dbotthepony.mc.otm.client.render.Widgets18
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
import ru.dbotthepony.mc.otm.client.screen.panels.*
import ru.dbotthepony.mc.otm.client.screen.panels.button.ButtonPanel
import ru.dbotthepony.mc.otm.client.screen.panels.button.DeviceControls
import ru.dbotthepony.mc.otm.client.screen.panels.button.LargeBooleanRectangleButtonPanel
import ru.dbotthepony.mc.otm.client.screen.panels.button.LargeEnumRectangleButtonPanel
import ru.dbotthepony.mc.otm.client.screen.panels.input.EditBoxPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.util.DiscreteScrollBarPanel
import ru.dbotthepony.mc.otm.client.screen.panels.util.ScrollBarConstants
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.math.integerDivisionDown
import ru.dbotthepony.mc.otm.core.util.ItemSorter
import ru.dbotthepony.mc.otm.menu.matter.MatterPanelMenu
import ru.dbotthepony.mc.otm.menu.matter.ReplicationRequestPacket
import ru.dbotthepony.mc.otm.network.MenuNetworkChannel
@ -28,26 +33,50 @@ class MatterPanelScreen(
inventory: Inventory,
title: Component
) : MatteryScreen<MatterPanelMenu>(menu, inventory, title) {
override fun makeMainFrame(): FramePanel<out MatteryScreen<*>> {
override fun makeMainFrame(): FramePanel<MatteryScreen<*>> {
var isPatternView = true
var scrollPatterns = 0
var scrollTasks = 0
val frame = FramePanel.padded(this, null, GRID_WIDTH * AbstractSlotPanel.SIZE + ScrollBarConstants.WIDTH + 4f, GRID_HEIGHT * AbstractSlotPanel.SIZE, title)
val controls = DeviceControls(this, frame)
LargeBooleanRectangleButtonPanel(this, controls,
prop = menu.isAscendingGS,
skinElementActive = Widgets18.ARROW_UP,
skinElementInactive = Widgets18.ARROW_DOWN,
tooltipActive = TranslatableComponent("otm.gui.sorting.ascending"),
tooltipInactive = TranslatableComponent("otm.gui.sorting.descending"),
).also {
controls.addButton(it)
}
LargeEnumRectangleButtonPanel(this, controls, enum = ItemSorter::class.java, prop = menu.sortingGS, defaultValue = ItemSorter.DEFAULT).also {
controls.addButton(it)
it.add(ItemSorter.DEFAULT, skinElement = Widgets18.SORT_DEFAULT, tooltip = ItemSorter.DEFAULT.title)
it.add(ItemSorter.NAME, skinElement = Widgets18.SORT_ALPHABET, tooltip = ItemSorter.NAME.title)
it.add(ItemSorter.ID, skinElement = Widgets18.SORT_ID, tooltip = ItemSorter.ID.title)
it.add(ItemSorter.MOD, skinElement = Widgets18.SORT_MODID, tooltip = ItemSorter.MOD.title)
}
val scrollBar = DiscreteScrollBarPanel(this, frame, {
if (isPatternView) {
integerDivisionDown(menu.patterns.size, GRID_WIDTH)
scrollPatterns = integerDivisionDown(menu.patterns.size, GRID_WIDTH)
scrollPatterns
} else {
integerDivisionDown(menu.tasks.size, GRID_WIDTH)
scrollTasks = integerDivisionDown(menu.tasks.size, GRID_WIDTH)
scrollTasks
}
}, { _, _, _ -> })
scrollBar.dock = Dock.RIGHT
frame.Tab(onOpen = { isPatternView = true }, activeIcon = PATTERN_LIST_ACTIVE, inactiveIcon = PATTERN_LIST_INACTIVE).also {
frame.Tab(onOpen = { isPatternView = true; scrollBar.scroll = scrollPatterns }, activeIcon = PATTERN_LIST_ACTIVE, inactiveIcon = PATTERN_LIST_INACTIVE).also {
it.tooltip = TranslatableComponent("otm.container.matter_panel.patterns")
}
frame.Tab(onOpen = { isPatternView = false }, activeIcon = TASK_LIST_ACTIVE, inactiveIcon = TASK_LIST_INACTIVE).also {
frame.Tab(onOpen = { isPatternView = false; scrollBar.scroll = scrollTasks }, activeIcon = TASK_LIST_ACTIVE, inactiveIcon = TASK_LIST_INACTIVE).also {
it.tooltip = TranslatableComponent("otm.container.matter_panel.tasks")
}
@ -133,6 +162,8 @@ class MatterPanelScreen(
private fun openTask(task: IReplicationTask<*>) {
val frame = FramePanel.padded(this, null, 170f, 20f, TranslatableComponent("otm.container.matter_panel.task"))
frame.closeOnEscape = true
object : AbstractSlotPanel<MatterPanelScreen>(this@MatterPanelScreen, frame) {
init {
dock = Dock.LEFT
@ -175,6 +206,8 @@ class MatterPanelScreen(
private fun openPattern(pattern: IPatternState) {
val frame = FramePanel.padded(this, null, 213f, (ButtonPanel.HEIGHT + 3f) * 4f, TranslatableComponent("otm.container.matter_panel.task"))
frame.closeOnEscape = true
val rowTop = EditablePanel(this, frame, height = ButtonPanel.HEIGHT)
val rowInput = EditablePanel(this, frame, height = ButtonPanel.HEIGHT)
val rowBottom = EditablePanel(this, frame, height = ButtonPanel.HEIGHT)

View File

@ -345,11 +345,8 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
fun flash() {
isFlashing = true
if (parent == null) {
popup()
}
}
val isFlashFrame: Boolean
get() = flashingSince != null && flashingSince!!.millis % 400L <= 200L
@ -1338,11 +1335,7 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
final override fun mouseClicked(x: Double, y: Double, button: Int): Boolean {
if (!isVisible() || !acceptMouseInput) return false
if (flashAnyBlocker()) {
return true
}
if (flashAnyBlocker()) return true
if (grabMouseInput) return mouseClickedInner(x, y, button)
for (child in visibleChildrenInternal) {
@ -1366,9 +1359,11 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
if (!isVisible() || !acceptMouseInput) return false
if (isGrabbingMouseInput() || withinBounds(x, y)) {
if (acceptMouseInput && parent == null) popup()
popup()
return mouseClicked(x, y, button)
} else if (withinExtendedBounds(x, y)) {
popup(false)
for (child in visibleChildrenInternal) {
if (child.mouseClickedChecked(x, y, button)) {
killFocusForEverythingExcept(child)
@ -1390,11 +1385,7 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
final override fun mouseReleased(x: Double, y: Double, button: Int): Boolean {
if (!isVisible() || !acceptMouseInput) return false
if (flashAnyBlocker(false)) {
return true
}
if (flashAnyBlocker(false)) return true
if (grabMouseInput) return mouseReleasedInner(x, y, button)
for (child in visibleChildrenInternal) {
@ -1434,11 +1425,7 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
final override fun mouseDragged(x: Double, y: Double, button: Int, xDelta: Double, yDelta: Double): Boolean {
if (!isVisible() || !acceptMouseInput) return false
if (flashAnyBlocker(false)) {
return true
}
if (flashAnyBlocker(false)) return true
if (grabMouseInput) return mouseDraggedInner(x, y, button, xDelta, yDelta)
for (child in visibleChildrenInternal) {
@ -1472,7 +1459,6 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
return false
}
protected open fun mouseScrolledInner(x: Double, y: Double, scroll: Double): Boolean {
return true
}
@ -1537,6 +1523,7 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
}
}
if (focusedAsParent) return keyPressedInternal(key, scancode, mods)
return false
}
@ -1560,6 +1547,7 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
}
}
if (focusedAsParent) return keyReleasedInternal(key, scancode, mods)
return false
}
@ -1641,14 +1629,18 @@ open class EditablePanel<out S : Screen> @JvmOverloads constructor(
isRemoved = true
}
fun popup() {
fun popup(focus: Boolean = true) {
if (isRemoved) {
return
}
if (screen is MatteryScreen<*>) {
if (screen is MatteryScreen<*> && parent == null) {
screen.popup(this as EditablePanel<MatteryScreen<*>>)
}
if (focus && !isEverFocused()) {
requestFocus()
}
}
fun asGrid(): FlexGridPanel<*> {

View File

@ -1,5 +1,6 @@
package ru.dbotthepony.mc.otm.client.screen.panels
import com.mojang.blaze3d.platform.InputConstants
import com.mojang.blaze3d.systems.RenderSystem
import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.client.gui.narration.NarratableEntry
@ -133,6 +134,8 @@ open class FramePanel<out S : Screen>(
protected var dragging = false
var closeOnEscape = false
init {
setDockPadding(PADDING, if (title != null) PADDING_TOP else PADDING, PADDING, PADDING)
}
@ -182,6 +185,15 @@ open class FramePanel<out S : Screen>(
}
override fun keyPressedInternal(key: Int, scancode: Int, mods: Int): Boolean {
if (key == InputConstants.KEY_ESCAPE && closeOnEscape) {
remove()
return true
}
return super.keyPressedInternal(key, scancode, mods)
}
override fun innerRender(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) {
RECTANGLE.render(stack, width = width, height = height)

View File

@ -1,11 +1,14 @@
package ru.dbotthepony.mc.otm.client.screen.panels.button
import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.ChatFormatting
import net.minecraft.client.gui.screens.Screen
import net.minecraft.network.chat.Component
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.render.AbstractMatterySprite
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.core.GetterSetter
import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.mc.otm.core.value
import ru.dbotthepony.mc.otm.menu.input.IPlayerInputWithFeedback
@ -17,9 +20,11 @@ abstract class BooleanRectangleButtonPanel<out S : Screen>(
width: Float,
height: Float,
val prop: GetterSetter<Boolean>,
val skinElementActive: AbstractMatterySprite? = null,
val skinElementInactive: AbstractMatterySprite? = null,
var skinElementActive: AbstractMatterySprite? = null,
var skinElementInactive: AbstractMatterySprite? = null,
val onChange: ((newValue: Boolean) -> Unit)? = null,
var tooltipActive: Component? = null,
var tooltipInactive: Component? = null,
) : RectangleButtonPanel<S>(screen, parent, x, y, width, height, null) {
override fun onClick(mouseButton: Int) {
val newValue = !prop.value
@ -27,6 +32,39 @@ abstract class BooleanRectangleButtonPanel<out S : Screen>(
onChange?.invoke(newValue)
}
override fun innerRenderTooltips(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float): Boolean {
if (isHovered) {
val tooltipActive = tooltipActive
val tooltipInactive = tooltipInactive
val tooltipList = tooltipList
val tooltip = tooltip
if (tooltipActive != null || tooltipInactive != null || tooltipList != null || tooltip != null) {
val tooltips = ArrayList<Component>(2)
if (tooltipList != null) {
tooltips.addAll(tooltipList)
tooltips.add(SPACE)
} else if (tooltip != null) {
tooltips.add(tooltip)
tooltips.add(SPACE)
}
if (tooltipActive != null) {
tooltips.add(tooltipActive.copy().withStyle(if (prop.get()) ChatFormatting.WHITE else ChatFormatting.GRAY))
}
if (tooltipInactive != null) {
tooltips.add(tooltipInactive.copy().withStyle(if (!prop.get()) ChatFormatting.WHITE else ChatFormatting.GRAY))
}
screen.renderComponentTooltip(stack, tooltips, mouseX.toInt(), mouseY.toInt(), font)
}
}
return super.innerRenderTooltips(stack, mouseX, mouseY, partialTick)
}
override var isDisabled: Boolean
get() {
if (prop is IPlayerInputWithFeedback<Boolean>) {
@ -46,4 +84,8 @@ abstract class BooleanRectangleButtonPanel<out S : Screen>(
skinElementInactive?.render(stack, width = width, height = height)
}
}
companion object {
private val SPACE = TextComponent("")
}
}

View File

@ -5,7 +5,6 @@ import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.block.entity.RedstoneSetting
import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.render.UVWindingOrder
import ru.dbotthepony.mc.otm.client.render.Widgets18
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
@ -192,29 +191,42 @@ private fun <S : MatteryScreen<*>> makeEnergyConfigPanel(
return frame
}
fun <S : MatteryScreen<*>> makeDeviceControls(
class DeviceControls<out S : MatteryScreen<*>>(
screen: S,
parent: FramePanel<S>,
redstone: IPlayerInputWithFeedback<RedstoneSetting>? = null,
itemConfig: ItemHandlerPlayerInput? = null,
energyConfig: EnergyPlayerInput? = null,
): EditablePanel<S> {
val panel = object : EditablePanel<S>(screen, parent, width = LargeEnumRectangleButtonPanel.SIZE, height = 0f, x = parent.width + 3f) {
override fun tickInner() {
super.tickInner()
x = parent.width + 3f
y = 0f
}
extra: Iterable<EditablePanel<S>> = listOf(),
val redstoneConfig: IPlayerInputWithFeedback<RedstoneSetting>? = null,
val itemConfig: ItemHandlerPlayerInput? = null,
val energyConfig: EnergyPlayerInput? = null,
) : EditablePanel<S>(screen, parent, x = parent.width + 3f, height = 0f, width = 0f) {
val itemConfigButton: LargeRectangleButtonPanel<S>?
val energyConfigButton: LargeRectangleButtonPanel<S>?
val redstoneControlsButton: LargeEnumRectangleButtonPanel<S, RedstoneSetting>?
private var nextY = 0f
fun <P : EditablePanel<@UnsafeVariance S>> addButton(button: P): P {
button.parent = this
button.x = 0f
button.y = nextY
nextY += button.height + 2f
height = nextY - 2f
width = button.width.coerceAtLeast(width)
return button
}
var y = 0f
init {
for (button in extra) {
addButton(button)
}
if (redstone != null) {
y += makeRedstoneSettingButton(screen, panel, y = y, control = redstone).height + 2f
if (redstoneConfig != null) {
redstoneControlsButton = addButton(makeRedstoneSettingButton(screen, this, control = redstoneConfig))
} else {
redstoneControlsButton = null
}
if (itemConfig != null) {
y += object : LargeRectangleButtonPanel<S>(screen, panel, y = y, skinElement = Widgets18.ITEMS_CONFIGURATION) {
itemConfigButton = addButton(object : LargeRectangleButtonPanel<S>(screen, this@DeviceControls, skinElement = Widgets18.ITEMS_CONFIGURATION) {
init {
tooltip = TranslatableComponent("otm.gui.sides.item_config")
}
@ -227,11 +239,13 @@ fun <S : MatteryScreen<*>> makeDeviceControls(
frame.y = absoluteY + height + 8f
}
}
}.height + 2f
})
} else {
itemConfigButton = null
}
if (energyConfig != null) {
y += object : LargeRectangleButtonPanel<S>(screen, panel, y = y, skinElement = Widgets18.ENERGY_CONFIGURATION) {
energyConfigButton = addButton(object : LargeRectangleButtonPanel<S>(screen, this@DeviceControls, y = nextY, skinElement = Widgets18.ENERGY_CONFIGURATION) {
init {
tooltip = TranslatableComponent("otm.gui.sides.energy_config")
}
@ -244,9 +258,26 @@ fun <S : MatteryScreen<*>> makeDeviceControls(
frame.y = absoluteY + height + 8f
}
}
}.height + 2f
})
} else {
energyConfigButton = null
}
}
panel.height = (y - 2f).coerceAtLeast(0f)
return panel
override fun tickInner() {
super.tickInner()
x = (parent?.width ?: 0f) + 3f
y = 0f
}
}
fun <S : MatteryScreen<*>> makeDeviceControls(
screen: S,
parent: FramePanel<S>,
extra: Iterable<EditablePanel<S>> = listOf(),
redstone: IPlayerInputWithFeedback<RedstoneSetting>? = null,
itemConfig: ItemHandlerPlayerInput? = null,
energyConfig: EnergyPlayerInput? = null,
): DeviceControls<S> {
return DeviceControls(screen, parent, extra = extra, redstoneConfig = redstone, itemConfig = itemConfig, energyConfig = energyConfig)
}

View File

@ -158,12 +158,14 @@ abstract class EnumRectangleButtonPanel<out S : Screen, T : Enum<T>>(
}
val listing = ArrayList<Component>()
val tooltipList = tooltipList
val tooltip = tooltip
if (tooltipList != null) {
listing.addAll(tooltipList!!)
listing.addAll(tooltipList)
listing.add(SPACE)
} else if (tooltip != null) {
listing.add(tooltip!!)
listing.add(tooltip)
listing.add(SPACE)
}

View File

@ -1,6 +1,7 @@
package ru.dbotthepony.mc.otm.client.screen.panels.button
import net.minecraft.client.gui.screens.Screen
import net.minecraft.network.chat.Component
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.render.AbstractMatterySprite
import ru.dbotthepony.mc.otm.client.render.Widgets18
@ -19,7 +20,9 @@ open class LargeBooleanRectangleButtonPanel<out S : Screen>(
skinElementActive: AbstractMatterySprite? = null,
skinElementInactive: AbstractMatterySprite? = null,
onChange: ((newValue: Boolean) -> Unit)? = null,
) : BooleanRectangleButtonPanel<S>(screen, parent, x, y, width, height, prop, skinElementActive, skinElementInactive, onChange) {
tooltipActive: Component? = null,
tooltipInactive: Component? = null,
) : BooleanRectangleButtonPanel<S>(screen, parent, x, y, width, height, prop, skinElementActive, skinElementInactive, onChange, tooltipActive, tooltipInactive) {
final override val IDLE = Widgets18.BUTTON_IDLE
final override val HOVERED = Widgets18.BUTTON_HOVERED
final override val PRESSED = Widgets18.BUTTON_PRESSED

View File

@ -11,7 +11,7 @@ import net.minecraftforge.common.util.INBTSerializable
import net.minecraftforge.network.NetworkEvent
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.core.nbt.ifHas
import ru.dbotthepony.mc.otm.core.nbt.map
import ru.dbotthepony.mc.otm.menu.MatteryMenu
import ru.dbotthepony.mc.otm.network.MatteryPacket
import ru.dbotthepony.mc.otm.core.nbt.set
@ -266,7 +266,7 @@ class ItemFilter(
if (nbt == null)
return
nbt.ifHas("items", ListTag::class.java) {
nbt.map("items") { it: ListTag ->
for ((i, value) in it.withIndex()) {
if (value is CompoundTag) {
filter[i] = ItemStack.of(value)

View File

@ -10,7 +10,7 @@ import kotlin.jvm.JvmOverloads
import net.minecraft.world.entity.player.Player
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraftforge.common.util.INBTSerializable
import ru.dbotthepony.mc.otm.core.nbt.ifHas
import ru.dbotthepony.mc.otm.core.nbt.map
import ru.dbotthepony.mc.otm.core.nbt.set
import java.util.*
@ -53,7 +53,7 @@ open class MatteryContainer(val watcher: Runnable, private val size: Int) : Cont
}
// нам не интересен размер
tag.ifHas("items", ListTag::class.java) {
tag.map("items") { it: ListTag ->
for (i in 0 until it.size.coerceAtMost(size)) {
slots[i] = ItemStack.of(it[i] as CompoundTag)
}

View File

@ -33,7 +33,6 @@ import net.minecraftforge.registries.IForgeRegistry
import ru.dbotthepony.mc.otm.core.math.Vector
import ru.dbotthepony.mc.otm.core.util.readInt
import java.io.InputStream
import java.io.OutputStream
import java.lang.ref.Reference
import java.math.BigInteger
import java.util.Arrays
@ -313,3 +312,72 @@ fun <T> Stream<T>.asIterable(): Iterable<T> {
}
}
}
// Kotlin type safety:
// since Java generics are invariant,
// and can not distinguish between null and non-null type parameters
// we need to tell compiler that Comparator.nullsFirst actually has next signature:
// fun <T> Comparator.nullsFirst(original: Comparator<in T>): Comparator<in T?>
fun <T> Comparator<in T>.nullsFirst(): Comparator<T?> {
return Comparator.nullsFirst(this as Comparator<in T?>)
}
fun <T> Comparator<in T>.nullsLast(): Comparator<T?> {
return Comparator.nullsLast(this as Comparator<in T?>)
}
/**
* Returns applicable index to put [element] into [List] determined by [comparator], optionally specifying ranges as [fromIndex] and [toIndex]
*
* If [List] is not sorted, result of this function is undefined
*/
fun <E> List<E>.searchInsertionIndex(element: E, comparator: Comparator<E>, fromIndex: Int = 0, toIndex: Int = size): Int {
require(toIndex >= fromIndex) { "Invalid range: to $toIndex >= from $fromIndex" }
require(fromIndex >= 0) { "Invalid from index: $fromIndex" }
require(toIndex >= 0) { "Invalid to index: $toIndex" }
require(fromIndex <= size) { "Invalid from index: $fromIndex (list size $size)" }
require(toIndex <= size) { "Invalid to index: $toIndex (list size $size)" }
if (fromIndex == size || fromIndex == toIndex || comparator.compare(element, this[fromIndex]) <= 0) {
return fromIndex
}
if (toIndex - fromIndex <= 10) {
for (i in fromIndex + 1 until toIndex) {
val compare = comparator.compare(element, this[i])
if (compare <= 0) {
return i
}
}
return size
} else {
var lower = fromIndex
var upper = toIndex - 1
while (upper - lower >= 10) {
val middle = (upper + lower) / 2
val compare = comparator.compare(element, this[middle])
if (compare == 0) {
return middle
} else if (compare < 0) {
upper = middle
} else {
lower = middle
}
}
return searchInsertionIndex(element, comparator, lower, upper + 1)
}
}
/**
* Inserts [element] into [MutableList] at index determined by [comparator]
*
* If [MutableList] is not sorted, result of this function is undefined
*/
fun <E> MutableList<E>.addSorted(element: E, comparator: Comparator<E>) {
add(searchInsertionIndex(element, comparator), element)
}

View File

@ -15,6 +15,7 @@ import net.minecraft.nbt.LongArrayTag
import net.minecraft.nbt.LongTag
import net.minecraft.nbt.NbtAccounter
import net.minecraft.nbt.NbtUtils
import net.minecraft.nbt.NumericTag
import net.minecraft.nbt.ShortTag
import net.minecraft.nbt.StringTag
import net.minecraft.nbt.Tag
@ -58,57 +59,13 @@ inline fun <R, reified T : Tag?> CompoundTag.map(key: String, consumer: (T) -> R
return null
}
inline fun <R, reified T : Tag> CompoundTag.mapIf(key: String, consumer: (T) -> R): R? {
val tag = get(key)
if (tag is T) {
return consumer(tag)
}
return null
}
inline fun <reified R : Enum<R>> CompoundTag.getEnum(key: String, ifNothing: () -> R = { R::class.java.enumConstants[0] }): R {
val tag = get(key)
if (tag is StringTag) {
val str = tag.asString
return R::class.java.enumConstants.first { it.name == str }
}
return ifNothing.invoke()
fun <T> CompoundTag.mapString(index: String, mapper: (String) -> T, orElse: T): T {
val tag = this[index] as? StringTag ?: return orElse
return mapper.invoke(tag.asString)
}
fun CompoundTag.getItemStack(key: String): ItemStack = map(key, ItemStack::of) ?: ItemStack.EMPTY
inline fun CompoundTag.ifHas(s: String, consumer: (Tag) -> Unit) {
val tag = get(s)
if (tag != null) {
consumer(tag)
}
}
inline fun CompoundTag.ifHas(s: String, type: Byte, consumer: (Tag) -> Unit) {
val tag = get(s)
if (tag != null && tag.id == type) {
consumer(tag)
}
}
inline fun <reified T : Tag> CompoundTag.ifHas(s: String, type: Class<T>, consumer: (T) -> Unit) {
val tag = get(s)
if (tag != null && tag::class.java === type) {
consumer(tag as T)
}
}
fun CompoundTag.getList(key: String): ListTag {
return this[key] as? ListTag ?: ListTag()
}
@Suppress("unchecked_cast") // type is checked inside getList
fun CompoundTag.getByteList(key: String): MutableList<ByteTag> = getList(key, Tag.TAG_BYTE.toInt()) as MutableList<ByteTag>
@Suppress("unchecked_cast") // type is checked inside getList
@ -153,3 +110,7 @@ fun CompoundTag.getJson(key: String, sizeLimit: NbtAccounter = NbtAccounter.UNLI
return FastByteArrayInputStream(bytes).readJson(sizeLimit)
}
fun CompoundTag.getBoolean(index: String, orElse: Boolean): Boolean {
return (this[index] as? NumericTag)?.asInt?.let { it > 0 } ?: orElse
}

View File

@ -0,0 +1,83 @@
package ru.dbotthepony.mc.otm.core.util
import it.unimi.dsi.fastutil.objects.Reference2IntFunction
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap
import net.minecraft.network.chat.Component
import net.minecraft.world.item.Item
import net.minecraftforge.common.CreativeModeTabRegistry
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.nullsFirst
import ru.dbotthepony.mc.otm.core.nullsLast
import ru.dbotthepony.mc.otm.core.registryName
object CreativeMenuComparator : Comparator<Item> {
override fun compare(o1: Item, o2: Item): Int {
rebuild()
return item2index.getInt(o1).compareTo(item2index.getInt(o2))
}
private val item2index = Reference2IntOpenHashMap<Item>()
init {
item2index.defaultReturnValue(Int.MAX_VALUE)
}
private fun rebuild() {
if (item2index.isEmpty()) {
var i = 0
for (tab in CreativeModeTabRegistry.getSortedCreativeModeTabs()) {
for (item in tab.displayItems) {
item2index.computeIfAbsent(item.item, Reference2IntFunction { i++ })
}
}
}
}
internal fun invalidate() {
item2index.clear()
}
val NullsFirst = nullsFirst()
val NullsLast = nullsLast()
}
object ItemLocalizedNameComparator : Comparator<Item> {
override fun compare(o1: Item, o2: Item): Int {
return o1.description.string.compareTo(o2.description.string)
}
val NullsFirst = nullsFirst()
val NullsLast = nullsLast()
}
object ItemModComparator : Comparator<Item> {
override fun compare(o1: Item, o2: Item): Int {
val a = o1.registryName?.namespace ?: return 0
val b = o2.registryName?.namespace ?: return 0
return a.compareTo(b)
}
val NullsFirst = nullsFirst()
val NullsLast = nullsLast()
}
object ItemIDComparator : Comparator<Item> {
override fun compare(o1: Item, o2: Item): Int {
val a = o1.registryName ?: return 0
val b = o2.registryName ?: return 0
return a.compareTo(b)
}
val NullsFirst = nullsFirst()
val NullsLast = nullsLast()
}
enum class ItemSorter(val comparator: Comparator<Item?>, private val sTitle: Component) {
DEFAULT(CreativeMenuComparator.NullsFirst, TranslatableComponent("otm.gui.sorting.default")),
NAME(ItemLocalizedNameComparator.NullsFirst.thenComparing(CreativeMenuComparator.NullsFirst), TranslatableComponent("otm.gui.sorting.name")),
ID(ItemIDComparator.NullsFirst.thenComparing(CreativeMenuComparator.NullsFirst), TranslatableComponent("otm.gui.sorting.id")),
MOD(ItemModComparator.NullsFirst.thenComparing(CreativeMenuComparator.NullsFirst), TranslatableComponent("otm.gui.sorting.modid"));
val title: Component get() = sTitle.copy()
}

View File

@ -16,7 +16,7 @@ import net.minecraft.world.level.Level
import net.minecraftforge.common.capabilities.Capability
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.capability.matter.*
import ru.dbotthepony.mc.otm.core.nbt.ifHas
import ru.dbotthepony.mc.otm.core.nbt.map
import java.util.*
import java.util.stream.Stream
@ -99,11 +99,9 @@ class PatternStorageItem : Item {
}
override val storedPatterns: Int get() {
stack.tag?.ifHas("otm_patterns", ListTag::class.java) {
return it.size
}
return 0
return stack.tag?.map("otm_patterns") { it: ListTag ->
it.size
} ?: 0
}
override fun <T> getCapability(cap: Capability<T>, side: Direction?): LazyOptional<T> {
@ -111,11 +109,9 @@ class PatternStorageItem : Item {
}
override val patterns: Stream<out IPatternState> get() {
stack.tag?.ifHas("otm_patterns", ListTag::class.java) {
return it.stream().map { PatternState.deserializeNBT(it) }.filter { it != null } as Stream<out IPatternState>
}
return Stream.empty()
return stack.tag?.map("otm_patterns") { it: ListTag ->
it.stream().map { PatternState.deserializeNBT(it) }.filter { it != null } as Stream<out IPatternState>
} ?: Stream.empty()
}
override fun insertPattern(

View File

@ -21,7 +21,7 @@ import net.minecraftforge.event.entity.player.EntityItemPickupEvent
import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.mc.otm.capability.drive.DrivePool
import ru.dbotthepony.mc.otm.container.ItemFilter
import ru.dbotthepony.mc.otm.core.nbt.ifHas
import ru.dbotthepony.mc.otm.core.nbt.map
import java.math.BigInteger
import java.util.*
@ -88,7 +88,7 @@ class PortableCondensationDriveItem(capacity: Int) :
fun getFilterSettings(drive: ItemStack): ItemFilter {
val filter = ItemFilter(MAX_FILTERS)
filter.isWhitelist = true
drive.tag?.ifHas(FILTER_PATH, CompoundTag::class.java, filter::deserializeNBT)
drive.tag?.map(FILTER_PATH, filter::deserializeNBT)
return filter
}

View File

@ -4,21 +4,26 @@ import net.minecraft.network.FriendlyByteBuf
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.Slot
import net.minecraftforge.network.NetworkEvent
import net.minecraftforge.network.PacketDistributor
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.mc.otm.block.entity.matter.MatterPanelBlockEntity
import ru.dbotthepony.mc.otm.capability.matter.*
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.core.GetterSetter
import ru.dbotthepony.mc.otm.core.addSorted
import ru.dbotthepony.mc.otm.core.util.BooleanValueCodec
import ru.dbotthepony.mc.otm.core.util.EnumValueCodec
import ru.dbotthepony.mc.otm.core.util.ItemSorter
import ru.dbotthepony.mc.otm.graph.matter.IMatterGraphListener
import ru.dbotthepony.mc.otm.graph.matter.MatterNetworkGraph
import ru.dbotthepony.mc.otm.menu.MatteryMenu
import ru.dbotthepony.mc.otm.network.*
import ru.dbotthepony.mc.otm.registry.MMenus
import java.util.*
import java.util.function.Consumer
import java.util.function.Supplier
import kotlin.Comparator
import kotlin.collections.ArrayList
class CancelTaskPacket(val id: UUID) : MatteryPacket {
override fun write(buff: FriendlyByteBuf) {
@ -146,66 +151,70 @@ class MatterPanelMenu @JvmOverloads constructor(
sendNetwork(TasksChangePacket(false, listOf(task)))
}
// client code
val sorting: ItemSorter by mSynchronizer.ComputedField(
getter = { tile?.getPlayerSettings(ply)?.sorter ?: ItemSorter.DEFAULT },
codec = EnumValueCodec(ItemSorter::class.java),
observer = {
patterns.sortWith(actualComparator)
tasks.sortWith(actualTaskComparator)
})
val isAscending: Boolean by mSynchronizer.ComputedField(
getter = { tile?.getPlayerSettings(ply)?.ascending ?: true },
codec = BooleanValueCodec,
observer = {
patterns.sortWith(actualComparator)
tasks.sortWith(actualTaskComparator)
})
val changeIsAscending = booleanInput(allowSpectators = true) { tile?.getPlayerSettings(ply)?.ascending = it }
val changeSorting = PlayerInput(EnumValueCodec(ItemSorter::class.java), allowSpectators = true) { tile?.getPlayerSettings(ply)?.sorter = it }
val sortingGS = GetterSetter.of(::sorting, changeSorting::input)
val isAscendingGS = GetterSetter.of(::isAscending, changeIsAscending::input)
private val actualComparator = Comparator<IPatternState> { o1, o2 -> sorting.comparator.compare(o1.item, o2.item) * (if (isAscending) 1 else -1) }
private val actualTaskComparator = Comparator<IReplicationTask<*>> { o1, o2 -> sorting.comparator.compare(o1.item, o2.item) * (if (isAscending) 1 else -1) }
val patterns = ArrayList<IPatternState>()
val tasks = ArrayList<IReplicationTask<*>>()
var changeset = 0
fun networkPatternsUpdated(patterns: Collection<IPatternState>) {
changeset++
for (pattern in patterns) {
val index = this.patterns.indexOfFirst(pattern::matchId)
if (index != -1) {
this.patterns[index] = pattern
} else {
this.patterns.add(pattern)
this.patterns.addSorted(pattern, actualComparator)
}
}
}
fun networkPatternsRemoved(patterns: Collection<IPatternState>) {
changeset++
for (pattern in patterns) {
this.patterns.remove(pattern)
}
}
fun networkTasksUpdated(tasks: Collection<IReplicationTask<*>>) {
changeset++
for (task in tasks) {
val index = this.tasks.indexOfFirst(task::matchId)
if (index != -1) {
this.tasks[index] = task
} else {
this.tasks.add(task)
this.tasks.addSorted(task, actualTaskComparator)
}
updateWatcher?.accept(task)
}
}
fun networkTasksRemoved(tasks: Collection<IReplicationTask<*>>) {
changeset++
for (task in tasks) {
this.tasks.remove(task)
deleteWatcher?.accept(task)
}
}
private var deleteWatcher: Consumer<IReplicationTask<*>>? = null
private var updateWatcher: Consumer<IReplicationTask<*>>? = null
fun networkTaskWatcher(updateWatcher: Consumer<IReplicationTask<*>>, deleteWatcher: Consumer<IReplicationTask<*>>) {
this.deleteWatcher = deleteWatcher
this.updateWatcher = updateWatcher
}
// server code
fun requestReplication(ply: ServerPlayer, id: UUID, count: Int) {
if (ply.isSpectator) {

View File

@ -688,6 +688,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
private val getter: () -> V,
private val codec: IStreamCodec<V>,
name: String = nextFieldName(),
private val observer: (new: V) -> Unit = {}
) : AbstractField<V>(name), IField<V> {
private var remote: V? = null
private var clientValue: V? = null
@ -726,7 +727,9 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
}
override fun read(stream: DataInputStream) {
clientValue = codec.read(stream)
val newValue = codec.read(stream)
clientValue = newValue
observer.invoke(newValue)
}
}

View File

@ -5,6 +5,7 @@ import net.minecraft.world.item.CreativeModeTab
import net.minecraft.world.item.ItemStack
import net.minecraftforge.event.CreativeModeTabEvent
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.core.util.CreativeMenuComparator
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.util.WriteOnce
import ru.dbotthepony.mc.otm.registry.MItems.BATTERY_CREATIVE
@ -16,6 +17,8 @@ object MCreativeTabs {
private set
fun register(event: CreativeModeTabEvent.Register) {
CreativeMenuComparator.invalidate()
MAIN = event.registerCreativeModeTab(ResourceLocation(OverdriveThatMatters.MOD_ID, "main")) {
it.icon { ItemStack(BATTERY_CREATIVE, 1) }
it.title(TranslatableComponent("itemGroup.otm"))

Binary file not shown.

Before

Width:  |  Height:  |  Size: 711 B

After

Width:  |  Height:  |  Size: 803 B

View File

@ -0,0 +1,34 @@
package ru.dbotthepony.mc.otm.tests
import it.unimi.dsi.fastutil.ints.IntComparators
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import ru.dbotthepony.mc.otm.core.addSorted
import java.util.Random
object ComparatorTests {
@Test
@DisplayName("Comparator tests")
fun test() {
val sortedList = mutableListOf(1, 4, 6)
sortedList.addSorted(2, IntComparators.NATURAL_COMPARATOR)
sortedList.addSorted(3, IntComparators.NATURAL_COMPARATOR)
sortedList.addSorted(7, IntComparators.NATURAL_COMPARATOR)
sortedList.addSorted(-1, IntComparators.NATURAL_COMPARATOR)
assertEquals(mutableListOf(-1, 1, 2, 3, 4, 6, 7), sortedList)
val rand = Random()
val sorted2 = ArrayList<Int>()
for (i in 0 .. 100) {
sorted2.addSorted(rand.nextInt(-100, 100), IntComparators.NATURAL_COMPARATOR)
}
val sorted22 = ArrayList(sorted2)
sorted22.sort()
assertEquals(sorted22, sorted2)
}
}