Bring storage system back to functional state
Add ISubscriptable, which is implemented by networked fields and networked inputs Unify storage grid code Add itemstack and itemstoragestack sorters
This commit is contained in:
parent
0754cf55eb
commit
506017f055
@ -213,6 +213,27 @@ private fun misc(provider: MatteryLanguageProvider) {
|
||||
misc("suffix.zepto", "%s z%s")
|
||||
misc("suffix.yocto", "%s y%s")
|
||||
|
||||
misc("suffix_concise.none", "%s")
|
||||
misc("suffix_concise.kilo", "%sk")
|
||||
misc("suffix_concise.mega", "%sM")
|
||||
misc("suffix_concise.giga", "%sG")
|
||||
misc("suffix_concise.tera", "%sT")
|
||||
misc("suffix_concise.peta", "%sP")
|
||||
misc("suffix_concise.exa", "%sE")
|
||||
misc("suffix_concise.zetta", "%sZ")
|
||||
misc("suffix_concise.yotta", "%sY")
|
||||
|
||||
misc("suffix_concise.deci", "%sd")
|
||||
misc("suffix_concise.centi", "%sc")
|
||||
misc("suffix_concise.milli", "%sm")
|
||||
misc("suffix_concise.micro", "%sμ")
|
||||
misc("suffix_concise.nano", "%sn")
|
||||
misc("suffix_concise.pico", "%sp")
|
||||
misc("suffix_concise.femto", "%sf")
|
||||
misc("suffix_concise.atto", "%sa")
|
||||
misc("suffix_concise.zepto", "%sz")
|
||||
misc("suffix_concise.yocto", "%sy")
|
||||
|
||||
misc("suffix_raw.kilo", "k")
|
||||
misc("suffix_raw.mega", "M")
|
||||
misc("suffix_raw.giga", "G")
|
||||
@ -776,7 +797,7 @@ private fun gui(provider: MatteryLanguageProvider) {
|
||||
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.count", "Sort by quantity")
|
||||
gui("sorting.ascending", "Ascending")
|
||||
gui("sorting.descending", "Descending")
|
||||
gui("sorting.matter_value", "Matter value")
|
||||
|
@ -221,6 +221,26 @@ private fun misc(provider: MatteryLanguageProvider) {
|
||||
misc("suffix.zepto", "%s з%s")
|
||||
misc("suffix.yocto", "%s и%s")
|
||||
|
||||
misc("suffix_concise.none", "%s")
|
||||
misc("suffix_concise.kilo", "%sк")
|
||||
misc("suffix_concise.mega", "%sМ")
|
||||
misc("suffix_concise.giga", "%sГ")
|
||||
misc("suffix_concise.tera", "%sТ")
|
||||
misc("suffix_concise.peta", "%sП")
|
||||
misc("suffix_concise.exa", "%sЭ")
|
||||
misc("suffix_concise.zetta", "%sЗ")
|
||||
misc("suffix_concise.yotta", "%sИ")
|
||||
misc("suffix_concise.deci", "%sд")
|
||||
misc("suffix_concise.centi", "%sс")
|
||||
misc("suffix_concise.milli", "%sм")
|
||||
misc("suffix_concise.micro", "%s к")
|
||||
misc("suffix_concise.nano", "%sн")
|
||||
misc("suffix_concise.pico", "%sп")
|
||||
misc("suffix_concise.femto", "%sф")
|
||||
misc("suffix_concise.atto", "%sа")
|
||||
misc("suffix_concise.zepto", "%sз")
|
||||
misc("suffix_concise.yocto", "%sи")
|
||||
|
||||
misc("suffix_raw.kilo", "к")
|
||||
misc("suffix_raw.mega", "М")
|
||||
misc("suffix_raw.giga", "Г")
|
||||
|
@ -142,7 +142,6 @@ public final class OverdriveThatMatters {
|
||||
}
|
||||
|
||||
private void setup(final FMLCommonSetupEvent event) {
|
||||
EVENT_BUS.addListener(EventPriority.LOWEST, DrivePool.INSTANCE::onServerPostTick);
|
||||
EVENT_BUS.addListener(EventPriority.HIGHEST, DrivePool.INSTANCE::serverStopEvent);
|
||||
EVENT_BUS.addListener(EventPriority.LOWEST, DrivePool.INSTANCE::serverStartEvent);
|
||||
EVENT_BUS.addListener(EventPriority.NORMAL, DrivePool.INSTANCE::onWorldSave);
|
||||
|
@ -37,7 +37,6 @@ import net.minecraftforge.event.entity.player.PlayerEvent
|
||||
import net.minecraftforge.event.level.ChunkWatchEvent
|
||||
import net.minecraftforge.event.level.LevelEvent
|
||||
import net.minecraftforge.event.server.ServerStoppingEvent
|
||||
import net.minecraftforge.items.IItemHandler
|
||||
import ru.dbotthepony.mc.otm.SERVER_IS_LIVE
|
||||
import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock
|
||||
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
||||
@ -45,7 +44,6 @@ import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
|
||||
import ru.dbotthepony.mc.otm.capability.isMekanismLoaded
|
||||
import ru.dbotthepony.mc.otm.compat.mekanism.Mattery2MekanismEnergyWrapper
|
||||
import ru.dbotthepony.mc.otm.compat.mekanism.Mekanism2MatteryEnergyWrapper
|
||||
import ru.dbotthepony.mc.otm.core.collect.SupplierList
|
||||
import ru.dbotthepony.mc.otm.core.collect.WeakHashSet
|
||||
import ru.dbotthepony.mc.otm.core.forValidRefs
|
||||
import ru.dbotthepony.mc.otm.core.get
|
||||
|
@ -1,7 +1,6 @@
|
||||
package ru.dbotthepony.mc.otm.block.entity
|
||||
|
||||
import com.google.common.collect.ImmutableSet
|
||||
import mekanism.common.tile.interfaces.IRedstoneControl
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
|
@ -20,7 +20,6 @@ import ru.dbotthepony.mc.otm.config.ItemsConfig
|
||||
import ru.dbotthepony.mc.otm.container.HandlerFilter
|
||||
import ru.dbotthepony.mc.otm.container.MatteryContainer
|
||||
import ru.dbotthepony.mc.otm.container.get
|
||||
import ru.dbotthepony.mc.otm.core.ifPresentK
|
||||
import ru.dbotthepony.mc.otm.core.isNotEmpty
|
||||
import ru.dbotthepony.mc.otm.core.orNull
|
||||
import ru.dbotthepony.mc.otm.core.util.FluidStackValueCodec
|
||||
|
@ -9,17 +9,14 @@ import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraftforge.items.IItemHandler
|
||||
import ru.dbotthepony.mc.otm.block.matter.MatterBottlerBlock
|
||||
import ru.dbotthepony.mc.otm.block.entity.MatteryPoweredBlockEntity
|
||||
import ru.dbotthepony.mc.otm.block.entity.WorkerState
|
||||
import ru.dbotthepony.mc.otm.capability.CombinedItemHandler
|
||||
import ru.dbotthepony.mc.otm.capability.FlowDirection
|
||||
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
||||
import ru.dbotthepony.mc.otm.capability.ProxiedItemHandler
|
||||
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
|
||||
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
|
||||
import ru.dbotthepony.mc.otm.capability.matter.IMatterStorage
|
||||
import ru.dbotthepony.mc.otm.capability.matter.MatterStorageImpl
|
||||
import ru.dbotthepony.mc.otm.capability.matter.ProfiledMatterStorage
|
||||
import ru.dbotthepony.mc.otm.config.MachinesConfig
|
||||
|
@ -17,11 +17,12 @@ import ru.dbotthepony.mc.otm.container.MatteryContainer
|
||||
import ru.dbotthepony.mc.otm.menu.storage.DriveRackMenu
|
||||
import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
||||
import ru.dbotthepony.mc.otm.storage.*
|
||||
import ru.dbotthepony.mc.otm.storage.powered.PoweredComponent
|
||||
import ru.dbotthepony.mc.otm.storage.powered.PoweredVirtualComponent
|
||||
|
||||
class DriveRackBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
|
||||
MatteryPoweredBlockEntity(MBlockEntities.DRIVE_RACK, p_155229_, p_155230_) {
|
||||
class DriveRackBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryPoweredBlockEntity(MBlockEntities.DRIVE_RACK, blockPos, blockState) {
|
||||
val energy = WorkerEnergyStorage(this::setChangedLight, MachinesConfig.DRIVE_RACK)
|
||||
val cell = StorageNode(energy)
|
||||
|
||||
val container: MatteryContainer = object : MatteryContainer(this::setChanged, 4) {
|
||||
override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) {
|
||||
@ -31,21 +32,15 @@ class DriveRackBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
|
||||
// but since we don't know generics of upvalue mattery drive, its storage type
|
||||
// is defined as out variant
|
||||
old.getCapability(MatteryCapability.DRIVE).ifPresent {
|
||||
cell.computeIfAbsent(it.storageType as StorageStack.Type<ItemStorageStack>) {
|
||||
PoweredVirtualComponent(VirtualComponent(it), ::energy)
|
||||
}.remove(it as IMatteryDrive<ItemStorageStack>)
|
||||
cell.removeStorageComponent(PoweredComponent(it, ::energy))
|
||||
}
|
||||
|
||||
new.getCapability(MatteryCapability.DRIVE).ifPresent {
|
||||
cell.computeIfAbsent(it.storageType as StorageStack.Type<ItemStorageStack>) {
|
||||
PoweredVirtualComponent(VirtualComponent(it), ::energy)
|
||||
}.add(it as IMatteryDrive<ItemStorageStack>)
|
||||
cell.addStorageComponent(PoweredComponent(it, ::energy))
|
||||
}
|
||||
}
|
||||
}.also(::addDroppableContainer)
|
||||
|
||||
val cell = StorageNode(energy)
|
||||
|
||||
init {
|
||||
savetable(::energy, ENERGY_KEY)
|
||||
savetable(::container, INVENTORY_KEY)
|
||||
|
@ -1,5 +1,7 @@
|
||||
package ru.dbotthepony.mc.otm.block.entity.storage
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.entity.player.Inventory
|
||||
@ -8,71 +10,88 @@ import net.minecraft.world.inventory.AbstractContainerMenu
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import ru.dbotthepony.mc.otm.OverdriveThatMatters
|
||||
import ru.dbotthepony.mc.otm.block.entity.MatteryPoweredBlockEntity
|
||||
import ru.dbotthepony.mc.otm.block.storage.DriveViewerBlock
|
||||
import ru.dbotthepony.mc.otm.block.entity.WorkerState
|
||||
import ru.dbotthepony.mc.otm.block.storage.DriveViewerBlock
|
||||
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
||||
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
|
||||
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
|
||||
import ru.dbotthepony.mc.otm.config.MachinesConfig
|
||||
import ru.dbotthepony.mc.otm.container.MatteryContainer
|
||||
import ru.dbotthepony.mc.otm.core.get
|
||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||
import ru.dbotthepony.mc.otm.core.util.ItemStorageStackSorter
|
||||
import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
||||
import ru.dbotthepony.mc.otm.menu.storage.DriveViewerMenu
|
||||
import ru.dbotthepony.mc.otm.storage.StorageStack
|
||||
import java.util.UUID
|
||||
|
||||
class DriveViewerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : MatteryPoweredBlockEntity(MBlockEntities.DRIVE_VIEWER, p_155229_, p_155230_) {
|
||||
override fun setChanged() {
|
||||
super.setChanged()
|
||||
class DriveViewerBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryPoweredBlockEntity(MBlockEntities.DRIVE_VIEWER, blockPos, blockState) {
|
||||
val energy = ProfiledEnergyStorage(WorkerEnergyStorage(this::energyUpdated, MachinesConfig.DRIVE_VIEWER))
|
||||
val energyConfig = ConfigurableEnergy(energy)
|
||||
|
||||
val level = level
|
||||
if (level is ServerLevel)
|
||||
tickList.once {
|
||||
var state = blockState
|
||||
|
||||
if (container.getItem(0).getCapability(MatteryCapability.DRIVE).isPresent) {
|
||||
state = state.setValue(WorkerState.SEMI_WORKER_STATE, WorkerState.WORKING)
|
||||
} else {
|
||||
state = state.setValue(WorkerState.SEMI_WORKER_STATE, WorkerState.IDLE)
|
||||
}
|
||||
|
||||
if (state !== blockState) {
|
||||
level.setBlock(blockPos, state, Block.UPDATE_CLIENTS)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val energy = WorkerEnergyStorage(this::setChangedLight, MachinesConfig.DRIVE_VIEWER)
|
||||
|
||||
val container: MatteryContainer = object : MatteryContainer(this::setChanged, 1) {
|
||||
val container: MatteryContainer = object : MatteryContainer(this::setChangedLight, 1) {
|
||||
override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) {
|
||||
super.setChanged(slot, new, old)
|
||||
|
||||
val level = level
|
||||
|
||||
if (level is ServerLevel)
|
||||
if (level is ServerLevel) {
|
||||
tickList.once {
|
||||
if (!isRemoved) {
|
||||
var state = blockState
|
||||
val isPresent = new.getCapability(MatteryCapability.DRIVE).isPresent
|
||||
var state = this@DriveViewerBlockEntity.blockState.setValue(DriveViewerBlock.DRIVE_PRESENT, isPresent)
|
||||
|
||||
if (new.getCapability(MatteryCapability.DRIVE).isPresent) {
|
||||
state = state.setValue(DriveViewerBlock.DRIVE_PRESENT, true)
|
||||
} else {
|
||||
state = state.setValue(DriveViewerBlock.DRIVE_PRESENT, false)
|
||||
if (!isPresent) {
|
||||
state = state.setValue(WorkerState.SEMI_WORKER_STATE, WorkerState.IDLE)
|
||||
} else if (energy.batteryLevel >= Decimal.TEN && !redstoneControl.isBlockedByRedstone) {
|
||||
state = state.setValue(WorkerState.SEMI_WORKER_STATE, WorkerState.WORKING)
|
||||
}
|
||||
|
||||
if (state !== blockState) {
|
||||
level.setBlock(blockPos, state, Block.UPDATE_CLIENTS)
|
||||
if (state !== this@DriveViewerBlockEntity.blockState) {
|
||||
level.setBlock(this@DriveViewerBlockEntity.blockPos, state, Block.UPDATE_CLIENTS)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}.also(::addDroppableContainer)
|
||||
|
||||
interface ISettings {
|
||||
var sorting: ItemStorageStackSorter
|
||||
var isAscending: Boolean
|
||||
}
|
||||
|
||||
inner class Settings : ISettings {
|
||||
override var sorting: ItemStorageStackSorter = ItemStorageStackSorter.DEFAULT
|
||||
set(value) {
|
||||
field = value
|
||||
setChangedLight()
|
||||
}
|
||||
|
||||
override var isAscending: Boolean = true
|
||||
set(value) {
|
||||
field = value
|
||||
setChangedLight()
|
||||
}
|
||||
}
|
||||
|
||||
val settings = Object2ObjectOpenHashMap<UUID, Settings>()
|
||||
|
||||
fun getSettingsFor(player: Player): ISettings {
|
||||
return settings.computeIfAbsent(player.uuid, Object2ObjectFunction { Settings() })
|
||||
}
|
||||
|
||||
private fun energyUpdated() {
|
||||
val state = blockState.setValue(WorkerState.SEMI_WORKER_STATE, if (energy.batteryLevel >= Decimal.TEN && !redstoneControl.isBlockedByRedstone && blockState[DriveViewerBlock.DRIVE_PRESENT]) WorkerState.WORKING else WorkerState.IDLE)
|
||||
|
||||
if (state != blockState) {
|
||||
level?.setBlock(blockPos, state, Block.UPDATE_CLIENTS)
|
||||
} else {
|
||||
setChangedLight()
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
savetable(::energy, ENERGY_KEY)
|
||||
savetable(::container, INVENTORY_KEY)
|
||||
|
||||
exposeEnergyGlobally(energy)
|
||||
}
|
||||
|
||||
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {
|
||||
|
@ -1,10 +1,10 @@
|
||||
package ru.dbotthepony.mc.otm.block.entity.storage
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.NonNullList
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.Container
|
||||
@ -19,19 +19,18 @@ import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraftforge.common.ForgeHooks
|
||||
import net.minecraftforge.common.util.INBTSerializable
|
||||
import net.minecraftforge.network.NetworkEvent
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import ru.dbotthepony.mc.otm.core.TranslatableComponent
|
||||
import ru.dbotthepony.mc.otm.block.entity.MatteryPoweredBlockEntity
|
||||
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
||||
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
|
||||
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
|
||||
import ru.dbotthepony.mc.otm.client.minecraft
|
||||
import ru.dbotthepony.mc.otm.capability.matteryPlayer
|
||||
import ru.dbotthepony.mc.otm.client.render.IGUIRenderable
|
||||
import ru.dbotthepony.mc.otm.client.render.UVWindingOrder
|
||||
import ru.dbotthepony.mc.otm.config.MachinesConfig
|
||||
import ru.dbotthepony.mc.otm.container.MatteryContainer
|
||||
import ru.dbotthepony.mc.otm.container.get
|
||||
import ru.dbotthepony.mc.otm.graph.storage.StorageNode
|
||||
import ru.dbotthepony.mc.otm.graph.storage.StorageGraph
|
||||
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.mapString
|
||||
@ -41,144 +40,143 @@ import ru.dbotthepony.mc.otm.storage.*
|
||||
import ru.dbotthepony.mc.otm.storage.powered.PoweredVirtualComponent
|
||||
import java.math.BigInteger
|
||||
import java.util.*
|
||||
import java.util.function.Supplier
|
||||
import kotlin.collections.HashMap
|
||||
import ru.dbotthepony.mc.otm.client.render.Widgets8
|
||||
import ru.dbotthepony.mc.otm.container.CombinedContainer
|
||||
import ru.dbotthepony.mc.otm.container.addItem
|
||||
import ru.dbotthepony.mc.otm.container.fullIterator
|
||||
import ru.dbotthepony.mc.otm.container.iterator
|
||||
import ru.dbotthepony.mc.otm.core.collect.map
|
||||
import ru.dbotthepony.mc.otm.core.collect.toList
|
||||
import ru.dbotthepony.mc.otm.core.isNotEmpty
|
||||
import ru.dbotthepony.mc.otm.core.util.ItemStorageStackSorter
|
||||
|
||||
class ItemMonitorPlayerSettings : INBTSerializable<CompoundTag>, MatteryPacket {
|
||||
enum class IngredientPriority(val component: Component) {
|
||||
// Refill everything from system
|
||||
SYSTEM(TranslatableComponent("otm.gui.item_monitor.refill_source.system")),
|
||||
interface IItemMonitorPlayerSettings {
|
||||
var ingredientPriority: ItemMonitorPlayerSettings.IngredientPriority
|
||||
var resultTarget: ItemMonitorPlayerSettings.ResultTarget
|
||||
var craftingAmount: ItemMonitorPlayerSettings.Amount
|
||||
var sorting: ItemStorageStackSorter
|
||||
var ascendingSort: Boolean
|
||||
}
|
||||
|
||||
// Refill everything from player's inventory
|
||||
INVENTORY(TranslatableComponent("otm.gui.item_monitor.refill_source.inventory")),
|
||||
private fun takeOne(inventory: CombinedContainer?, item: ItemStack): Boolean {
|
||||
val iterator = inventory?.optimizedIterator() ?: return false
|
||||
|
||||
// Refill everything from system, if can't refill from player's inventory
|
||||
SYSTEM_FIRST(TranslatableComponent("otm.gui.item_monitor.refill_source.system_first")),
|
||||
|
||||
// Refill everything from player's inventory, if can't refill from system
|
||||
INVENTORY_FIRST(TranslatableComponent("otm.gui.item_monitor.refill_source.inventory_first")),
|
||||
|
||||
// Do not refill (?)
|
||||
DO_NOT(TranslatableComponent("otm.gui.item_monitor.refill_source.do_not")),
|
||||
for (stack in iterator) {
|
||||
if (stack.equals(item, false)) {
|
||||
stack.shrink(1)
|
||||
inventory.setChanged()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
enum class ResultTarget(val component: Component) {
|
||||
return false
|
||||
}
|
||||
|
||||
private fun takeOne(id: UUID?, view: IStorageProvider<ItemStorageStack>?): Boolean {
|
||||
return view?.extractStack(id ?: return false, BigInteger.ONE, false)?.isNotEmpty == true
|
||||
}
|
||||
|
||||
class ItemMonitorPlayerSettings : INBTSerializable<CompoundTag>, IItemMonitorPlayerSettings {
|
||||
interface Setting {
|
||||
val component: Component
|
||||
val icon: IGUIRenderable
|
||||
val winding: UVWindingOrder
|
||||
}
|
||||
|
||||
enum class IngredientPriority(override val component: Component, icon: Lazy<IGUIRenderable>, override val winding: UVWindingOrder = UVWindingOrder.NORMAL) : Setting {
|
||||
// Refill everything from system
|
||||
SYSTEM(TranslatableComponent("otm.gui.item_monitor.refill_source.system"), lazy { Widgets8.WHITE_ARROW_DOWN }, UVWindingOrder.FLIP) {
|
||||
override fun takeOne(item: ItemStack, view: IStorageProvider<ItemStorageStack>?, inventory: CombinedContainer?, id: UUID?): Boolean {
|
||||
return takeOne(id, view)
|
||||
}
|
||||
},
|
||||
|
||||
// Refill everything from player's inventory
|
||||
INVENTORY(TranslatableComponent("otm.gui.item_monitor.refill_source.inventory"), lazy { Widgets8.WHITE_ARROW_DOWN }) {
|
||||
override fun takeOne(item: ItemStack, view: IStorageProvider<ItemStorageStack>?, inventory: CombinedContainer?, id: UUID?): Boolean {
|
||||
return takeOne(inventory, item)
|
||||
}
|
||||
},
|
||||
|
||||
// Refill everything from system, if can't refill from player's inventory
|
||||
SYSTEM_FIRST(TranslatableComponent("otm.gui.item_monitor.refill_source.system_first"), lazy { Widgets8.ARROW_SIDEWAYS }, UVWindingOrder.FLIP) {
|
||||
override fun takeOne(item: ItemStack, view: IStorageProvider<ItemStorageStack>?, inventory: CombinedContainer?, id: UUID?): Boolean {
|
||||
return takeOne(id, view) || takeOne(inventory, item)
|
||||
}
|
||||
},
|
||||
|
||||
// Refill everything from player's inventory, if can't refill from system
|
||||
INVENTORY_FIRST(TranslatableComponent("otm.gui.item_monitor.refill_source.inventory_first"), lazy { Widgets8.ARROW_SIDEWAYS }) {
|
||||
override fun takeOne(item: ItemStack, view: IStorageProvider<ItemStorageStack>?, inventory: CombinedContainer?, id: UUID?): Boolean {
|
||||
return takeOne(inventory, item) || takeOne(id, view)
|
||||
}
|
||||
},
|
||||
|
||||
// Do not refill (?)
|
||||
DO_NOT(TranslatableComponent("otm.gui.item_monitor.refill_source.do_not"), lazy { Widgets8.MINUS }) {
|
||||
override fun takeOne(item: ItemStack, view: IStorageProvider<ItemStorageStack>?, inventory: CombinedContainer?, id: UUID?): Boolean {
|
||||
return false
|
||||
}
|
||||
};
|
||||
|
||||
override val icon: IGUIRenderable by icon
|
||||
|
||||
abstract fun takeOne(item: ItemStack, view: IStorageProvider<ItemStorageStack>?, inventory: CombinedContainer?, id: UUID?): Boolean
|
||||
}
|
||||
|
||||
enum class ResultTarget(override val component: Component, icon: Lazy<IGUIRenderable>, override val winding: UVWindingOrder = UVWindingOrder.NORMAL) : Setting {
|
||||
// Everything goes into storage system
|
||||
ALL_SYSTEM(TranslatableComponent("otm.gui.item_monitor.result_target.all_system")),
|
||||
ALL_SYSTEM(TranslatableComponent("otm.gui.item_monitor.result_target.all_system"), lazy { Widgets8.ARROW_PAINTED_UP }),
|
||||
|
||||
// Result goes to player inventory
|
||||
// Crafting remainder goes to storage system
|
||||
MIXED(TranslatableComponent("otm.gui.item_monitor.result_target.mixed")),
|
||||
MIXED(TranslatableComponent("otm.gui.item_monitor.result_target.mixed"), lazy { Widgets8.ARROW_SIDEWAYS }),
|
||||
|
||||
// Everything goes into player inventory
|
||||
ALL_INVENTORY(TranslatableComponent("otm.gui.item_monitor.result_target.all_inventory")),
|
||||
ALL_INVENTORY(TranslatableComponent("otm.gui.item_monitor.result_target.all_inventory"), lazy { Widgets8.ARROW_PAINTED_UP }, UVWindingOrder.FLIP);
|
||||
|
||||
override val icon: IGUIRenderable by icon
|
||||
}
|
||||
|
||||
enum class Amount(val component: Component) {
|
||||
ONE(TranslatableComponent("otm.gui.item_monitor.amount.one")),
|
||||
STACK(TranslatableComponent("otm.gui.item_monitor.amount.stack")),
|
||||
FULL(TranslatableComponent("otm.gui.item_monitor.amount.full"))
|
||||
enum class Amount(override val component: Component, icon: Lazy<IGUIRenderable>, override val winding: UVWindingOrder = UVWindingOrder.NORMAL) : Setting {
|
||||
ONE(TranslatableComponent("otm.gui.item_monitor.amount.one"), lazy { Widgets8.ONE }),
|
||||
STACK(TranslatableComponent("otm.gui.item_monitor.amount.stack"), lazy { Widgets8.S }),
|
||||
FULL(TranslatableComponent("otm.gui.item_monitor.amount.full"), lazy { Widgets8.F });
|
||||
|
||||
override val icon: IGUIRenderable by icon
|
||||
}
|
||||
|
||||
var ingredientPriority = IngredientPriority.SYSTEM
|
||||
var resultTarget = ResultTarget.MIXED
|
||||
var craftingAmount = Amount.STACK
|
||||
override var ingredientPriority = IngredientPriority.SYSTEM
|
||||
override var resultTarget = ResultTarget.MIXED
|
||||
override var craftingAmount = Amount.STACK
|
||||
override var sorting: ItemStorageStackSorter = ItemStorageStackSorter.DEFAULT
|
||||
override var ascendingSort: Boolean = false
|
||||
|
||||
override fun serializeNBT(): CompoundTag {
|
||||
return CompoundTag().also {
|
||||
it[INGREDIENT_PRIORITY_KEY] = ingredientPriority.name
|
||||
it[RESULT_TARGET_KEY] = resultTarget.name
|
||||
it[QUICK_CRAFT_AMOUNT_KEY] = craftingAmount.name
|
||||
it["ingredientPriority"] = ingredientPriority.name
|
||||
it["resultTarget"] = resultTarget.name
|
||||
it["quickCraftAmount"] = craftingAmount.name
|
||||
it["sorting"] = sorting.name
|
||||
it["ascendingSort"] = ascendingSort
|
||||
}
|
||||
}
|
||||
|
||||
override fun deserializeNBT(nbt: CompoundTag) {
|
||||
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)
|
||||
ingredientPriority = nbt.mapString("ingredientPriority", IngredientPriority::valueOf, IngredientPriority.SYSTEM)
|
||||
resultTarget = nbt.mapString("resultTarget", ResultTarget::valueOf, ResultTarget.MIXED)
|
||||
craftingAmount = nbt.mapString("quickCraftAmount", Amount::valueOf, Amount.STACK)
|
||||
sorting = nbt.mapString("sorting", ItemStorageStackSorter::valueOf, ItemStorageStackSorter.DEFAULT)
|
||||
ascendingSort = nbt.getBoolean("ascendingSort")
|
||||
}
|
||||
|
||||
fun read(buff: FriendlyByteBuf) {
|
||||
ingredientPriority = buff.readEnum(IngredientPriority::class.java)
|
||||
resultTarget = buff.readEnum(ResultTarget::class.java)
|
||||
craftingAmount = buff.readEnum(Amount::class.java)
|
||||
}
|
||||
|
||||
fun read(other: ItemMonitorPlayerSettings) {
|
||||
ingredientPriority = other.ingredientPriority
|
||||
resultTarget = other.resultTarget
|
||||
craftingAmount = other.craftingAmount
|
||||
}
|
||||
|
||||
override fun write(buff: FriendlyByteBuf) {
|
||||
buff.writeEnum(ingredientPriority)
|
||||
buff.writeEnum(resultTarget)
|
||||
buff.writeEnum(craftingAmount)
|
||||
}
|
||||
|
||||
private fun playClient() {
|
||||
val ply = minecraft.player ?: throw IllegalStateException("Player is missing")
|
||||
val container = ply.containerMenu as? ItemMonitorMenu ?: return LOGGER.error("Received ItemMonitorPlayerSettings but container is missing or not of right type ({})!", ply.containerMenu)
|
||||
|
||||
container.settings.read(this)
|
||||
}
|
||||
|
||||
// spectators are allowed because they change their own setting which is invisible to everyone else
|
||||
private fun playServer(ply: ServerPlayer) {
|
||||
val container = ply.containerMenu as? ItemMonitorMenu ?: return LOGGER.error("Received ItemMonitorPlayerSettings from {} but container is missing or not of right type ({})!", ply, ply.containerMenu)
|
||||
container.settings.read(this)
|
||||
(container.tile as ItemMonitorBlockEntity).setChangedLight()
|
||||
}
|
||||
|
||||
override fun play(context: Supplier<NetworkEvent.Context>) {
|
||||
context.get().packetHandled = true
|
||||
val ply = context.get().sender
|
||||
|
||||
if (ply != null) {
|
||||
playServer(ply)
|
||||
} else {
|
||||
playClient()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun read(buff: FriendlyByteBuf): ItemMonitorPlayerSettings {
|
||||
return ItemMonitorPlayerSettings().also { it.read(buff) }
|
||||
}
|
||||
|
||||
private val LOGGER = LogManager.getLogger()
|
||||
const val INGREDIENT_PRIORITY_KEY = "ingredientPriority"
|
||||
const val RESULT_TARGET_KEY = "resultTarget"
|
||||
const val QUICK_CRAFT_AMOUNT_KEY = "quickCraftAmount"
|
||||
}
|
||||
}
|
||||
|
||||
private fun takeOne(inventory: Inventory, item: ItemStack): Boolean {
|
||||
for (i in 0 until inventory.containerSize) {
|
||||
if (!inventory[i].isEmpty && ItemStack.isSameItemSameTags(inventory[i], item)) {
|
||||
inventory.removeItem(i, 1)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun takeOne(id: UUID?, view: IStorageProvider<ItemStorageStack>): Boolean {
|
||||
val extracted = view.extractStack(id ?: return false, BigInteger.ONE, false)
|
||||
|
||||
if (!extracted.isEmpty) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
class ItemMonitorBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryPoweredBlockEntity(MBlockEntities.ITEM_MONITOR, blockPos, blockState), IStorageEventConsumer<ItemStorageStack> {
|
||||
val energy = WorkerEnergyStorage(this::setChangedLight, MachinesConfig.ITEM_MONITOR)
|
||||
val energy = ProfiledEnergyStorage(WorkerEnergyStorage(this::setChangedLight, MachinesConfig.ITEM_MONITOR))
|
||||
val energyConfig = ConfigurableEnergy(energy)
|
||||
|
||||
init {
|
||||
exposeEnergyGlobally(energy)
|
||||
savetable(::energy, ENERGY_KEY)
|
||||
}
|
||||
|
||||
@ -203,40 +201,42 @@ class ItemMonitorBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matte
|
||||
}
|
||||
|
||||
private val settings = HashMap<UUID, ItemMonitorPlayerSettings>()
|
||||
private val craftingAmount = Object2ObjectArrayMap<Player, Int>()
|
||||
private val craftingAmount = Object2IntOpenHashMap<Player>()
|
||||
private val lastCraftingRecipe = Object2ObjectArrayMap<Player, CraftingRecipe>()
|
||||
private var inProcessOfCraft = false
|
||||
private val craftingGridTuples = arrayOfNulls<UUID>(3 * 3)
|
||||
|
||||
fun howMuchPlayerCrafted(ply: Player): Int = craftingAmount[ply] ?: 0
|
||||
fun howMuchPlayerCrafted(ply: Player): Int = craftingAmount.getInt(ply)
|
||||
fun lastCraftingRecipe(ply: Player) = lastCraftingRecipe[ply]
|
||||
|
||||
// a lot of code is hardcoded to take CraftingContainer as it's input
|
||||
// hence we are forced to work around this by providing proxy container
|
||||
val craftingGrid = object : MatteryContainer(::setChangedLight, 3 * 3) {
|
||||
val craftingGrid = object : MatteryContainer(3 * 3) {
|
||||
override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) {
|
||||
super.setChanged(slot, new, old)
|
||||
craftingGridDummy[slot] = new
|
||||
setChangedLight()
|
||||
craftingGridVanilla[slot] = new
|
||||
|
||||
if (!inProcessOfCraft)
|
||||
scanCraftingGrid()
|
||||
if (!inProcessOfCraft) {
|
||||
scanCraftingGrid(false)
|
||||
}
|
||||
}
|
||||
}.also(::addDroppableContainer)
|
||||
|
||||
private var inProcessOfCraft = false
|
||||
private val craftingGridVanilla = TransientCraftingContainer(object : AbstractContainerMenu(null, Int.MIN_VALUE) {
|
||||
override fun stillValid(p_38874_: Player) = true
|
||||
override fun quickMoveStack(p_38941_: Player, p_38942_: Int) = ItemStack.EMPTY
|
||||
}, 3, 3)
|
||||
|
||||
private fun scanCraftingGrid() {
|
||||
val server = level?.server ?: return
|
||||
val oldRecipe = craftingRecipe
|
||||
private fun scanCraftingGrid(justCheckForRecipeChange: Boolean): Boolean {
|
||||
val level = level ?: return false
|
||||
val server = level.server ?: return false
|
||||
|
||||
if (oldRecipe != null && oldRecipe.matches(craftingGridDummy, level!!)) {
|
||||
return
|
||||
}
|
||||
var craftingRecipe = craftingRecipe
|
||||
if (craftingRecipe != null && craftingRecipe.matches(craftingGridVanilla, level)) return true
|
||||
if (justCheckForRecipeChange) return false
|
||||
|
||||
craftingRecipe = server.recipeManager.getRecipeFor(RecipeType.CRAFTING, craftingGridDummy, level!!).orElse(null)
|
||||
val craftingRecipe = craftingRecipe
|
||||
|
||||
if (oldRecipe != craftingRecipe) {
|
||||
craftingRecipe = server.recipeManager.getRecipeFor(RecipeType.CRAFTING, craftingGridVanilla, level).orElse(null)
|
||||
Arrays.fill(craftingGridTuples, null)
|
||||
|
||||
val poweredView = poweredView
|
||||
|
||||
if (craftingRecipe != null && poweredView != null) {
|
||||
@ -248,34 +248,27 @@ class ItemMonitorBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matte
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val craftingGridTuples = arrayOfNulls<UUID>(3 * 3)
|
||||
|
||||
private val craftingMenu = object : AbstractContainerMenu(null, Int.MIN_VALUE) {
|
||||
override fun stillValid(p_38874_: Player): Boolean {
|
||||
return true
|
||||
this.craftingRecipe = craftingRecipe
|
||||
return false
|
||||
}
|
||||
|
||||
override fun quickMoveStack(p_38941_: Player, p_38942_: Int): ItemStack {
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
}
|
||||
|
||||
private val craftingGridDummy = TransientCraftingContainer(craftingMenu, 3, 3)
|
||||
|
||||
override val storageType: StorageStack.Type<ItemStorageStack>
|
||||
get() = StorageStack.ITEMS
|
||||
|
||||
override fun onStackAdded(stack: ItemStorageStack, id: UUID, provider: IStorageProvider<ItemStorageStack>) {
|
||||
// no op
|
||||
}
|
||||
|
||||
override fun onStackChanged(stack: ItemStorageStack, id: UUID) {
|
||||
// no op
|
||||
if (craftingRecipe != null) {
|
||||
for (i in craftingGridTuples.indices) {
|
||||
val item = craftingGrid[i]
|
||||
|
||||
if (!item.isEmpty && stack.equalsWithoutCount(ItemStorageStack.unsafe(item))) {
|
||||
craftingGridTuples[i] = id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStackChanged(stack: ItemStorageStack, id: UUID) {}
|
||||
override fun onStackRemoved(id: UUID) {
|
||||
for (i in craftingGridTuples.indices) {
|
||||
if (craftingGridTuples[i] == id) {
|
||||
@ -300,137 +293,112 @@ class ItemMonitorBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matte
|
||||
|
||||
override fun getContainerSize() = 1
|
||||
override fun isEmpty() = craftingRecipe == null
|
||||
override fun stillValid(p_18946_: Player) = true
|
||||
override fun stillValid(player: Player) = true
|
||||
|
||||
override fun getItem(p_18941_: Int): ItemStack {
|
||||
require(p_18941_ == 0) { "Invalid slot ID: $p_18941_" }
|
||||
override fun getItem(slot: Int): ItemStack {
|
||||
require(slot == 0) { "Invalid slot: $slot" }
|
||||
return craftingRecipe?.getResultItem(level?.registryAccess() ?: return ItemStack.EMPTY)?.copy() ?: ItemStack.EMPTY
|
||||
}
|
||||
|
||||
override fun removeItem(index: Int, amount: Int): ItemStack {
|
||||
require(index == 0) { "Invalid index $index" }
|
||||
|
||||
val craftingRecipe = craftingRecipe
|
||||
val craftingPlayer = craftingPlayer
|
||||
val level = level ?: return ItemStack.EMPTY
|
||||
val craftingRecipe = craftingRecipe ?: return ItemStack.EMPTY
|
||||
val craftingPlayer = craftingPlayer ?: return ItemStack.EMPTY
|
||||
|
||||
if (craftingRecipe == null || craftingPlayer == null || craftingRecipe.getResultItem(level?.registryAccess() ?: return ItemStack.EMPTY).count != amount) {
|
||||
try {
|
||||
ForgeHooks.setCraftingPlayer(craftingPlayer)
|
||||
|
||||
if (craftingRecipe.getResultItem(level.registryAccess()).count != amount) {
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
} finally {
|
||||
ForgeHooks.setCraftingPlayer(null)
|
||||
}
|
||||
|
||||
var crafts = craftingAmount[craftingPlayer] ?: 0
|
||||
crafts++
|
||||
craftingAmount[craftingPlayer] = crafts
|
||||
craftingAmount[craftingPlayer] = craftingAmount.getInt(craftingPlayer) + 1
|
||||
lastCraftingRecipe[craftingPlayer] = craftingRecipe
|
||||
|
||||
tickList.namedTimer(craftingPlayer, 4) {
|
||||
craftingAmount.removeInt(craftingPlayer)
|
||||
lastCraftingRecipe.remove(craftingPlayer)
|
||||
}
|
||||
|
||||
val settings = getSettings(craftingPlayer)
|
||||
|
||||
inProcessOfCraft = true
|
||||
|
||||
try {
|
||||
ForgeHooks.setCraftingPlayer(craftingPlayer)
|
||||
val remainingItems: NonNullList<ItemStack> = craftingRecipe.getRemainingItems(craftingGridDummy)
|
||||
val residue: NonNullList<ItemStack>
|
||||
val result: ItemStack
|
||||
|
||||
try {
|
||||
residue = craftingRecipe.getRemainingItems(craftingGridVanilla)
|
||||
result = craftingRecipe.getResultItem(level.registryAccess())
|
||||
} finally {
|
||||
ForgeHooks.setCraftingPlayer(null)
|
||||
}
|
||||
|
||||
check(remainingItems.size == craftingGrid.containerSize) { "${remainingItems.size} != ${craftingGrid.containerSize} !!!" }
|
||||
check(residue.size == craftingGrid.containerSize) { "Container and residue list sizes mismatch: ${residue.size} != ${craftingGrid.containerSize}" }
|
||||
|
||||
val combinedInventory = craftingPlayer.matteryPlayer?.combinedInventory
|
||||
val copy = craftingGrid.fullIterator().map { it.copy() }.toList()
|
||||
|
||||
// удаляем по одному предмету из сетки крафта
|
||||
for (slot in 0 until craftingGrid.containerSize) {
|
||||
val oldItem = craftingGrid[slot].let { if (!it.isEmpty || it.count < 2) it.copy() else it }
|
||||
|
||||
if (!craftingGrid[slot].isEmpty) {
|
||||
craftingGrid.removeItem(slot, 1)
|
||||
}
|
||||
|
||||
var newItem = craftingGrid[slot]
|
||||
|
||||
if (newItem.isEmpty) {
|
||||
when (settings.ingredientPriority) {
|
||||
ItemMonitorPlayerSettings.IngredientPriority.SYSTEM -> {
|
||||
if (poweredView != null && takeOne(craftingGridTuples[slot], poweredView!!)) {
|
||||
newItem = oldItem
|
||||
craftingGrid[slot] = newItem
|
||||
if (residue[slot].isNotEmpty) {
|
||||
craftingGrid[slot] = residue[slot]
|
||||
}
|
||||
}
|
||||
|
||||
ItemMonitorPlayerSettings.IngredientPriority.INVENTORY -> {
|
||||
if (takeOne(craftingPlayer.inventory, oldItem)) {
|
||||
newItem = oldItem
|
||||
craftingGrid[slot] = newItem
|
||||
}
|
||||
}
|
||||
|
||||
ItemMonitorPlayerSettings.IngredientPriority.SYSTEM_FIRST -> {
|
||||
if (poweredView != null && takeOne(craftingGridTuples[slot], poweredView!!)) {
|
||||
newItem = oldItem
|
||||
craftingGrid[slot] = newItem
|
||||
}
|
||||
|
||||
if (newItem.isEmpty && takeOne(craftingPlayer.inventory, oldItem)) {
|
||||
newItem = oldItem
|
||||
craftingGrid[slot] = newItem
|
||||
}
|
||||
}
|
||||
|
||||
ItemMonitorPlayerSettings.IngredientPriority.INVENTORY_FIRST -> {
|
||||
if (takeOne(craftingPlayer.inventory, oldItem)) {
|
||||
newItem = oldItem
|
||||
craftingGrid[slot] = newItem
|
||||
} else if (poweredView != null && takeOne(craftingGridTuples[slot], poweredView!!)) {
|
||||
newItem = oldItem
|
||||
craftingGrid[slot] = newItem
|
||||
}
|
||||
}
|
||||
|
||||
ItemMonitorPlayerSettings.IngredientPriority.DO_NOT -> { /* no op */ }
|
||||
}
|
||||
}
|
||||
|
||||
val remainder = remainingItems[slot]
|
||||
|
||||
if (!remainder.isEmpty) {
|
||||
when (settings.resultTarget) {
|
||||
ItemMonitorPlayerSettings.ResultTarget.ALL_SYSTEM, ItemMonitorPlayerSettings.ResultTarget.MIXED -> {
|
||||
val remaining = poweredView?.insertStack(ItemStorageStack(remainder), false)?.toItemStack() ?: remainder
|
||||
|
||||
if (!remaining.isEmpty) {
|
||||
if (newItem.isEmpty) {
|
||||
craftingGrid[slot] = remaining
|
||||
} else if (ItemStack.isSameItemSameTags(newItem, remaining)) {
|
||||
newItem.grow(remaining.count)
|
||||
craftingGrid.setChanged(slot)
|
||||
} else if (!craftingPlayer.inventory.add(remaining)) {
|
||||
craftingPlayer.drop(remaining, false)
|
||||
if (!scanCraftingGrid(true)) {
|
||||
// заполняем опустевшие слоты
|
||||
for (slot in 0 until craftingGrid.containerSize) {
|
||||
if (
|
||||
craftingGrid[slot].isEmpty &&
|
||||
copy[slot].isNotEmpty &&
|
||||
settings.ingredientPriority.takeOne(copy[slot], poweredView, combinedInventory, craftingGridTuples[slot])
|
||||
) {
|
||||
craftingGrid[slot] = copy[slot].copyWithCount(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ItemMonitorPlayerSettings.ResultTarget.ALL_INVENTORY -> {
|
||||
if (!craftingPlayer.inventory.add(remainder)) {
|
||||
val remaining = poweredView?.insertStack(ItemStorageStack(remainder), false)?.toItemStack() ?: remainder
|
||||
if (!scanCraftingGrid(true)) {
|
||||
// избавляемся от остатков, если они мешают
|
||||
for (slot in 0 until craftingGrid.containerSize) {
|
||||
if (residue[slot].isNotEmpty) {
|
||||
var remaining = residue[slot]
|
||||
|
||||
if (!remaining.isEmpty) {
|
||||
if (newItem.isEmpty) {
|
||||
craftingGrid[slot] = remaining
|
||||
} else if (ItemStack.isSameItemSameTags(newItem, remaining)) {
|
||||
newItem.grow(remaining.count)
|
||||
craftingGrid.setChanged(slot)
|
||||
} else {
|
||||
craftingPlayer.drop(remaining, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (settings.resultTarget != ItemMonitorPlayerSettings.ResultTarget.ALL_INVENTORY) {
|
||||
remaining = poweredView?.insertStack(ItemStorageStack(remaining), false)?.toItemStack() ?: remaining
|
||||
}
|
||||
|
||||
remaining = combinedInventory?.addItem(remaining, false) ?: remaining
|
||||
|
||||
if (remaining.isNotEmpty) {
|
||||
craftingPlayer.spawnAtLocation(remaining)
|
||||
}
|
||||
|
||||
if (copy[slot].isNotEmpty && settings.ingredientPriority.takeOne(copy[slot], poweredView, combinedInventory, craftingGridTuples[slot])) {
|
||||
craftingGrid[slot] = copy[slot].copyWithCount(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result.copy()
|
||||
} finally {
|
||||
inProcessOfCraft = false
|
||||
scanCraftingGrid()
|
||||
scanCraftingGrid(false)
|
||||
}
|
||||
}
|
||||
|
||||
return craftingRecipe.getResultItem(level?.registryAccess() ?: return ItemStack.EMPTY).copy()
|
||||
}
|
||||
|
||||
override fun removeItemNoUpdate(p_18951_: Int): ItemStack {
|
||||
override fun removeItemNoUpdate(slot: Int): ItemStack {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
@ -464,11 +432,7 @@ class ItemMonitorBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matte
|
||||
val settings = nbt.getCompound("player_settings")
|
||||
|
||||
for (key in settings.allKeys) {
|
||||
val uuid = UUID.fromString(key)
|
||||
val deserialized = ItemMonitorPlayerSettings()
|
||||
deserialized.deserializeNBT(settings.getCompound(key))
|
||||
|
||||
check(this.settings.put(uuid, deserialized) == null) { "Duplicate UUID??? $uuid" }
|
||||
check(this.settings.put(UUID.fromString(key), ItemMonitorPlayerSettings().also { it.deserializeNBT(settings.getCompound(key)) }) == null)
|
||||
}
|
||||
|
||||
craftingGrid.deserializeNBT(nbt["crafting_grid"])
|
||||
@ -480,14 +444,7 @@ class ItemMonitorBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matte
|
||||
|
||||
override fun tick() {
|
||||
super.tick()
|
||||
|
||||
cell.tickEnergyDemanding()
|
||||
|
||||
if (craftingAmount.size != 0)
|
||||
craftingAmount.clear()
|
||||
|
||||
if (lastCraftingRecipe.size != 0)
|
||||
lastCraftingRecipe.clear()
|
||||
}
|
||||
|
||||
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {
|
||||
@ -497,6 +454,7 @@ class ItemMonitorBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matte
|
||||
override fun setLevel(level: Level) {
|
||||
super.setLevel(level)
|
||||
cell.discover(this)
|
||||
scanCraftingGrid(false)
|
||||
}
|
||||
|
||||
override fun setRemoved() {
|
||||
|
@ -2,6 +2,7 @@ package ru.dbotthepony.mc.otm.block.entity.storage
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap
|
||||
import it.unimi.dsi.fastutil.ints.IntAVLTreeSet
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.player.Inventory
|
||||
@ -126,6 +127,7 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
|
||||
|
||||
private inner class ItemHandlerComponent(private val parent: IItemHandler) : IStorageComponent<ItemStorageStack> {
|
||||
private fun updateTuple(tuple: TrackedTuple, diff: BigInteger) {
|
||||
val stack = tuple.stack
|
||||
tuple.stack = tuple.stack.grow(diff)
|
||||
|
||||
if (tuple.stack.isNotEmpty) {
|
||||
@ -138,7 +140,7 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
|
||||
}
|
||||
|
||||
id2tuples.remove(tuple.id)
|
||||
items2tuples.remove(tuple.stack) ?: throw IllegalStateException("Cross-reference integrity check failed for $tuple")
|
||||
items2tuples.remove(stack) ?: throw IllegalStateException("Cross-reference integrity check failed for $tuple")
|
||||
}
|
||||
}
|
||||
|
||||
@ -376,29 +378,32 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
|
||||
val tuple = id2tuples[id] ?: return ItemStorageStack.EMPTY
|
||||
val slots = tuple.children.values.iterator()
|
||||
val lstack = tuple.stack
|
||||
val affectedSlots = IntArrayList()
|
||||
|
||||
while (amount < total && slots.hasNext() && energy.batteryLevel.isPositive) {
|
||||
while (amount > total && slots.hasNext() && energy.batteryLevel.isPositive) {
|
||||
val (slot, stack) = slots.next()
|
||||
val extracted = parent.extractItem(slot, stack.count.coerceAtMost(amount.toIntSafe()), true)
|
||||
val extracted = parent.extractItem(slot, stack.count.coerceAtMost((amount - total).toIntSafe()), true)
|
||||
val required = StorageStack.ITEMS.energyPerExtract(ItemStorageStack.unsafe(extracted))
|
||||
|
||||
if (extracted.isNotEmpty && tuple.stack.equalsWithoutCount(ItemStorageStack.unsafe(extracted)) && energy.extractEnergy(required, true) == required) {
|
||||
if (extracted.isNotEmpty && lstack.equalsWithoutCount(ItemStorageStack.unsafe(extracted)) && energy.extractEnergy(required, true) == required) {
|
||||
if (simulate) {
|
||||
total += extracted.count.toBigInteger()
|
||||
} else {
|
||||
affectedSlots.add(slot)
|
||||
val extracted2 = parent.extractItem(slot, extracted.count, false)
|
||||
total += extracted2.count.toBigInteger()
|
||||
|
||||
if (extracted2.count == extracted.count) {
|
||||
energy.extractEnergy(required, false)
|
||||
} else {
|
||||
energy.extractEnergy(StorageStack.ITEMS.energyPerExtract(ItemStorageStack.unsafe(extracted2)), false)
|
||||
}
|
||||
|
||||
total += extracted2.count.toBigInteger()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val i = affectedSlots.intIterator()
|
||||
while (i.hasNext()) scan(i.nextInt())
|
||||
return lstack.copy(total)
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,6 @@ import net.minecraft.world.inventory.AbstractContainerMenu
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.phys.AABB
|
||||
import net.minecraftforge.common.ForgeConfigSpec
|
||||
import ru.dbotthepony.mc.otm.block.entity.MatteryPoweredBlockEntity
|
||||
import ru.dbotthepony.mc.otm.block.entity.WorkerState
|
||||
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
||||
@ -18,13 +17,9 @@ import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
|
||||
import ru.dbotthepony.mc.otm.capability.moveEnergy
|
||||
import ru.dbotthepony.mc.otm.config.MachinesConfig
|
||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||
import ru.dbotthepony.mc.otm.core.math.DecimalConfigValue
|
||||
import ru.dbotthepony.mc.otm.core.math.defineDecimal
|
||||
import ru.dbotthepony.mc.otm.core.ifPresentK
|
||||
import ru.dbotthepony.mc.otm.menu.tech.AndroidStationMenu
|
||||
import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
||||
import ru.dbotthepony.mc.otm.registry.MNames
|
||||
import ru.dbotthepony.mc.otm.core.util.WriteOnce
|
||||
|
||||
class AndroidStationBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
|
||||
MatteryPoweredBlockEntity(MBlockEntities.ANDROID_STATION, p_155229_, p_155230_), MenuProvider {
|
||||
|
@ -4,13 +4,9 @@ import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.player.Inventory
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraftforge.common.ForgeConfigSpec
|
||||
import net.minecraftforge.common.ForgeHooks
|
||||
import net.minecraftforge.common.capabilities.ForgeCapabilities
|
||||
import net.minecraftforge.energy.IEnergyStorage
|
||||
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
|
||||
import ru.dbotthepony.mc.otm.block.entity.WorkerState
|
||||
import ru.dbotthepony.mc.otm.capability.*
|
||||
@ -19,14 +15,9 @@ import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
|
||||
import ru.dbotthepony.mc.otm.config.MachinesConfig
|
||||
import ru.dbotthepony.mc.otm.container.MatteryContainer
|
||||
import ru.dbotthepony.mc.otm.container.HandlerFilter
|
||||
import ru.dbotthepony.mc.otm.core.*
|
||||
import ru.dbotthepony.mc.otm.menu.tech.ChemicalGeneratorMenu
|
||||
import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
||||
import ru.dbotthepony.mc.otm.registry.MNames
|
||||
import ru.dbotthepony.mc.otm.core.util.WriteOnce
|
||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||
import ru.dbotthepony.mc.otm.core.math.DecimalConfigValue
|
||||
import ru.dbotthepony.mc.otm.core.math.defineDecimal
|
||||
|
||||
class ChemicalGeneratorBlockEntity(pos: BlockPos, state: BlockState) : MatteryDeviceBlockEntity(MBlockEntities.CHEMICAL_GENERATOR, pos, state) {
|
||||
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {
|
||||
|
@ -4,7 +4,6 @@ import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.player.Inventory
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
|
||||
import ru.dbotthepony.mc.otm.capability.FlowDirection
|
||||
|
@ -1,7 +1,6 @@
|
||||
package ru.dbotthepony.mc.otm.block.entity.tech
|
||||
|
||||
import net.minecraft.core.BlockPos
|
||||
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.AbstractContainerMenu
|
||||
|
@ -3,6 +3,7 @@ package ru.dbotthepony.mc.otm.capability.drive
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
||||
import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet
|
||||
import kotlin.jvm.JvmOverloads
|
||||
import java.util.UUID
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
@ -30,7 +31,7 @@ abstract class AbstractMatteryDrive<T : StorageStack<T>> @JvmOverloads construct
|
||||
|
||||
override var isDirty = false
|
||||
set(value) {
|
||||
if (value != field && value && DrivePool.isLegalAccess()) {
|
||||
if (value != field && value) {
|
||||
DrivePool.markDirty(uuid)
|
||||
}
|
||||
|
||||
@ -43,6 +44,24 @@ abstract class AbstractMatteryDrive<T : StorageStack<T>> @JvmOverloads construct
|
||||
override var storedCount: BigInteger = BigInteger.ZERO
|
||||
protected set
|
||||
|
||||
protected val listeners = ObjectLinkedOpenHashSet<IStorageEventConsumer<T>>()
|
||||
|
||||
override fun addListener(listener: IStorageEventConsumer<T>): Boolean {
|
||||
return listeners.add(listener)
|
||||
}
|
||||
|
||||
override fun removeListener(listener: IStorageEventConsumer<T>): Boolean {
|
||||
return listeners.remove(listener)
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is AbstractMatteryDrive<*> && storageType == other.storageType && uuid == other.uuid
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return (uuid.hashCode() * 31) xor storageType.hashCode()
|
||||
}
|
||||
|
||||
override fun insertStack(stack: T, simulate: Boolean): T {
|
||||
val maxInsert = driveCapacity.minus(storedCount).coerceAtMost(stack.count)
|
||||
if (maxInsert <= BigInteger.ZERO) return stack
|
||||
@ -189,14 +208,4 @@ abstract class AbstractMatteryDrive<T : StorageStack<T>> @JvmOverloads construct
|
||||
override val stacks: Stream<IStorageTuple<T>> get() {
|
||||
return ArrayList<IStorageTuple<T>>(stack2tuples.size).also { it.addAll(stack2tuples.values) }.stream()
|
||||
}
|
||||
|
||||
protected val listeners = ObjectArraySet<IStorageEventConsumer<T>>()
|
||||
|
||||
override fun addListener(listener: IStorageEventConsumer<T>): Boolean {
|
||||
return listeners.add(listener)
|
||||
}
|
||||
|
||||
override fun removeListener(listener: IStorageEventConsumer<T>): Boolean {
|
||||
return listeners.remove(listener)
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,13 @@
|
||||
package ru.dbotthepony.mc.otm.capability.drive
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectAVLTreeMap
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
|
||||
import java.util.UUID
|
||||
import net.minecraft.ReportedException
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.NbtIo
|
||||
import net.minecraft.CrashReport
|
||||
import net.minecraft.world.level.storage.LevelResource
|
||||
import java.util.concurrent.locks.LockSupport
|
||||
import net.minecraftforge.event.TickEvent.ServerTickEvent
|
||||
import net.minecraftforge.event.TickEvent
|
||||
import net.minecraftforge.event.server.ServerAboutToStartEvent
|
||||
import net.minecraftforge.event.server.ServerStoppingEvent
|
||||
import net.minecraftforge.event.level.LevelEvent
|
||||
@ -19,51 +17,82 @@ import ru.dbotthepony.mc.otm.SERVER_IS_LIVE
|
||||
import java.io.File
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.ArrayList
|
||||
import java.util.concurrent.ConcurrentLinkedQueue
|
||||
|
||||
private class WeakDriveReference(drive: IMatteryDrive<*>) {
|
||||
private var drive: IMatteryDrive<*>? = drive
|
||||
private val weak = WeakReference(drive)
|
||||
|
||||
val isValid: Boolean get() {
|
||||
return drive != null || weak.get() != null
|
||||
}
|
||||
|
||||
fun drive(): IMatteryDrive<*>? {
|
||||
return drive ?: weak.get()
|
||||
}
|
||||
|
||||
fun sync(): CompoundTag? {
|
||||
val drive = drive() ?: return null
|
||||
|
||||
if (!drive.isDirty) {
|
||||
this.drive = null
|
||||
return null
|
||||
}
|
||||
|
||||
val tag = drive.serializeNBT()
|
||||
drive.isDirty = false
|
||||
this.drive = null
|
||||
|
||||
return tag
|
||||
}
|
||||
|
||||
fun access(): IMatteryDrive<*>? {
|
||||
this.drive = weak.get()
|
||||
return drive
|
||||
}
|
||||
}
|
||||
|
||||
private data class BacklogLine(val uuid: UUID, val tag: CompoundTag)
|
||||
|
||||
/**
|
||||
* Let me answer everyone's questions about:
|
||||
*
|
||||
* Why?
|
||||
*
|
||||
* There are several reasons:
|
||||
* 1. This data can get very large very quickly, even when playing singleplayer (and much quicker and bigger when hosting a dedicated server)
|
||||
* 2. This data can not be stored inside ItemStack.ForgeCaps due to it's size
|
||||
* 3. This data can not be stored inside unshared (server only) nbt tag because, again, mods prone to use and interact with
|
||||
* it wrong, causing loss of stored data or mods exposing full content of a drive inside their own tag (which cause very real "NBT size too large"
|
||||
* network kicks, often locking players out of server/singleplayer worlds
|
||||
* 4. [net.minecraft.world.level.saveddata.SavedData] is for storing everything inside one dat file, which
|
||||
* is performance tanking, because we have to write *entire* NBT on each save, not the data of Drives that are dirty
|
||||
* 5. Mods which check items for being stack-able even with stack size of 1 gonna compare nbt tag,
|
||||
* which will be performance tanking due to clause 1.
|
||||
* Separate thread and separate files are way faster for enormous volumes of data (if mod is being run on public dedicated server)
|
||||
*/
|
||||
object DrivePool {
|
||||
private val resource = LevelResource("otm_drives")
|
||||
private val LOGGER = LogManager.getLogger()
|
||||
private val pool = Object2ObjectOpenHashMap<UUID, WeakDriveReference>()
|
||||
private var thread: Thread? = null
|
||||
private var stopping = false
|
||||
private var exception: ReportedException? = null
|
||||
private const val DATA_PATH = "data/otm_drives"
|
||||
|
||||
@JvmStatic
|
||||
operator fun <T : IMatteryDrive<*>> get(
|
||||
id: UUID,
|
||||
deserializer: (CompoundTag) -> T,
|
||||
factory: () -> T
|
||||
): T {
|
||||
private val backlog = ConcurrentLinkedQueue<BacklogLine>()
|
||||
private var knownBaseDirectory: File? = null
|
||||
|
||||
private var serverThread: Thread? = null
|
||||
|
||||
private val baseDirectory: File? get() {
|
||||
val server = NULLABLE_MINECRAFT_SERVER ?: return null
|
||||
|
||||
val baseDirectory = server.storageSource.getLevelPath(resource).toFile()
|
||||
|
||||
if (knownBaseDirectory != baseDirectory) {
|
||||
baseDirectory.mkdirs()
|
||||
knownBaseDirectory = baseDirectory
|
||||
}
|
||||
|
||||
return baseDirectory
|
||||
}
|
||||
|
||||
operator fun <T : IMatteryDrive<*>> get(id: UUID, deserializer: (CompoundTag) -> T, factory: () -> T): T {
|
||||
if (!isLegalAccess())
|
||||
throw IllegalAccessException("Can not access drive pool from outside of server thread.")
|
||||
|
||||
if (!SERVER_IS_LIVE)
|
||||
throw IllegalStateException("Fail-fast: Server is shutting down")
|
||||
throw IllegalStateException("Server is not running")
|
||||
|
||||
val get = pool[id]
|
||||
val get = pool[id]?.access()
|
||||
|
||||
if (get != null) {
|
||||
val accessed = get.access()
|
||||
|
||||
if (accessed != null) {
|
||||
return accessed as T
|
||||
}
|
||||
return get as T
|
||||
}
|
||||
|
||||
val file = File(baseDirectory, "$id.dat")
|
||||
@ -85,70 +114,10 @@ object DrivePool {
|
||||
return factory().also { pool[id] = WeakDriveReference(it) }
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun put(id: UUID, drive: IMatteryDrive<*>) {
|
||||
if (!isLegalAccess())
|
||||
throw IllegalAccessException("Can not access drive pool from outside of server thread.")
|
||||
|
||||
if (!SERVER_IS_LIVE)
|
||||
throw IllegalStateException("Fail-fast: Server is shutting down")
|
||||
|
||||
pool[id] = WeakDriveReference(drive)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun markDirty(id: UUID) {
|
||||
if (!isLegalAccess())
|
||||
throw IllegalAccessException("Can not access drive pool from outside of server thread.")
|
||||
|
||||
if (!SERVER_IS_LIVE)
|
||||
throw IllegalStateException("Fail-fast: Server is shutting down")
|
||||
|
||||
if (isLegalAccess()) {
|
||||
pool[id]?.access()
|
||||
}
|
||||
|
||||
private var backlog = ArrayList<BacklogLine>()
|
||||
private var knownBaseDirectory: File? = null
|
||||
|
||||
private val baseDirectory: File? get() {
|
||||
val server = NULLABLE_MINECRAFT_SERVER ?: return null
|
||||
|
||||
val baseDirectory = File(server.storageSource.worldDir.toFile(), DATA_PATH)
|
||||
|
||||
if (knownBaseDirectory != baseDirectory) {
|
||||
baseDirectory.mkdirs()
|
||||
knownBaseDirectory = baseDirectory
|
||||
}
|
||||
|
||||
return baseDirectory
|
||||
}
|
||||
|
||||
private var serverThread: Thread? = null
|
||||
|
||||
private fun thread() {
|
||||
while (true) {
|
||||
LockSupport.park()
|
||||
|
||||
if (stopping) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// TODO: Syncing status with main thread, since server stop can occur during work.
|
||||
sync()
|
||||
} catch (error: ReportedException) {
|
||||
exception = error
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onServerPostTick(event: ServerTickEvent) {
|
||||
if (event.phase == TickEvent.Phase.END) {
|
||||
if (exception != null) {
|
||||
throw exception!!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -156,12 +125,8 @@ object DrivePool {
|
||||
*
|
||||
* If you are making your own drive for your own storage stack, feed code outside of server thread (e.g. client code) dummy disk.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun isLegalAccess(): Boolean {
|
||||
if (serverThread == null)
|
||||
return false
|
||||
|
||||
return Thread.currentThread() === serverThread
|
||||
return serverThread != null && Thread.currentThread() === serverThread
|
||||
}
|
||||
|
||||
fun serverStartEvent(event: ServerAboutToStartEvent) {
|
||||
@ -175,7 +140,7 @@ object DrivePool {
|
||||
serverThread = Thread.currentThread()
|
||||
|
||||
stopping = false
|
||||
thread = Thread(null, this::thread, "Overdrive That Matters DrivePool IO").also { it.start() }
|
||||
thread = Thread(null, this::run, "Overdrive That Matters DrivePool IO").also { it.start() }
|
||||
}
|
||||
|
||||
fun serverStopEvent(event: ServerStoppingEvent) {
|
||||
@ -186,11 +151,8 @@ object DrivePool {
|
||||
LockSupport.unpark(thread)
|
||||
}
|
||||
|
||||
if (exception == null) {
|
||||
writeBacklog()
|
||||
sync()
|
||||
}
|
||||
|
||||
pool.clear()
|
||||
}
|
||||
|
||||
@ -200,125 +162,58 @@ object DrivePool {
|
||||
|
||||
private fun writeBacklog() {
|
||||
var needsSync = false
|
||||
var removeKeys: ArrayList<UUID>? = null
|
||||
val removeKeys = ArrayList<UUID>()
|
||||
|
||||
for ((key, value) in pool) {
|
||||
try {
|
||||
val tag = value.sync()
|
||||
|
||||
if (tag != null) {
|
||||
LOGGER.info("Serializing OTM Drive {}", key)
|
||||
val index = backlog.indexOf<Any>(key)
|
||||
|
||||
if (index != -1) {
|
||||
backlog[index] = BacklogLine(key, tag, value.drive()!!)
|
||||
} else {
|
||||
backlog.add(BacklogLine(key, tag, value.drive()!!))
|
||||
}
|
||||
|
||||
LOGGER.debug("Serializing OTM Drive {}", key)
|
||||
backlog.add(BacklogLine(key, tag))
|
||||
needsSync = true
|
||||
} else if (!value.valid()) {
|
||||
if (removeKeys == null)
|
||||
removeKeys = ArrayList()
|
||||
|
||||
} else if (!value.isValid) {
|
||||
removeKeys.add(key)
|
||||
}
|
||||
} catch (err: Throwable) {
|
||||
LOGGER.error("Failed to serialize OTM Drive for persistent storage with ID $key", err)
|
||||
}
|
||||
}
|
||||
|
||||
if (removeKeys != null) {
|
||||
for (key in removeKeys) {
|
||||
pool.remove(key)
|
||||
}
|
||||
}
|
||||
|
||||
if (needsSync) {
|
||||
LockSupport.unpark(thread)
|
||||
}
|
||||
}
|
||||
|
||||
private fun run() {
|
||||
while (!stopping) {
|
||||
sync()
|
||||
LockSupport.park()
|
||||
}
|
||||
}
|
||||
|
||||
private fun sync() {
|
||||
val copy = backlog
|
||||
backlog = ArrayList()
|
||||
while (true) {
|
||||
val next = backlog.poll() ?: return
|
||||
val (uuid, tag) = next
|
||||
|
||||
for (entry in copy) {
|
||||
try {
|
||||
LOGGER.info("Syncing OTM Drive {}", entry.file)
|
||||
LOGGER.debug("Syncing OTM Drive {}", uuid)
|
||||
|
||||
val oldFile = File(baseDirectory, entry.file.toString() + ".dat_old")
|
||||
val oldFile = File(baseDirectory, "$uuid.dat_old")
|
||||
val newFile = File(baseDirectory, "$uuid.dat")
|
||||
|
||||
if (oldFile.exists()) {
|
||||
check(oldFile.delete()) { "Unable to delete old dat file" }
|
||||
}
|
||||
if (oldFile.exists()) check(oldFile.delete()) { "Unable to delete old dat file" }
|
||||
if (newFile.exists()) check(newFile.renameTo(oldFile)) { "Unable to move old dat file" }
|
||||
|
||||
val newFile = File(baseDirectory, entry.file.toString() + ".dat")
|
||||
|
||||
if (newFile.exists()) {
|
||||
check(newFile.renameTo(oldFile)) { "Unable to move old dat file" }
|
||||
}
|
||||
|
||||
NbtIo.writeCompressed(entry.tag, newFile)
|
||||
NbtIo.writeCompressed(tag, newFile)
|
||||
} catch (error: Throwable) {
|
||||
val report = CrashReport("Syncing OTM Drives state to disk", error)
|
||||
val category = report.addCategory("Corrupt drive")
|
||||
category.setDetail("UUID", entry.file.toString())
|
||||
category.setDetail("Stored item count", entry.drive.storedCount)
|
||||
category.setDetail("Capacity", entry.drive.driveCapacity)
|
||||
throw ReportedException(report)
|
||||
LOGGER.error("Failed to sync OTM Drive to persistent storage with ID $uuid", error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class WeakDriveReference(drive: IMatteryDrive<*>) {
|
||||
private var drive: IMatteryDrive<*>? = drive
|
||||
private val weak = WeakReference(drive)
|
||||
|
||||
fun valid(): Boolean {
|
||||
return drive != null || weak.get() != null
|
||||
}
|
||||
|
||||
fun drive(): IMatteryDrive<*>? {
|
||||
return drive ?: weak.get()
|
||||
}
|
||||
|
||||
fun sync(): CompoundTag? {
|
||||
var drive = drive
|
||||
|
||||
if (drive == null) {
|
||||
drive = weak.get()
|
||||
|
||||
if (drive == null)
|
||||
return null
|
||||
}
|
||||
|
||||
if (!drive.isDirty) {
|
||||
this.drive = null
|
||||
return null
|
||||
}
|
||||
|
||||
val tag = drive.serializeNBT()
|
||||
drive.isDirty = false
|
||||
this.drive = null
|
||||
|
||||
return tag
|
||||
}
|
||||
|
||||
fun access(): IMatteryDrive<*>? {
|
||||
this.drive = weak.get()
|
||||
return drive
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("EqualsOrHashCode")
|
||||
private data class BacklogLine(val file: UUID, val tag: CompoundTag, val drive: IMatteryDrive<*>) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other is BacklogLine) {
|
||||
return other.file == file
|
||||
}
|
||||
|
||||
if (other is UUID) {
|
||||
return other == file
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -54,27 +54,10 @@ class MatterPanelScreen(
|
||||
|
||||
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)
|
||||
controls.sortingButtons(menu.isAscendingGS, menu.sortingGS, ItemSorter.DEFAULT) {
|
||||
for (v in ItemSorter.entries) {
|
||||
add(v, skinElement = v.icon, tooltip = v.title)
|
||||
}
|
||||
|
||||
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)
|
||||
it.add(ItemSorter.MATTER_VALUE, skinElement = Widgets18.SORT_MATTER_VALUE, tooltip = ItemSorter.MATTER_VALUE.title)
|
||||
it.add(ItemSorter.MATTER_COMPLEXITY, skinElement = Widgets18.SORT_MATTER_COMPLEXITY, tooltip = ItemSorter.MATTER_COMPLEXITY.title)
|
||||
it.finish()
|
||||
}
|
||||
|
||||
val scrollBar = DiscreteScrollBarPanel(this, frame, {
|
||||
@ -156,7 +139,7 @@ class MatterPanelScreen(
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemStackTooltip(stack: ItemStack): List<Component> {
|
||||
override fun getItemStackTooltip(stack: ItemStack): MutableList<Component> {
|
||||
val list = super.getItemStackTooltip(stack).toMutableList()
|
||||
|
||||
if (isPatternView) {
|
||||
@ -326,7 +309,7 @@ class MatterPanelScreen(
|
||||
return pattern.stack()
|
||||
}
|
||||
|
||||
override fun getItemStackTooltip(stack: ItemStack): List<Component> {
|
||||
override fun getItemStackTooltip(stack: ItemStack): MutableList<Component> {
|
||||
return super.getItemStackTooltip(stack).toMutableList().also {
|
||||
it.add(TranslatableComponent(
|
||||
"otm.item.pattern.research",
|
||||
|
@ -31,8 +31,26 @@ open class FramePanel<out S : Screen>(
|
||||
var onOpen: Runnable? = null,
|
||||
var onClose: Runnable? = null,
|
||||
var activeIcon: IGUIRenderable? = null,
|
||||
var inactiveIcon: IGUIRenderable? = null,
|
||||
var inactiveIcon: IGUIRenderable? = activeIcon,
|
||||
) : AbstractButtonPanel<S>(this@FramePanel.screen, this@FramePanel, 0f, 0f, 26f, 28f) {
|
||||
constructor(panels: List<EditablePanel<*>>, activeIcon: IGUIRenderable? = null, inactiveIcon: IGUIRenderable? = activeIcon) : this(activeIcon = activeIcon, inactiveIcon = inactiveIcon) {
|
||||
onOpen = Runnable {
|
||||
for (panel in panels) {
|
||||
panel.visible = true
|
||||
}
|
||||
}
|
||||
|
||||
onClose = Runnable {
|
||||
for (panel in panels) {
|
||||
panel.visible = false
|
||||
}
|
||||
}
|
||||
|
||||
if (!isActive) {
|
||||
onClose!!.run()
|
||||
}
|
||||
}
|
||||
|
||||
var isActive = tabs.isEmpty()
|
||||
protected set
|
||||
|
||||
|
@ -0,0 +1,110 @@
|
||||
package ru.dbotthepony.mc.otm.client.screen.panels
|
||||
|
||||
import net.minecraft.ChatFormatting
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import ru.dbotthepony.mc.otm.client.render.RenderGravity
|
||||
import ru.dbotthepony.mc.otm.client.render.draw
|
||||
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
|
||||
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.GridPanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.util.ScrollBarConstants
|
||||
import ru.dbotthepony.mc.otm.client.screen.storage.ItemMonitorScreen
|
||||
import ru.dbotthepony.mc.otm.core.TranslatableComponent
|
||||
import ru.dbotthepony.mc.otm.core.math.integerDivisionDown
|
||||
import ru.dbotthepony.mc.otm.core.util.formatReadableNumber
|
||||
import ru.dbotthepony.mc.otm.core.util.formatSiComponent
|
||||
import ru.dbotthepony.mc.otm.menu.data.NetworkedItemView
|
||||
import ru.dbotthepony.mc.otm.storage.StorageStack
|
||||
|
||||
open class NetworkedItemGridPanel<out S : MatteryScreen<*>>(
|
||||
screen: S,
|
||||
parent: EditablePanel<*>,
|
||||
val view: NetworkedItemView,
|
||||
x: Float = 0f,
|
||||
y: Float = 0f,
|
||||
width: Float = 40f,
|
||||
height: Float = 40f,
|
||||
) : EditablePanel<S>(screen, parent, x, y, width, height) {
|
||||
constructor(
|
||||
screen: S,
|
||||
parent: EditablePanel<*>,
|
||||
view: NetworkedItemView,
|
||||
x: Float = 0f,
|
||||
y: Float = 0f,
|
||||
width: Int,
|
||||
height: Int
|
||||
) : this(screen, parent, view, x, y, width * AbstractSlotPanel.SIZE + ScrollBarConstants.WIDTH, height * AbstractSlotPanel.SIZE)
|
||||
|
||||
private val slots = ArrayList<Slot>()
|
||||
|
||||
val canvas = object : GridPanel<S>(screen, this@NetworkedItemGridPanel, 0f, 0f, 0f, 0f, 0, 0) {
|
||||
override fun performLayout() {
|
||||
super.performLayout()
|
||||
|
||||
val count = columns * rows
|
||||
|
||||
while (slots.size < count) {
|
||||
slots.add(Slot(slots.size))
|
||||
}
|
||||
|
||||
while (slots.size > count) {
|
||||
slots.removeLast().remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val scrollbar = DiscreteScrollBarPanel(
|
||||
screen,
|
||||
this,
|
||||
{ integerDivisionDown(view.itemCount, canvas.columns) },
|
||||
{ _, _, _ -> }
|
||||
)
|
||||
|
||||
inner class Slot(private val i: Int) : AbstractSlotPanel<S>(screen, canvas) {
|
||||
private val index get() = i + scrollbar.scroll * canvas.columns
|
||||
|
||||
override val itemStack: ItemStack get() {
|
||||
return view.sortedView.getOrNull(index)?.stack?.toItemStack() ?: ItemStack.EMPTY
|
||||
}
|
||||
|
||||
override fun mouseScrolledInner(x: Double, y: Double, scroll: Double): Boolean {
|
||||
return scrollbar.mouseScrolledInner(x, y, scroll)
|
||||
}
|
||||
|
||||
override fun mouseClickedInner(x: Double, y: Double, button: Int): Boolean {
|
||||
view.mouseClick(index, button)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun innerRender(graphics: GuiGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {
|
||||
renderSlotBackground(graphics, mouseX, mouseY, partialTick)
|
||||
val itemStack = view.sortedView.getOrNull(index)?.stack ?: StorageStack.ITEMS.empty
|
||||
renderRegular(graphics, itemStack.toItemStack(), "")
|
||||
|
||||
if (!itemStack.isEmpty) {
|
||||
graphics.draw(font, itemStack.count.formatSiComponent(decimalPlaces = 1), x = width - 1f, y = height - 1f, gravity = RenderGravity.BOTTOM_RIGHT, scale = 0.75f, drawShadow = true)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemStackTooltip(stack: ItemStack): MutableList<Component> {
|
||||
return super.getItemStackTooltip(stack).also {
|
||||
it.add(TranslatableComponent("otm.gui.stored_amount", view.sortedView[index].stack.count.formatReadableNumber()).withStyle(ChatFormatting.DARK_GRAY))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
canvas.dock = Dock.FILL
|
||||
scrollbar.dock = Dock.RIGHT
|
||||
}
|
||||
|
||||
override fun performLayout() {
|
||||
super.performLayout()
|
||||
|
||||
canvas.rows = (canvas.height / AbstractSlotPanel.SIZE).toInt()
|
||||
canvas.columns = (canvas.width / AbstractSlotPanel.SIZE).toInt()
|
||||
}
|
||||
}
|
@ -29,6 +29,7 @@ import ru.dbotthepony.mc.otm.core.GetterSetter
|
||||
import ru.dbotthepony.mc.otm.core.TextComponent
|
||||
import ru.dbotthepony.mc.otm.core.TranslatableComponent
|
||||
import ru.dbotthepony.mc.otm.core.math.RelativeSide
|
||||
import ru.dbotthepony.mc.otm.core.util.ItemSorter
|
||||
import ru.dbotthepony.mc.otm.core.value
|
||||
import ru.dbotthepony.mc.otm.menu.UpgradeSlots
|
||||
import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback
|
||||
@ -315,7 +316,6 @@ class DeviceControls<out S : MatteryScreen<*>>(
|
||||
val upgradesButton: LargeRectangleButtonPanel<S>?
|
||||
|
||||
private var upgradeWindow: FramePanel<S>? = null
|
||||
|
||||
private var nextY = 0f
|
||||
|
||||
fun <P : EditablePanel<@UnsafeVariance S>> addButton(button: P): P {
|
||||
@ -328,6 +328,40 @@ class DeviceControls<out S : MatteryScreen<*>>(
|
||||
return button
|
||||
}
|
||||
|
||||
fun <P : EditablePanel<@UnsafeVariance S>> prependButton(button: P): P {
|
||||
for (child in children) {
|
||||
child.y += button.height + 2f
|
||||
}
|
||||
|
||||
button.parent = this
|
||||
button.x = 0f
|
||||
button.y = 0f
|
||||
nextY += button.height + 2f
|
||||
height = nextY - 2f
|
||||
width = button.width.coerceAtLeast(width)
|
||||
return button
|
||||
}
|
||||
|
||||
inline fun <reified T : Enum<T>> sortingButtons(ascending: GetterSetter<Boolean>, sorting: GetterSetter<T>, default: T, configurator: LargeEnumRectangleButtonPanel<S, T>.() -> Unit) {
|
||||
LargeBooleanRectangleButtonPanel(
|
||||
screen,
|
||||
this,
|
||||
prop = ascending,
|
||||
skinElementActive = Widgets18.ARROW_UP,
|
||||
skinElementInactive = Widgets18.ARROW_DOWN,
|
||||
tooltipActive = TranslatableComponent("otm.gui.sorting.ascending"),
|
||||
tooltipInactive = TranslatableComponent("otm.gui.sorting.descending"),
|
||||
).also {
|
||||
prependButton(it)
|
||||
}
|
||||
|
||||
LargeEnumRectangleButtonPanel(screen, this, enum = T::class.java, prop = sorting, defaultValue = default).also {
|
||||
prependButton(it)
|
||||
configurator.invoke(it)
|
||||
it.finish()
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
for (button in extra) {
|
||||
addButton(button)
|
||||
|
@ -42,7 +42,7 @@ abstract class AbstractSlotPanel<out S : MatteryScreen<*>> @JvmOverloads constru
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun getItemStackTooltip(stack: ItemStack): List<Component> {
|
||||
protected open fun getItemStackTooltip(stack: ItemStack): MutableList<Component> {
|
||||
return getTooltipFromItem(ru.dbotthepony.mc.otm.client.minecraft, stack)
|
||||
}
|
||||
|
||||
|
@ -4,16 +4,28 @@ import net.minecraft.client.gui.screens.Screen
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.Dock
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
|
||||
|
||||
open class GridPanel<out S : Screen> @JvmOverloads constructor(
|
||||
open class GridPanel<out S : Screen>(
|
||||
screen: S,
|
||||
parent: EditablePanel<*>?,
|
||||
x: Float = 0f,
|
||||
y: Float = 0f,
|
||||
width: Float,
|
||||
height: Float,
|
||||
protected var columns: Int,
|
||||
protected var rows: Int
|
||||
width: Float = 0f,
|
||||
height: Float = 0f,
|
||||
columns: Int,
|
||||
rows: Int
|
||||
) : EditablePanel<S>(screen, parent, x, y, width, height) {
|
||||
var columns: Int = columns
|
||||
set(value) {
|
||||
field = value
|
||||
invalidateLayout()
|
||||
}
|
||||
|
||||
var rows: Int = rows
|
||||
set(value) {
|
||||
field = value
|
||||
invalidateLayout()
|
||||
}
|
||||
|
||||
override fun performLayout() {
|
||||
var currentX = 0f
|
||||
var currentY = 0f
|
||||
|
@ -1,23 +1,26 @@
|
||||
package ru.dbotthepony.mc.otm.client.screen.storage
|
||||
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.world.entity.player.Inventory
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import ru.dbotthepony.mc.otm.client.render.ItemStackIcon
|
||||
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
|
||||
import ru.dbotthepony.mc.otm.core.TranslatableComponent
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.*
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.button.CheckBoxLabelInputPanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.button.DeviceControls
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.slot.BatterySlotPanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.slot.FilterSlotPanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.util.DiscreteScrollBarPanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.util.GridPanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.widget.PowerGaugePanel
|
||||
import ru.dbotthepony.mc.otm.core.math.integerDivisionDown
|
||||
import ru.dbotthepony.mc.otm.client.screen.widget.WideProfiledPowerGaugePanel
|
||||
import ru.dbotthepony.mc.otm.core.asGetterSetter
|
||||
import ru.dbotthepony.mc.otm.core.util.ItemStorageStackSorter
|
||||
import ru.dbotthepony.mc.otm.item.PortableCondensationDriveItem
|
||||
import ru.dbotthepony.mc.otm.menu.storage.DriveViewerMenu
|
||||
import ru.dbotthepony.mc.otm.registry.MItems
|
||||
import yalter.mousetweaks.api.MouseTweaksDisableWheelTweak
|
||||
|
||||
@MouseTweaksDisableWheelTweak
|
||||
@ -25,106 +28,58 @@ class DriveViewerScreen(menu: DriveViewerMenu, inventory: Inventory, title: Comp
|
||||
MatteryScreen<DriveViewerMenu>(menu, inventory, title) {
|
||||
|
||||
override fun makeMainFrame(): FramePanel<MatteryScreen<*>> {
|
||||
val frame = FramePanel(this, null, 0f, 0f, FRAME_WIDTH, FRAME_HEIGHT, getTitle())
|
||||
val frame = FramePanel(this, null, 0f, 0f, 200f, 114f, getTitle())
|
||||
|
||||
frame.makeCloseButton()
|
||||
frame.onClose { onClose() }
|
||||
|
||||
val views = ArrayList<EditablePanel<*>>()
|
||||
val controls = DeviceControls(this, frame, redstoneConfig = menu.redstoneConfig, energyConfig = menu.energyConfig)
|
||||
|
||||
controls.sortingButtons(menu.settings::isAscending.asGetterSetter(), menu.settings::sorting.asGetterSetter(), ItemStorageStackSorter.DEFAULT) {
|
||||
for (v in ItemStorageStackSorter.entries) {
|
||||
add(v, v.icon, v.title)
|
||||
}
|
||||
}
|
||||
|
||||
val view = ArrayList<EditablePanel<*>>()
|
||||
val settings = ArrayList<EditablePanel<*>>()
|
||||
|
||||
frame.Tab(onOpen = {
|
||||
for (panel in views) {
|
||||
panel.visible = true
|
||||
}
|
||||
}, onClose = {
|
||||
for (panel in views) {
|
||||
panel.visible = false
|
||||
view.add(EditablePanel(this, frame, width = AbstractSlotPanel.SIZE).also {
|
||||
it.dock = Dock.LEFT
|
||||
WideProfiledPowerGaugePanel(this, it, menu.profiledEnergy).also {
|
||||
it.dock = Dock.TOP
|
||||
it.dockBottom = 6f
|
||||
}
|
||||
|
||||
BatterySlotPanel(this, it, menu.batterySlot).dock = Dock.TOP
|
||||
SlotPanel(this, it, menu.driveSlot).dock = Dock.TOP
|
||||
})
|
||||
|
||||
frame.Tab(onOpen = {
|
||||
for (panel in settings) {
|
||||
panel.visible = true
|
||||
}
|
||||
}, onClose = {
|
||||
for (panel in settings) {
|
||||
panel.visible = false
|
||||
}
|
||||
view.add(NetworkedItemGridPanel(this, frame, menu.networkedItemView).also {
|
||||
it.dock = Dock.FILL
|
||||
it.setDockMargin(4f, 0f, 0f, 0f)
|
||||
})
|
||||
|
||||
views.add(PowerGaugePanel(this, frame, menu.energyWidget, 8f, 16f))
|
||||
views.add(BatterySlotPanel(this, frame, menu.batterySlot, 8f, 67f))
|
||||
views.add(SlotPanel(this, frame, menu.driveSlot, 8f, 85f))
|
||||
|
||||
val grid = GridPanel(this, frame, 28f, 16f, GRID_WIDTH * 18f, GRID_HEIGHT * 18f, GRID_WIDTH, GRID_HEIGHT)
|
||||
|
||||
val scrollBar = DiscreteScrollBarPanel(this, frame, { integerDivisionDown(menu.networkedItemView.itemCount, GRID_WIDTH) }, { _, _, _ -> }, 192f, 14f, 92f)
|
||||
|
||||
views.add(grid)
|
||||
views.add(scrollBar)
|
||||
|
||||
for (i in 0 until GRID_WIDTH * GRID_HEIGHT) {
|
||||
object : AbstractSlotPanel<DriveViewerScreen>(this@DriveViewerScreen, grid, 0f, 0f) {
|
||||
override val itemStack: ItemStack get() {
|
||||
val index = i + scrollBar.scroll * GRID_WIDTH
|
||||
return menu.networkedItemView.sortedView.getOrNull(index)?.stack?.toItemStack() ?: ItemStack.EMPTY
|
||||
}
|
||||
|
||||
override fun mouseScrolledInner(x: Double, y: Double, scroll: Double): Boolean {
|
||||
return scrollBar.mouseScrolledInner(x, y, scroll)
|
||||
}
|
||||
|
||||
override fun mouseClickedInner(x: Double, y: Double, button: Int): Boolean {
|
||||
val index = i + scrollBar.scroll * GRID_WIDTH
|
||||
menu.networkedItemView.mouseClick(index, button)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun innerRender(graphics: GuiGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {
|
||||
renderSlotBackground(graphics, mouseX, mouseY, partialTick)
|
||||
renderRegular(graphics, itemStack, "")
|
||||
}
|
||||
|
||||
override fun getItemStackTooltip(stack: ItemStack): List<Component> {
|
||||
return super.getItemStackTooltip(stack).also {
|
||||
it as MutableList<Component>
|
||||
val index = i + scrollBar.scroll * GRID_WIDTH
|
||||
val realStack = menu.networkedItemView.sortedView.getOrNull(index)!!.stack
|
||||
it.add(TranslatableComponent("otm.gui.item_amount", realStack.count.toString()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val dock_left = FlexGridPanel(this, frame, 0f, 0f, 90f, 0f)
|
||||
dock_left.dock = Dock.LEFT
|
||||
dock_left.setDockMargin(4f, 0f, 4f, 0f)
|
||||
|
||||
val grid_filter = FlexGridPanel(this, frame)
|
||||
grid_filter.dock = Dock.FILL
|
||||
settings.add(dock_left)
|
||||
settings.add(grid_filter)
|
||||
val filterGrid = GridPanel(this, frame, width = AbstractSlotPanel.SIZE * 3f, height = AbstractSlotPanel.SIZE * 4f, rows = 3, columns = 4)
|
||||
filterGrid.dock = Dock.FILL
|
||||
filterGrid.dockResize = DockResizeMode.NONE
|
||||
filterGrid.dockTop = 20f
|
||||
settings.add(filterGrid)
|
||||
|
||||
for (i in 0 until PortableCondensationDriveItem.MAX_FILTERS) {
|
||||
FilterSlotPanel(this, grid_filter, menu.driveFilterSlots[i], 0f, 0f)
|
||||
FilterSlotPanel(this, filterGrid, menu.driveFilterSlots[i], 0f, 0f)
|
||||
}
|
||||
|
||||
CheckBoxLabelInputPanel(this, dock_left, menu.isWhitelist, TranslatableComponent("otm.gui.filter.is_whitelist"), width = 90f, height = 20f).also { it.setDockMargin(0f, 4f, 0f, 0f) }
|
||||
CheckBoxLabelInputPanel(this, dock_left, menu.matchTag, TranslatableComponent("otm.gui.filter.match_tag"), width = 90f, height = 20f).also { it.setDockMargin(0f, 4f, 0f, 0f) }
|
||||
CheckBoxLabelInputPanel(this, dock_left, menu.matchNBT, TranslatableComponent("otm.gui.filter.match_nbt"), width = 90f, height = 20f).also { it.setDockMargin(0f, 4f, 0f, 0f) }
|
||||
settings.add(EditablePanel(this, frame, width = 90f).also {
|
||||
it.dock = Dock.LEFT
|
||||
CheckBoxLabelInputPanel(this, it, menu.isWhitelist, TranslatableComponent("otm.gui.filter.is_whitelist")).also { it.dockTop = 20f; it.dock = Dock.TOP }
|
||||
CheckBoxLabelInputPanel(this, it, menu.matchTag, TranslatableComponent("otm.gui.filter.match_tag")).also { it.dockTop = 4f; it.dock = Dock.TOP }
|
||||
CheckBoxLabelInputPanel(this, it, menu.matchNBT, TranslatableComponent("otm.gui.filter.match_nbt")).also { it.dockTop = 4f; it.dock = Dock.TOP }
|
||||
})
|
||||
|
||||
for (panel in settings) {
|
||||
panel.visible = false
|
||||
}
|
||||
frame.Tab(view, activeIcon = ItemStackIcon(ItemStack(MItems.PORTABLE_CONDENSATION_DRIVE)))
|
||||
frame.Tab(settings, activeIcon = ItemStackIcon(ItemStack(Items.HOPPER)))
|
||||
|
||||
return frame
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val FRAME_WIDTH = 210f
|
||||
const val FRAME_HEIGHT = 110f
|
||||
const val GRID_WIDTH = 9
|
||||
const val GRID_HEIGHT = 5
|
||||
}
|
||||
}
|
||||
|
@ -1,23 +1,21 @@
|
||||
package ru.dbotthepony.mc.otm.client.screen.storage
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import net.minecraft.ChatFormatting
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.world.entity.player.Inventory
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import org.lwjgl.opengl.GL11
|
||||
import ru.dbotthepony.mc.otm.core.TranslatableComponent
|
||||
import ru.dbotthepony.mc.otm.block.entity.storage.ItemMonitorPlayerSettings
|
||||
import ru.dbotthepony.mc.otm.client.render.UVWindingOrder
|
||||
import ru.dbotthepony.mc.otm.client.render.Widgets8
|
||||
import ru.dbotthepony.mc.otm.client.render.Widgets18
|
||||
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.Dock
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.DockResizeMode
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.FramePanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.NetworkedItemGridPanel
|
||||
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.button.SmallEnumRectangleButtonPanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.slot.BatterySlotPanel
|
||||
@ -27,12 +25,10 @@ import ru.dbotthepony.mc.otm.client.screen.panels.util.DiscreteScrollBarPanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.panels.util.GridPanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel
|
||||
import ru.dbotthepony.mc.otm.client.screen.widget.WidePowerGaugePanel
|
||||
import ru.dbotthepony.mc.otm.core.TranslatableComponent
|
||||
import ru.dbotthepony.mc.otm.core.asGetterSetter
|
||||
import ru.dbotthepony.mc.otm.core.math.integerDivisionDown
|
||||
import ru.dbotthepony.mc.otm.core.util.formatReadableNumber
|
||||
import ru.dbotthepony.mc.otm.core.util.formatSiComponent
|
||||
import ru.dbotthepony.mc.otm.core.util.ItemStorageStackSorter
|
||||
import ru.dbotthepony.mc.otm.menu.storage.ItemMonitorMenu
|
||||
import ru.dbotthepony.mc.otm.storage.StorageStack
|
||||
import yalter.mousetweaks.api.MouseTweaksDisableWheelTweak
|
||||
|
||||
@MouseTweaksDisableWheelTweak
|
||||
@ -57,70 +53,16 @@ class ItemMonitorScreen(menu: ItemMonitorMenu, inventory: Inventory, title: Comp
|
||||
frame.height = topPanel.height + bottomPanel.height + frame.dockPadding.top + frame.dockPadding.bottom + 3f
|
||||
frame.width = 178f + frame.dockPadding.left + frame.dockPadding.right
|
||||
|
||||
val viewScrollBar = DiscreteScrollBarPanel(this, topPanel,
|
||||
{ integerDivisionDown(menu.networkedItemView.itemCount, ITEM_GRID_WIDTH) },
|
||||
{ _, _, _ -> },
|
||||
28f + ITEM_GRID_WIDTH * 18f + 2f, 16f, ITEM_GRID_HEIGHT * 18f)
|
||||
val controls = DeviceControls(this, frame)
|
||||
|
||||
viewScrollBar.dock = Dock.RIGHT
|
||||
viewScrollBar.setDockMargin(left = 2f)
|
||||
|
||||
val gridPanel = GridPanel(this, topPanel, width = ITEM_GRID_WIDTH * 18f, height = ITEM_GRID_HEIGHT * 18f, columns = ITEM_GRID_WIDTH, rows = ITEM_GRID_HEIGHT)
|
||||
gridPanel.dock = Dock.FILL
|
||||
|
||||
for (i in 0 until ITEM_GRID_WIDTH * ITEM_GRID_HEIGHT) {
|
||||
object : AbstractSlotPanel<ItemMonitorScreen>(this@ItemMonitorScreen, gridPanel) {
|
||||
private val index get() = i + viewScrollBar.scroll * ITEM_GRID_WIDTH
|
||||
|
||||
override val itemStack: ItemStack get() {
|
||||
return menu.networkedItemView.sortedView.getOrNull(index)?.stack?.toItemStack() ?: ItemStack.EMPTY
|
||||
}
|
||||
|
||||
override fun mouseScrolledInner(x: Double, y: Double, scroll: Double): Boolean {
|
||||
return viewScrollBar.mouseScrolledInner(x, y, scroll)
|
||||
}
|
||||
|
||||
override fun mouseClickedInner(x: Double, y: Double, button: Int): Boolean {
|
||||
menu.networkedItemView.mouseClick(index, button)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun innerRender(graphics: GuiGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {
|
||||
renderSlotBackground(graphics, mouseX, mouseY, partialTick)
|
||||
|
||||
val itemstack = menu.networkedItemView.sortedView.getOrNull(index)?.stack ?: StorageStack.ITEMS.empty
|
||||
val stack = graphics.pose()
|
||||
|
||||
renderRegular(graphics, itemstack.toItemStack(), "")
|
||||
|
||||
if (!itemstack.isEmpty) {
|
||||
stack.pushPose()
|
||||
|
||||
val text = itemstack.count.formatSiComponent(decimalPlaces = 1)
|
||||
val textWidth = font.width(text)
|
||||
|
||||
stack.translate((width - textWidth * FONT_SCALE).toDouble(), (height - font.lineHeight * FONT_SCALE).toDouble(), 103.0)
|
||||
|
||||
stack.scale(FONT_SCALE, FONT_SCALE, 1f)
|
||||
|
||||
RenderSystem.depthFunc(GL11.GL_ALWAYS)
|
||||
|
||||
graphics.drawString(font, text, 1, 1, 0x0)
|
||||
graphics.drawString(font, text, 0, 0, 16777215)
|
||||
|
||||
stack.popPose()
|
||||
controls.sortingButtons(menu.settings::ascendingSort.asGetterSetter(), menu.settings::sorting.asGetterSetter(), ItemStorageStackSorter.DEFAULT) {
|
||||
for (v in ItemStorageStackSorter.entries) {
|
||||
add(v, skinElement = v.icon, tooltip = v.title)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemStackTooltip(stack: ItemStack): List<Component> {
|
||||
return super.getItemStackTooltip(stack).also {
|
||||
it as MutableList<Component>
|
||||
val realStack = menu.networkedItemView.sortedView.getOrNull(index)!!.stack
|
||||
it.add(TranslatableComponent("otm.gui.stored_amount", realStack.count.formatReadableNumber()).withStyle(ChatFormatting.DARK_GRAY))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
val grid = NetworkedItemGridPanel(this, topPanel, menu.networkedItemView)
|
||||
grid.dock = Dock.FILL
|
||||
|
||||
val craftingGrid = GridPanel(this, bottomPanel, width = 3 * 18f, height = 3 * 18f, columns = 3, rows = 3)
|
||||
craftingGrid.dock = Dock.LEFT
|
||||
@ -142,19 +84,19 @@ class ItemMonitorScreen(menu: ItemMonitorMenu, inventory: Inventory, title: Comp
|
||||
|
||||
val arrowLine = EditablePanel(this, arrowAndButtons, y = 38f, height = 8f, width = arrowAndButtons.width)
|
||||
|
||||
val refillPriority = SmallEnumRectangleButtonPanel(this, arrowLine,
|
||||
SmallEnumRectangleButtonPanel(this, arrowLine,
|
||||
enum = ItemMonitorPlayerSettings.IngredientPriority::class.java,
|
||||
prop = menu.settings::ingredientPriority.asGetterSetter(watch = { _, _ -> menu.sendSettingsToServer() }),
|
||||
prop = menu.settings::ingredientPriority.asGetterSetter(),
|
||||
defaultValue = ItemMonitorPlayerSettings.IngredientPriority.SYSTEM)
|
||||
.also {
|
||||
it.tooltips.add(TranslatableComponent("otm.gui.item_monitor.refill_source.desc"))
|
||||
|
||||
refillPriority.tooltips.add(TranslatableComponent("otm.gui.item_monitor.refill_source.desc"))
|
||||
refillPriority.add(ItemMonitorPlayerSettings.IngredientPriority.SYSTEM, tooltip = ItemMonitorPlayerSettings.IngredientPriority.SYSTEM.component, skinElement = Widgets8.WHITE_ARROW_DOWN, winding = UVWindingOrder.FLIP)
|
||||
refillPriority.add(ItemMonitorPlayerSettings.IngredientPriority.INVENTORY, tooltip = ItemMonitorPlayerSettings.IngredientPriority.INVENTORY.component, skinElement = Widgets8.WHITE_ARROW_DOWN)
|
||||
refillPriority.add(ItemMonitorPlayerSettings.IngredientPriority.INVENTORY_FIRST, tooltip = ItemMonitorPlayerSettings.IngredientPriority.INVENTORY_FIRST.component, skinElement = Widgets8.ARROW_SIDEWAYS, winding = UVWindingOrder.FLIP)
|
||||
refillPriority.add(ItemMonitorPlayerSettings.IngredientPriority.SYSTEM_FIRST, tooltip = ItemMonitorPlayerSettings.IngredientPriority.SYSTEM_FIRST.component, skinElement = Widgets8.ARROW_SIDEWAYS)
|
||||
refillPriority.add(ItemMonitorPlayerSettings.IngredientPriority.DO_NOT, tooltip = ItemMonitorPlayerSettings.IngredientPriority.DO_NOT.component, skinElement = Widgets8.MINUS)
|
||||
for (setting in ItemMonitorPlayerSettings.IngredientPriority.entries) {
|
||||
it.add(setting, setting.icon, setting.component, setting.winding)
|
||||
}
|
||||
|
||||
refillPriority.dock = Dock.LEFT
|
||||
it.dock = Dock.LEFT
|
||||
}
|
||||
|
||||
val resultAndButtons = EditablePanel(this, bottomPanel, width = 18f)
|
||||
|
||||
@ -163,25 +105,29 @@ class ItemMonitorScreen(menu: ItemMonitorMenu, inventory: Inventory, title: Comp
|
||||
|
||||
SlotPanel(this, resultAndButtons, menu.craftingResult, y = 18f)
|
||||
|
||||
val resultTarget = SmallEnumRectangleButtonPanel(this, resultAndButtons, y = 38f,
|
||||
SmallEnumRectangleButtonPanel(this, resultAndButtons, y = 38f,
|
||||
enum = ItemMonitorPlayerSettings.ResultTarget::class.java,
|
||||
prop = menu.settings::resultTarget.asGetterSetter(watch = { _, _ -> menu.sendSettingsToServer() }),
|
||||
prop = menu.settings::resultTarget.asGetterSetter(),
|
||||
defaultValue = ItemMonitorPlayerSettings.ResultTarget.MIXED)
|
||||
.also {
|
||||
it.tooltips.add(TranslatableComponent("otm.gui.item_monitor.result_target.desc"))
|
||||
|
||||
resultTarget.tooltips.add(TranslatableComponent("otm.gui.item_monitor.result_target.desc"))
|
||||
resultTarget.add(ItemMonitorPlayerSettings.ResultTarget.MIXED, tooltip = ItemMonitorPlayerSettings.ResultTarget.MIXED.component, skinElement = Widgets8.ARROW_SIDEWAYS)
|
||||
resultTarget.add(ItemMonitorPlayerSettings.ResultTarget.ALL_INVENTORY, tooltip = ItemMonitorPlayerSettings.ResultTarget.ALL_INVENTORY.component, skinElement = Widgets8.ARROW_PAINTED_UP, winding = UVWindingOrder.FLIP)
|
||||
resultTarget.add(ItemMonitorPlayerSettings.ResultTarget.ALL_SYSTEM, tooltip = ItemMonitorPlayerSettings.ResultTarget.ALL_SYSTEM.component, skinElement = Widgets8.ARROW_PAINTED_UP)
|
||||
for (setting in ItemMonitorPlayerSettings.ResultTarget.entries) {
|
||||
it.add(setting, setting.icon, setting.component, setting.winding)
|
||||
}
|
||||
}
|
||||
|
||||
val craftingAmount = SmallEnumRectangleButtonPanel(this, resultAndButtons, x = 10f, y = 38f,
|
||||
SmallEnumRectangleButtonPanel(this, resultAndButtons, x = 10f, y = 38f,
|
||||
enum = ItemMonitorPlayerSettings.Amount::class.java,
|
||||
prop = menu.settings::craftingAmount.asGetterSetter(watch = { _, _ -> menu.sendSettingsToServer() }),
|
||||
prop = menu.settings::craftingAmount.asGetterSetter(),
|
||||
defaultValue = ItemMonitorPlayerSettings.Amount.STACK)
|
||||
.also {
|
||||
it.tooltips.add(TranslatableComponent("otm.gui.item_monitor.amount.desc"))
|
||||
|
||||
craftingAmount.tooltips.add(TranslatableComponent("otm.gui.item_monitor.amount.desc"))
|
||||
craftingAmount.add(ItemMonitorPlayerSettings.Amount.ONE, tooltip = ItemMonitorPlayerSettings.Amount.ONE.component, skinElement = Widgets8.ONE)
|
||||
craftingAmount.add(ItemMonitorPlayerSettings.Amount.STACK, tooltip = ItemMonitorPlayerSettings.Amount.STACK.component, skinElement = Widgets8.S)
|
||||
craftingAmount.add(ItemMonitorPlayerSettings.Amount.FULL, tooltip = ItemMonitorPlayerSettings.Amount.FULL.component, skinElement = Widgets8.F)
|
||||
for (setting in ItemMonitorPlayerSettings.Amount.entries) {
|
||||
it.add(setting, setting.icon, setting.component, setting.winding)
|
||||
}
|
||||
}
|
||||
|
||||
val craftingHistory = GridPanel(this, bottomPanel, width = 3 * 18f, height = 3 * 18f, columns = 3, rows = 3)
|
||||
craftingHistory.dock = Dock.LEFT
|
||||
@ -227,7 +173,5 @@ class ItemMonitorScreen(menu: ItemMonitorMenu, inventory: Inventory, title: Comp
|
||||
companion object {
|
||||
const val ITEM_GRID_WIDTH = 9
|
||||
const val ITEM_GRID_HEIGHT = 5
|
||||
|
||||
const val FONT_SCALE = 0.6f
|
||||
}
|
||||
}
|
||||
|
@ -139,10 +139,10 @@ object MachinesConfig : AbstractConfig("machines") {
|
||||
}
|
||||
|
||||
val STORAGE_POWER_SUPPLIER = conciseValues(MNames.STORAGE_POWER_SUPPLIER, Decimal(80_000), Decimal(2_000))
|
||||
val STORAGE_INTERFACES = conciseValues("STORAGE_INTERFACES", Decimal(10_000), Decimal(100))
|
||||
val ITEM_MONITOR = conciseValues(MNames.ITEM_MONITOR, Decimal(10_000), Decimal(100))
|
||||
val DRIVE_VIEWER = conciseValues(MNames.DRIVE_VIEWER, Decimal(10_000), Decimal(100))
|
||||
val DRIVE_RACK = conciseValues(MNames.DRIVE_RACK, Decimal(10_000), Decimal(100))
|
||||
val STORAGE_INTERFACES = conciseValues("STORAGE_INTERFACES", Decimal(10_000), Decimal(10_000))
|
||||
val ITEM_MONITOR = conciseValues(MNames.ITEM_MONITOR, Decimal(10_000), Decimal(10_000))
|
||||
val DRIVE_VIEWER = conciseValues(MNames.DRIVE_VIEWER, Decimal(10_000), Decimal(10_000))
|
||||
val DRIVE_RACK = conciseValues(MNames.DRIVE_RACK, Decimal(10_000), Decimal(10_000))
|
||||
|
||||
object AndroidCharger {
|
||||
val RADIUS_WIDTH: Double by builder
|
||||
|
@ -12,6 +12,7 @@ import net.minecraft.world.Container
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import ru.dbotthepony.mc.otm.core.GetterSetter
|
||||
import ru.dbotthepony.mc.otm.core.collect.concatIterators
|
||||
import ru.dbotthepony.mc.otm.core.collect.filter
|
||||
import ru.dbotthepony.mc.otm.core.collect.flatMap
|
||||
import ru.dbotthepony.mc.otm.core.collect.map
|
||||
@ -167,10 +168,10 @@ class CombinedContainer(containers: Stream<Pair<Container, Iterator<Int>>>) : Co
|
||||
}
|
||||
|
||||
fun optimizedIterator(): Iterator<ItemStack> {
|
||||
return listOf(
|
||||
return concatIterators(
|
||||
fullCoverage.iterator().flatMap { it.iterator() },
|
||||
notFullCoverage.values.iterator().flatMap { it.iterator() }.map { it.item }.filter { it.isNotEmpty }
|
||||
).iterator().flatMap { it }
|
||||
)
|
||||
}
|
||||
|
||||
class Builder {
|
||||
|
@ -1,5 +1,6 @@
|
||||
package ru.dbotthepony.mc.otm.container
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.ObjectIterators
|
||||
import net.minecraft.world.Container
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import ru.dbotthepony.mc.otm.core.isNotEmpty
|
||||
@ -50,3 +51,32 @@ fun Container.iterator(): IContainerIterator {
|
||||
ContainerIterator(this)
|
||||
}
|
||||
}
|
||||
|
||||
class FullContainerIterator(val container: Container, initialPos: Int = 0) : MutableIterator<ItemStack>, ObjectIterators.AbstractIndexBasedListIterator<ItemStack>(0, initialPos), IContainerIterator {
|
||||
override fun remove(location: Int) {
|
||||
pos++
|
||||
container[location] = ItemStack.EMPTY
|
||||
}
|
||||
|
||||
override fun get(location: Int): ItemStack {
|
||||
return container[location]
|
||||
}
|
||||
|
||||
override fun getMaxPos(): Int {
|
||||
return container.containerSize
|
||||
}
|
||||
|
||||
override fun add(location: Int, k: ItemStack?) {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun set(location: Int, k: ItemStack) {
|
||||
container[location] = k
|
||||
}
|
||||
|
||||
override fun setChanged() {
|
||||
container.setChanged()
|
||||
}
|
||||
}
|
||||
|
||||
fun Container.fullIterator() = FullContainerIterator(this)
|
||||
|
@ -53,6 +53,7 @@ import java.util.Arrays
|
||||
import java.util.Spliterators
|
||||
import java.util.UUID
|
||||
import java.util.function.Consumer
|
||||
import java.util.function.Supplier
|
||||
import java.util.stream.Stream
|
||||
import java.util.stream.StreamSupport
|
||||
import kotlin.reflect.KProperty
|
||||
@ -367,12 +368,20 @@ fun <T> Comparator<in T>.nullsLast(): Comparator<T?> {
|
||||
return Comparator.nullsLast(this as Comparator<in T?>)
|
||||
}
|
||||
|
||||
fun <A, B> Comparator<A>.map(mapper: (B) -> A): Comparator<B> {
|
||||
return Comparator { a, b -> this@map.compare(mapper.invoke(a), mapper.invoke(b)) }
|
||||
}
|
||||
|
||||
fun <T> Comparator<T>.suppliers(): Comparator<Supplier<T>> {
|
||||
return Comparator { o1, o2 -> this@suppliers.compare(o1.get(), o2.get()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
fun <E> List<E>.searchInsertionIndex(element: E, comparator: Comparator<in 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" }
|
||||
@ -421,7 +430,7 @@ fun <E> List<E>.searchInsertionIndex(element: E, comparator: Comparator<E>, from
|
||||
*
|
||||
* If [MutableList] is not sorted, result of this function is undefined
|
||||
*/
|
||||
fun <E> MutableList<E>.addSorted(element: E, comparator: Comparator<E>) {
|
||||
fun <E> MutableList<E>.addSorted(element: E, comparator: Comparator<in E>) {
|
||||
add(searchInsertionIndex(element, comparator), element)
|
||||
}
|
||||
|
||||
@ -433,3 +442,8 @@ fun <E> MutableList<E>.addSorted(element: E, comparator: Comparator<E>) {
|
||||
fun <E : Comparable<E>> MutableList<E>.addSorted(element: E) {
|
||||
add(searchInsertionIndex(element, ObjectComparators.NATURAL_COMPARATOR), element)
|
||||
}
|
||||
|
||||
fun <A, B> lazy2(a: () -> A, b: A.() -> B): Supplier<B> {
|
||||
val first = lazy(a)
|
||||
return Supplier { b.invoke(first.value) }
|
||||
}
|
||||
|
@ -93,8 +93,14 @@ interface GetterSetter<V> : Supplier<V>, Consumer<V>, ReadWriteProperty<Any?, V>
|
||||
}
|
||||
}
|
||||
|
||||
fun <V> box(value: V): GetterSetter<V> {
|
||||
return object : GetterSetter<V> {
|
||||
fun <V> box(value: V): SentientGetterSetter<V> {
|
||||
return object : SentientGetterSetter<V> {
|
||||
private val subs = ISubscriptable.Impl<V>()
|
||||
|
||||
override fun addListener(listener: Consumer<V>): ISubscriptable.L {
|
||||
return subs.addListener(listener)
|
||||
}
|
||||
|
||||
private var value = value
|
||||
|
||||
override fun get(): V {
|
||||
@ -103,12 +109,15 @@ interface GetterSetter<V> : Supplier<V>, Consumer<V>, ReadWriteProperty<Any?, V>
|
||||
|
||||
override fun accept(t: V) {
|
||||
this.value = t
|
||||
subs.accept(t)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface SentientGetterSetter<V> : GetterSetter<V>, ISubscriptable<V>
|
||||
|
||||
operator fun <T> Supplier<T>.getValue(thisRef: Any?, property: KProperty<*>): T {
|
||||
return get()
|
||||
}
|
||||
|
213
src/main/kotlin/ru/dbotthepony/mc/otm/core/ISubscripable.kt
Normal file
213
src/main/kotlin/ru/dbotthepony/mc/otm/core/ISubscripable.kt
Normal file
@ -0,0 +1,213 @@
|
||||
package ru.dbotthepony.mc.otm.core
|
||||
|
||||
import it.unimi.dsi.fastutil.booleans.BooleanConsumer
|
||||
import it.unimi.dsi.fastutil.floats.FloatConsumer
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet
|
||||
import java.util.function.Consumer
|
||||
import java.util.function.DoubleConsumer
|
||||
import java.util.function.IntConsumer
|
||||
import java.util.function.LongConsumer
|
||||
|
||||
interface ISubscriptable<V> {
|
||||
/**
|
||||
* Listener token, allows to remove listener from subscriber list
|
||||
*/
|
||||
fun interface L {
|
||||
/**
|
||||
* Removes this listener
|
||||
*/
|
||||
fun remove()
|
||||
}
|
||||
|
||||
fun addListener(listener: Consumer<V>): L
|
||||
|
||||
class Impl<V> : ISubscriptable<V>, Consumer<V> {
|
||||
private inner class L(val callback: Consumer<V>) : ISubscriptable.L {
|
||||
init {
|
||||
subscribers.add(this)
|
||||
}
|
||||
|
||||
override fun remove() {
|
||||
subscribers.remove(this)
|
||||
}
|
||||
}
|
||||
|
||||
private val subscribers = ReferenceLinkedOpenHashSet<L>(0)
|
||||
|
||||
override fun addListener(listener: Consumer<V>): ISubscriptable.L {
|
||||
return L(listener)
|
||||
}
|
||||
|
||||
override fun accept(t: V) {
|
||||
subscribers.forEach { it.callback.accept(t) }
|
||||
}
|
||||
}
|
||||
|
||||
companion object : ISubscriptable<Nothing>, L {
|
||||
@Suppress("unchecked_cast")
|
||||
fun <T> empty(): ISubscriptable<T> {
|
||||
return this as ISubscriptable<T>
|
||||
}
|
||||
|
||||
override fun remove() {}
|
||||
|
||||
override fun addListener(listener: Consumer<Nothing>): L {
|
||||
return this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface IFloatSubcripable : ISubscriptable<Float> {
|
||||
@Deprecated("Use type specific listener")
|
||||
override fun addListener(listener: Consumer<Float>): ISubscriptable.L {
|
||||
return addListener(listener::accept)
|
||||
}
|
||||
|
||||
fun addListener(listener: FloatConsumer): ISubscriptable.L
|
||||
|
||||
class Impl : IFloatSubcripable, Consumer<Float>, FloatConsumer {
|
||||
private inner class L(val callback: FloatConsumer) : ISubscriptable.L {
|
||||
init {
|
||||
subscribers.add(this)
|
||||
}
|
||||
|
||||
override fun remove() {
|
||||
subscribers.remove(this)
|
||||
}
|
||||
}
|
||||
|
||||
private val subscribers = ReferenceLinkedOpenHashSet<L>(0)
|
||||
|
||||
override fun addListener(listener: FloatConsumer): ISubscriptable.L {
|
||||
return L(listener)
|
||||
}
|
||||
|
||||
override fun accept(t: Float) {
|
||||
subscribers.forEach { it.callback.accept(t) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface IDoubleSubcripable : ISubscriptable<Double> {
|
||||
@Deprecated("Use type specific listener")
|
||||
override fun addListener(listener: Consumer<Double>): ISubscriptable.L {
|
||||
return addListener(DoubleConsumer { listener.accept(it) })
|
||||
}
|
||||
|
||||
fun addListener(listener: DoubleConsumer): ISubscriptable.L
|
||||
|
||||
class Impl : IDoubleSubcripable, Consumer<Double>, DoubleConsumer {
|
||||
private inner class L(val callback: DoubleConsumer) : ISubscriptable.L {
|
||||
init {
|
||||
subscribers.add(this)
|
||||
}
|
||||
|
||||
override fun remove() {
|
||||
subscribers.remove(this)
|
||||
}
|
||||
}
|
||||
|
||||
private val subscribers = ReferenceLinkedOpenHashSet<L>(0)
|
||||
|
||||
override fun addListener(listener: DoubleConsumer): ISubscriptable.L {
|
||||
return L(listener)
|
||||
}
|
||||
|
||||
override fun accept(t: Double) {
|
||||
subscribers.forEach { it.callback.accept(t) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface IIntSubcripable : ISubscriptable<Int> {
|
||||
@Deprecated("Use type specific listener")
|
||||
override fun addListener(listener: Consumer<Int>): ISubscriptable.L {
|
||||
return addListener(IntConsumer { listener.accept(it) })
|
||||
}
|
||||
|
||||
fun addListener(listener: IntConsumer): ISubscriptable.L
|
||||
|
||||
class Impl : IIntSubcripable, Consumer<Int>, IntConsumer {
|
||||
private inner class L(val callback: IntConsumer) : ISubscriptable.L {
|
||||
init {
|
||||
subscribers.add(this)
|
||||
}
|
||||
|
||||
override fun remove() {
|
||||
subscribers.remove(this)
|
||||
}
|
||||
}
|
||||
|
||||
private val subscribers = ReferenceLinkedOpenHashSet<L>(0)
|
||||
|
||||
override fun addListener(listener: IntConsumer): ISubscriptable.L {
|
||||
return L(listener)
|
||||
}
|
||||
|
||||
override fun accept(t: Int) {
|
||||
subscribers.forEach { it.callback.accept(t) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface ILongSubcripable : ISubscriptable<Long> {
|
||||
@Deprecated("Use type specific listener")
|
||||
override fun addListener(listener: Consumer<Long>): ISubscriptable.L {
|
||||
return addListener(LongConsumer { listener.accept(it) })
|
||||
}
|
||||
|
||||
fun addListener(listener: LongConsumer): ISubscriptable.L
|
||||
|
||||
class Impl : ILongSubcripable, Consumer<Long>, LongConsumer {
|
||||
private inner class L(val callback: LongConsumer) : ISubscriptable.L {
|
||||
init {
|
||||
subscribers.add(this)
|
||||
}
|
||||
|
||||
override fun remove() {
|
||||
subscribers.remove(this)
|
||||
}
|
||||
}
|
||||
|
||||
private val subscribers = ReferenceLinkedOpenHashSet<L>(0)
|
||||
|
||||
override fun addListener(listener: LongConsumer): ISubscriptable.L {
|
||||
return L(listener)
|
||||
}
|
||||
|
||||
override fun accept(t: Long) {
|
||||
subscribers.forEach { it.callback.accept(t) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface IBooleanSubscriptable : ISubscriptable<Boolean> {
|
||||
@Deprecated("Use type specific listener")
|
||||
override fun addListener(listener: Consumer<Boolean>): ISubscriptable.L {
|
||||
return addListener(listener::accept)
|
||||
}
|
||||
|
||||
fun addListener(listener: BooleanConsumer): ISubscriptable.L
|
||||
|
||||
class Impl : IBooleanSubscriptable, Consumer<Boolean>, BooleanConsumer {
|
||||
private inner class L(val callback: BooleanConsumer) : ISubscriptable.L {
|
||||
init {
|
||||
subscribers.add(this)
|
||||
}
|
||||
|
||||
override fun remove() {
|
||||
subscribers.remove(this)
|
||||
}
|
||||
}
|
||||
|
||||
private val subscribers = ReferenceLinkedOpenHashSet<L>(0)
|
||||
|
||||
override fun addListener(listener: BooleanConsumer): ISubscriptable.L {
|
||||
return L(listener)
|
||||
}
|
||||
|
||||
override fun accept(t: Boolean) {
|
||||
subscribers.forEach { it.callback.accept(t) }
|
||||
}
|
||||
}
|
||||
}
|
@ -251,7 +251,7 @@ fun <T> Iterator<T>.mapToDouble(mapper: O2DFunction<T>): it.unimi.dsi.fastutil.d
|
||||
}
|
||||
|
||||
override fun remove() {
|
||||
throw UnsupportedOperationException()
|
||||
(this@mapToDouble as MutableIterator).remove()
|
||||
}
|
||||
|
||||
override fun nextDouble(): Double {
|
||||
@ -267,7 +267,7 @@ fun <T> Iterator<T>.mapToInt(mapper: O2IFunction<T>): it.unimi.dsi.fastutil.ints
|
||||
}
|
||||
|
||||
override fun remove() {
|
||||
throw UnsupportedOperationException()
|
||||
(this@mapToInt as MutableIterator).remove()
|
||||
}
|
||||
|
||||
override fun nextInt(): Int {
|
||||
@ -362,7 +362,7 @@ fun <T, A, R> Iterator<T>.collect(collector: Collector<T, A, R>): R {
|
||||
return collector.finisher().apply(instance)
|
||||
}
|
||||
|
||||
fun <T> Iterator<T>.toList(): List<T> {
|
||||
fun <T> Iterator<T>.toList(): MutableList<T> {
|
||||
val result = ArrayList<T>()
|
||||
result.addAll(this)
|
||||
return result
|
||||
|
@ -71,7 +71,12 @@ inline fun <R, reified T : Tag> CompoundTag.mapPresent(key: String, consumer: (T
|
||||
|
||||
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)
|
||||
|
||||
return try {
|
||||
mapper.invoke(tag.asString)
|
||||
} catch (err: NoSuchElementException) {
|
||||
orElse
|
||||
}
|
||||
}
|
||||
|
||||
fun CompoundTag.getItemStack(key: String): ItemStack = map(key, ItemStack::of) ?: ItemStack.EMPTY
|
||||
|
@ -96,6 +96,9 @@ fun BigInteger.formatSiComponent(suffix: Any = "", decimalPlaces: Int = 3, forma
|
||||
buffer[add + i + divided.length + 1] = prefix.paddedIndex(remainder, i)
|
||||
}
|
||||
|
||||
if (suffix == "")
|
||||
return TranslatableComponent(prefix.conciseFormatLocaleKey, String(buffer))
|
||||
else
|
||||
return TranslatableComponent(prefix.formatLocaleKey, String(buffer), suffix)
|
||||
}
|
||||
|
||||
|
@ -3,17 +3,31 @@ 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.network.chat.MutableComponent
|
||||
import net.minecraft.world.item.CreativeModeTabs
|
||||
import net.minecraft.world.item.Item
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraftforge.common.CreativeModeTabRegistry
|
||||
import ru.dbotthepony.mc.otm.client.minecraft
|
||||
import ru.dbotthepony.mc.otm.client.render.IGUIRenderable
|
||||
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
|
||||
import ru.dbotthepony.mc.otm.core.suppliers
|
||||
import ru.dbotthepony.mc.otm.matter.MatterManager
|
||||
import ru.dbotthepony.mc.otm.storage.ItemStorageStack
|
||||
import ru.dbotthepony.mc.otm.client.render.Widgets18
|
||||
|
||||
object CreativeMenuComparator : Comparator<Item> {
|
||||
private fun Comparator<Item?>.stacks(): Comparator<ItemStack?> {
|
||||
return Comparator<ItemStack> { o1, o2 -> this@stacks.compare(o1.item, o2.item) }.nullsFirst()
|
||||
}
|
||||
|
||||
private fun Comparator<Item?>.storage(): Comparator<ItemStorageStack?> {
|
||||
return Comparator<ItemStorageStack> { o1, o2 -> this@storage.compare(o1.item, o2.item) }.nullsFirst()
|
||||
}
|
||||
|
||||
object CreativeMenuItemComparator : Comparator<Item> {
|
||||
override fun compare(o1: Item, o2: Item): Int {
|
||||
rebuild()
|
||||
return item2index.getInt(o1).compareTo(item2index.getInt(o2))
|
||||
@ -84,6 +98,24 @@ object ItemLocalizedNameComparator : Comparator<Item> {
|
||||
val NullsLast = nullsLast()
|
||||
}
|
||||
|
||||
object ItemStackNameComparator : Comparator<ItemStack> {
|
||||
override fun compare(o1: ItemStack, o2: ItemStack): Int {
|
||||
return o1.hoverName.string.compareTo(o2.hoverName.string)
|
||||
}
|
||||
|
||||
val NullsFirst = nullsFirst()
|
||||
val NullsLast = nullsLast()
|
||||
}
|
||||
|
||||
object ItemStorageStackNameComparator : Comparator<ItemStorageStack> {
|
||||
override fun compare(o1: ItemStorageStack, o2: ItemStorageStack): Int {
|
||||
return o1.hoverName.string.compareTo(o2.hoverName.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
|
||||
@ -106,14 +138,65 @@ object ItemIDComparator : Comparator<Item> {
|
||||
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")),
|
||||
MATTER_VALUE(MatterValueComparator.NullsFirst.thenComparing(MatterComplexityComparator.NullsFirst).thenComparing(CreativeMenuComparator.NullsFirst), TranslatableComponent("otm.gui.sorting.matter_value")),
|
||||
MATTER_COMPLEXITY(MatterComplexityComparator.NullsFirst.thenComparing(MatterValueComparator.NullsFirst).thenComparing(CreativeMenuComparator.NullsFirst), TranslatableComponent("otm.gui.sorting.matter_complexity")),
|
||||
object ItemStackCountComparator : Comparator<ItemStack> {
|
||||
override fun compare(o1: ItemStack, o2: ItemStack): Int {
|
||||
return o1.count.compareTo(o2.count)
|
||||
}
|
||||
|
||||
val NullsFirst = nullsFirst()
|
||||
val NullsLast = nullsLast()
|
||||
}
|
||||
|
||||
object ItemStorageStackCountComparator : Comparator<ItemStorageStack> {
|
||||
override fun compare(o1: ItemStorageStack, o2: ItemStorageStack): Int {
|
||||
return o1.count.compareTo(o2.count)
|
||||
}
|
||||
|
||||
val NullsFirst = nullsFirst()
|
||||
val NullsLast = nullsLast()
|
||||
}
|
||||
|
||||
enum class ItemSorter(comparator: Comparator<Item>, private val sTitle: Component, icon: Lazy<IGUIRenderable>) : Comparator<Item?> by comparator.nullsFirst() {
|
||||
DEFAULT(CreativeMenuItemComparator, TranslatableComponent("otm.gui.sorting.default"), lazy { Widgets18.SORT_DEFAULT }),
|
||||
NAME(ItemLocalizedNameComparator.thenComparing(CreativeMenuItemComparator), TranslatableComponent("otm.gui.sorting.name"), lazy { Widgets18.SORT_ALPHABET }),
|
||||
ID(ItemIDComparator.thenComparing(CreativeMenuItemComparator), TranslatableComponent("otm.gui.sorting.id"), lazy { Widgets18.SORT_ID }),
|
||||
MOD(ItemModComparator.thenComparing(CreativeMenuItemComparator), TranslatableComponent("otm.gui.sorting.modid"), lazy { Widgets18.SORT_MODID }),
|
||||
MATTER_VALUE(MatterValueComparator.thenComparing(MatterComplexityComparator).thenComparing(CreativeMenuItemComparator), TranslatableComponent("otm.gui.sorting.matter_value"), lazy { Widgets18.SORT_MATTER_VALUE }),
|
||||
MATTER_COMPLEXITY(MatterComplexityComparator.thenComparing(MatterValueComparator).thenComparing(CreativeMenuItemComparator), TranslatableComponent("otm.gui.sorting.matter_complexity"), lazy { Widgets18.SORT_MATTER_COMPLEXITY }),
|
||||
;
|
||||
|
||||
val title: Component get() = sTitle.copy()
|
||||
val icon: IGUIRenderable by icon
|
||||
val title: MutableComponent get() = sTitle.copy()
|
||||
val suppliers = suppliers()
|
||||
val reversed: Comparator<Item?> = reversed()
|
||||
}
|
||||
|
||||
enum class ItemStackSorter(comparator: Comparator<ItemStack?>, private val sTitle: Component, icon: Lazy<IGUIRenderable>) : Comparator<ItemStack?> by comparator {
|
||||
DEFAULT(ItemSorter.DEFAULT.stacks(), ItemSorter.DEFAULT.title, lazy { Widgets18.SORT_DEFAULT }),
|
||||
COUNT(ItemStackCountComparator.NullsFirst.thenComparing(ItemSorter.DEFAULT.stacks()), TranslatableComponent("otm.gui.sorting.count"), lazy { Widgets18.SORT_COUNT }),
|
||||
NAME(ItemStackNameComparator.NullsFirst.thenComparing(ItemSorter.DEFAULT.stacks()), ItemSorter.NAME.title, lazy { Widgets18.SORT_ALPHABET }),
|
||||
ID(ItemSorter.ID.stacks(), ItemSorter.ID.title, lazy { Widgets18.SORT_ID }),
|
||||
MOD(ItemSorter.MOD.stacks(), ItemSorter.MOD.title, lazy { Widgets18.SORT_MODID }),
|
||||
MATTER_VALUE(ItemSorter.MATTER_VALUE.stacks(), ItemSorter.MATTER_VALUE.title, lazy { Widgets18.SORT_MATTER_VALUE }),
|
||||
MATTER_COMPLEXITY(ItemSorter.MATTER_COMPLEXITY.stacks(), ItemSorter.MATTER_COMPLEXITY.title, lazy { Widgets18.SORT_MATTER_COMPLEXITY });
|
||||
|
||||
val icon: IGUIRenderable by icon
|
||||
val title: MutableComponent get() = sTitle.copy()
|
||||
val suppliers = suppliers()
|
||||
val reversed: Comparator<ItemStack?> = reversed()
|
||||
}
|
||||
|
||||
enum class ItemStorageStackSorter(comparator: Comparator<ItemStorageStack?>, private val sTitle: Component, icon: Lazy<IGUIRenderable>) : Comparator<ItemStorageStack?> by comparator {
|
||||
DEFAULT(ItemSorter.DEFAULT.storage(), ItemSorter.DEFAULT.title, lazy { Widgets18.SORT_DEFAULT }),
|
||||
COUNT(ItemStorageStackCountComparator.NullsFirst.thenComparing(ItemSorter.DEFAULT.storage()), TranslatableComponent("otm.gui.sorting.count"), lazy { Widgets18.SORT_COUNT }),
|
||||
NAME(ItemStorageStackNameComparator.NullsFirst.thenComparing(ItemSorter.DEFAULT.storage()), ItemSorter.NAME.title, lazy { Widgets18.SORT_ALPHABET }),
|
||||
ID(ItemSorter.ID.storage(), ItemSorter.ID.title, lazy { Widgets18.SORT_ID }),
|
||||
MOD(ItemSorter.MOD.storage(), ItemSorter.MOD.title, lazy { Widgets18.SORT_MODID }),
|
||||
MATTER_VALUE(ItemSorter.MATTER_VALUE.storage(), ItemSorter.MATTER_VALUE.title, lazy { Widgets18.SORT_MATTER_VALUE }),
|
||||
MATTER_COMPLEXITY(ItemSorter.MATTER_COMPLEXITY.storage(), ItemSorter.MATTER_COMPLEXITY.title, lazy { Widgets18.SORT_MATTER_COMPLEXITY });
|
||||
|
||||
val icon: IGUIRenderable by icon
|
||||
val title: MutableComponent get() = sTitle.copy()
|
||||
val suppliers = suppliers()
|
||||
val reversed: Comparator<ItemStorageStack?> = reversed()
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ enum class SiPrefix(val power: Int, val symbol: String) {
|
||||
open val isEmpty: Boolean get() = false
|
||||
|
||||
val formatLocaleKey = "otm.suffix.${name.lowercase()}".intern()
|
||||
val conciseFormatLocaleKey = "otm.suffix_concise.${name.lowercase()}".intern()
|
||||
val rawLocaleKey = "otm.suffix_raw.${name.lowercase()}".intern()
|
||||
|
||||
val string: String
|
||||
|
@ -1,5 +1,6 @@
|
||||
package ru.dbotthepony.mc.otm.core.util
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import ru.dbotthepony.mc.otm.core.addSorted
|
||||
|
||||
@ -15,6 +16,7 @@ class TickList : ITickable {
|
||||
private val toRemoveFromAlways = ArrayList<ITickable>()
|
||||
|
||||
private val timers = ArrayDeque<Timer>()
|
||||
private val namedTimers = Object2ObjectOpenHashMap<Any, Timer>(0)
|
||||
|
||||
var inTicker = false
|
||||
private set
|
||||
@ -42,6 +44,20 @@ class TickList : ITickable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calling this method while timer already exists removes old timer
|
||||
*/
|
||||
fun namedTimer(name: Any?, ticks: Int, callback: Runnable): Timer {
|
||||
val remove = namedTimers.remove(name)
|
||||
|
||||
if (remove != null)
|
||||
timers.remove(remove)
|
||||
|
||||
val timer = Timer(ticks, callback)
|
||||
namedTimers[name] = timer
|
||||
return timer
|
||||
}
|
||||
|
||||
inner class Ticker(parent: ITickable) : ITickable by parent {
|
||||
init {
|
||||
add(this, always, alwaysQueued)
|
||||
|
@ -1,5 +1,6 @@
|
||||
package ru.dbotthepony.mc.otm.graph.storage
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
||||
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
|
||||
@ -13,7 +14,7 @@ import java.util.*
|
||||
import java.util.function.Supplier
|
||||
|
||||
open class StorageNode(private val energyDemander: IMatteryEnergyStorage? = null) : GraphNode<StorageNode, StorageGraph>(::StorageGraph) {
|
||||
protected val components = ArrayList<IStorage<*>>()
|
||||
protected val components = ObjectArraySet<IStorage<*>>()
|
||||
|
||||
/**
|
||||
* Setting this to true will render non functional default [attachComponents],
|
||||
@ -74,32 +75,15 @@ open class StorageNode(private val energyDemander: IMatteryEnergyStorage? = null
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("unchecked_cast")
|
||||
fun <T : StorageStack<T>, U : IStorage<T>> computeIfAbsent(type: StorageStack.Type<T>, provider: (StorageStack.Type<T>) -> U): U {
|
||||
return components.firstOrNull { it.storageType == type } as U? ?: provider(type).also { addStorageComponent(it) }
|
||||
}
|
||||
|
||||
fun addStorageComponent(component: IStorage<*>) {
|
||||
if (!components.any { component === it || it.storageType === component.storageType }) {
|
||||
components.add(component)
|
||||
|
||||
if (components.add(component))
|
||||
if (isValid && !manualAttaching)
|
||||
graph.add(component)
|
||||
}
|
||||
}
|
||||
|
||||
fun removeStorageComponent(component: IStorage<*>) {
|
||||
val indexOf = components.indexOfFirst { component === it || it.storageType === component.storageType }
|
||||
if (indexOf == -1) return
|
||||
val self = components.removeAt(indexOf)
|
||||
if (isValid) graph.remove(self)
|
||||
}
|
||||
|
||||
fun removeStorageComponent(component: StorageStack.Type<*>) {
|
||||
val indexOf = components.indexOfFirst { it.storageType === component }
|
||||
if (indexOf == -1) return
|
||||
val self = components.removeAt(indexOf)
|
||||
if (isValid) graph.remove(self)
|
||||
if (components.remove(component) && isValid)
|
||||
graph.remove(component)
|
||||
}
|
||||
|
||||
override fun invalidate() {
|
||||
|
@ -54,6 +54,7 @@ import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
|
||||
import ru.dbotthepony.mc.otm.network.MatteryPacket
|
||||
import ru.dbotthepony.mc.otm.network.MenuFieldPacket
|
||||
import ru.dbotthepony.mc.otm.network.MenuNetworkChannel
|
||||
import ru.dbotthepony.mc.otm.network.SetCarriedPacket
|
||||
import ru.dbotthepony.mc.otm.network.enqueueWork
|
||||
import ru.dbotthepony.mc.otm.network.packetHandled
|
||||
import ru.dbotthepony.mc.otm.network.sender
|
||||
@ -461,6 +462,16 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
|
||||
return player.distanceToSqr(pos.x.toDouble() + 0.5, pos.y.toDouble() + 0.5, pos.z.toDouble() + 0.5) <= 64.0
|
||||
}
|
||||
|
||||
fun syncCarried() {
|
||||
setRemoteCarried(carried.copy())
|
||||
MenuNetworkChannel.send(ply as ServerPlayer, SetCarriedPacket(carried))
|
||||
}
|
||||
|
||||
fun syncCarried(stack: ItemStack) {
|
||||
carried = stack
|
||||
syncCarried()
|
||||
}
|
||||
|
||||
private val externalSlots = ConditionalSet<Slot>()
|
||||
private val quickMoveMapping = Reference2ObjectOpenHashMap<Slot, ReferenceArrayList<Collection<Slot>>>()
|
||||
|
||||
@ -655,9 +666,9 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
|
||||
// first pass - stack with existing slots
|
||||
if (copy.isStackable) {
|
||||
for (slot in slots) {
|
||||
if (onlyFiltered && (slot !is UserFilteredSlot || slot.filter == null || slot.filter!!.get() != item.item)) {
|
||||
if (onlyFiltered && (slot !is UserFilteredSlot || !slot.test(item))) {
|
||||
continue
|
||||
} else if (!onlyFiltered && slot is UserFilteredSlot && slot.filter != null && slot.filter!!.get() != null && slot.filter!!.get() != item.item) {
|
||||
} else if (!onlyFiltered && slot is UserFilteredSlot && !slot.test(item)) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@ import ru.dbotthepony.mc.otm.container.UpgradeContainer
|
||||
import ru.dbotthepony.mc.otm.core.GetterSetter
|
||||
import ru.dbotthepony.mc.otm.core.immutableList
|
||||
import ru.dbotthepony.mc.otm.runOnClient
|
||||
import java.util.function.Predicate
|
||||
|
||||
/**
|
||||
* Make slots for single container
|
||||
@ -46,7 +47,7 @@ open class MatterySlot(container: Container, index: Int, x: Int = 0, y: Int = 0)
|
||||
}
|
||||
}
|
||||
|
||||
open class UserFilteredSlot(container: Container, index: Int, x: Int = 0, y: Int = 0) : MatterySlot(container, index, x, y) {
|
||||
open class UserFilteredSlot(container: Container, index: Int, x: Int = 0, y: Int = 0) : MatterySlot(container, index, x, y), Predicate<ItemStack> {
|
||||
var hasSetFilter = false
|
||||
private set
|
||||
|
||||
@ -60,6 +61,10 @@ open class UserFilteredSlot(container: Container, index: Int, x: Int = 0, y: Int
|
||||
return filter?.get() == null
|
||||
}
|
||||
|
||||
override fun test(t: ItemStack): Boolean {
|
||||
return filter?.get() == null || filter?.get() == t.item
|
||||
}
|
||||
|
||||
fun isSameFilter(other: Slot): Boolean {
|
||||
if (other !is UserFilteredSlot)
|
||||
return filter?.get() == null
|
||||
|
@ -2,6 +2,7 @@ package ru.dbotthepony.mc.otm.menu.data
|
||||
|
||||
import com.mojang.blaze3d.platform.InputConstants
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.screens.Screen
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
@ -13,21 +14,28 @@ import net.minecraft.world.item.ItemStack
|
||||
import net.minecraftforge.network.NetworkEvent
|
||||
import net.minecraftforge.network.PacketDistributor
|
||||
import ru.dbotthepony.mc.otm.client.minecraft
|
||||
import ru.dbotthepony.mc.otm.core.addSorted
|
||||
import ru.dbotthepony.mc.otm.core.isNotEmpty
|
||||
import ru.dbotthepony.mc.otm.core.map
|
||||
import ru.dbotthepony.mc.otm.core.readBigInteger
|
||||
import ru.dbotthepony.mc.otm.core.writeBigInteger
|
||||
import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
||||
import ru.dbotthepony.mc.otm.network.*
|
||||
import ru.dbotthepony.mc.otm.core.registryName
|
||||
import ru.dbotthepony.mc.otm.core.util.ItemStorageStackSorter
|
||||
import ru.dbotthepony.mc.otm.storage.*
|
||||
import java.math.BigInteger
|
||||
import java.util.*
|
||||
import java.util.function.Supplier
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
interface INetworkedItemViewProvider {
|
||||
val networkedItemView: NetworkedItemView
|
||||
}
|
||||
|
||||
class ItemViewInteractPacket(val stackID: Int, val type: ClickType, val action: ClickAction) : MatteryPacket {
|
||||
private fun all(stack: ItemStack): Int = stack.maxStackSize.coerceAtMost(stack.count)
|
||||
private fun half(stack: ItemStack): Int = (stack.maxStackSize.coerceAtMost(stack.count) / 2).coerceAtLeast(1)
|
||||
|
||||
data class ItemViewInteractPacket(val stackID: Int, val type: ClickType, val action: ClickAction) : MatteryPacket {
|
||||
override fun write(buff: FriendlyByteBuf) {
|
||||
buff.writeInt(stackID)
|
||||
buff.writeEnum(type)
|
||||
@ -53,144 +61,88 @@ class ItemViewInteractPacket(val stackID: Int, val type: ClickType, val action:
|
||||
}
|
||||
}
|
||||
|
||||
object ClearItemViewPacket : MatteryPacket {
|
||||
override fun write(buff: FriendlyByteBuf) {
|
||||
// NO-OP
|
||||
}
|
||||
|
||||
override fun play(context: Supplier<NetworkEvent.Context>) {
|
||||
abstract class NetworkedItemViewPacket : MatteryPacket {
|
||||
final override fun play(context: Supplier<NetworkEvent.Context>) {
|
||||
context.packetHandled = true
|
||||
context.enqueueWork {
|
||||
(minecraft.player?.containerMenu as? INetworkedItemViewProvider)?.networkedItemView?.clear()
|
||||
val get = Minecraft.getInstance().player?.containerMenu ?: return@enqueueWork
|
||||
val view = (get as? INetworkedItemViewProvider)?.networkedItemView ?: throw IllegalStateException("No NetworkedItemView is present in currently open menu")
|
||||
action(view)
|
||||
}
|
||||
}
|
||||
|
||||
fun read(buff: FriendlyByteBuf): ClearItemViewPacket {
|
||||
return ClearItemViewPacket
|
||||
}
|
||||
protected abstract fun action(view: NetworkedItemView)
|
||||
}
|
||||
|
||||
fun send(ply: ServerPlayer) {
|
||||
MenuNetworkChannel.send(ply, this)
|
||||
object ClearItemViewPacket : NetworkedItemViewPacket() {
|
||||
override fun write(buff: FriendlyByteBuf) {}
|
||||
|
||||
override fun action(view: NetworkedItemView) {
|
||||
view.clear()
|
||||
}
|
||||
}
|
||||
|
||||
class StackAddPacket(val containerId: Int, val id: Int, val stack: ItemStorageStack) : MatteryPacket {
|
||||
class StackAddPacket(val stackId: Int, val stack: ItemStorageStack) : NetworkedItemViewPacket() {
|
||||
override fun write(buff: FriendlyByteBuf) {
|
||||
buff.writeInt(containerId)
|
||||
buff.writeInt(id)
|
||||
buff.writeInt(stackId)
|
||||
stack.write(buff)
|
||||
}
|
||||
|
||||
override fun play(context: Supplier<NetworkEvent.Context>) {
|
||||
context.get().packetHandled = true
|
||||
context.get().enqueueWork {
|
||||
val get = Minecraft.getInstance().player?.containerMenu ?: return@enqueueWork
|
||||
|
||||
if (get.containerId != containerId)
|
||||
return@enqueueWork
|
||||
|
||||
val view = (get as? INetworkedItemViewProvider)?.networkedItemView ?: throw IllegalStateException("No such item tracker with id $containerId")
|
||||
|
||||
if (view.localState.containsKey(id)) {
|
||||
throw IllegalStateException("Item tracker $containerId already has stack with id of $id")
|
||||
override fun action(view: NetworkedItemView) {
|
||||
if (view.id2tuple.containsKey(stackId)) {
|
||||
throw IllegalStateException("NetworkedItemView $view already contains stack with id $stackId")
|
||||
}
|
||||
|
||||
val state = NetworkedItemView.NetworkedItem(id, stack)
|
||||
view.localState[id] = state
|
||||
|
||||
/*val iterator = view.sortedView.iterator().withIndex()
|
||||
var lastCompare = 0
|
||||
var hit = false
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
val (i, existing) = iterator.next()
|
||||
val cmp = view.sorter.compare(existing.stack, stack)
|
||||
|
||||
if (cmp != lastCompare && lastCompare != 0) {
|
||||
hit = true
|
||||
view.sortedView.add(i, state)
|
||||
} else if (cmp != lastCompare) {
|
||||
lastCompare = cmp
|
||||
}
|
||||
}
|
||||
|
||||
if (!hit) {*/
|
||||
view.sortedView.add(state)
|
||||
//}
|
||||
|
||||
view.resort()
|
||||
}
|
||||
val tuple = NetworkedItemView.Tuple(stackId, stack)
|
||||
view.id2tuple[stackId] = tuple
|
||||
view.sortedView.addSorted(tuple, view.sorter.map(NetworkedItemView.Tuple::stack))
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun read(buffer: FriendlyByteBuf): StackAddPacket {
|
||||
val containerId = buffer.readInt()
|
||||
val id = buffer.readInt()
|
||||
val item = StorageStack.ITEMS.read(buffer)
|
||||
return StackAddPacket(containerId, id, item)
|
||||
return StackAddPacket(id, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class StackChangePacket(val id: Int, val stackID: Int, val newCount: BigInteger) : MatteryPacket {
|
||||
class StackChangePacket(val stackId: Int, val newCount: BigInteger) : NetworkedItemViewPacket() {
|
||||
override fun write(buff: FriendlyByteBuf) {
|
||||
buff.writeInt(id)
|
||||
buff.writeInt(stackID)
|
||||
buff.writeInt(stackId)
|
||||
buff.writeBigInteger(newCount)
|
||||
}
|
||||
|
||||
override fun play(context: Supplier<NetworkEvent.Context>) {
|
||||
context.get().packetHandled = true
|
||||
context.get().enqueueWork {
|
||||
val get = Minecraft.getInstance().player?.containerMenu ?: return@enqueueWork
|
||||
|
||||
if (get.containerId != id)
|
||||
return@enqueueWork
|
||||
|
||||
val view = (get as? INetworkedItemViewProvider)?.networkedItemView ?: throw IllegalStateException("No such item tracker with id $id")
|
||||
val state = view.localState[stackID] ?: throw IllegalStateException("No such stack with id $stackID in $view")
|
||||
|
||||
state.stack = state.stack.copy(newCount)
|
||||
override fun action(view: NetworkedItemView) {
|
||||
val tuple = view.id2tuple[stackId] ?: throw IllegalStateException("No such stack with id $stackId in $view")
|
||||
tuple.stack = tuple.stack.copy(newCount)
|
||||
view.resort()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun read(buffer: FriendlyByteBuf): StackChangePacket {
|
||||
val id = buffer.readInt()
|
||||
val stackID = buffer.readInt()
|
||||
val newCount = buffer.readBigInteger()
|
||||
return StackChangePacket(id, stackID, newCount)
|
||||
return StackChangePacket(stackID, newCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class StackRemovePacket(val id: Int, val stackID: Int) : MatteryPacket {
|
||||
class StackRemovePacket(val stackId: Int) : NetworkedItemViewPacket() {
|
||||
override fun write(buff: FriendlyByteBuf) {
|
||||
buff.writeInt(id)
|
||||
buff.writeInt(stackID)
|
||||
buff.writeInt(stackId)
|
||||
}
|
||||
|
||||
override fun play(context: Supplier<NetworkEvent.Context>) {
|
||||
context.get().packetHandled = true
|
||||
context.get().enqueueWork {
|
||||
val get = Minecraft.getInstance().player?.containerMenu ?: return@enqueueWork
|
||||
|
||||
if (get.containerId != id)
|
||||
return@enqueueWork
|
||||
|
||||
val view = (get as? INetworkedItemViewProvider)?.networkedItemView ?: throw IllegalStateException("No such item tracker with id $id")
|
||||
val obj = view.localState.remove(stackID) ?: throw IllegalStateException("No such stack with id $stackID in $view")
|
||||
override fun action(view: NetworkedItemView) {
|
||||
val obj = view.id2tuple.remove(stackId) ?: throw IllegalStateException("No such stack with id $stackId in $view")
|
||||
view.sortedView.remove(obj)
|
||||
view.resort()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun read(buffer: FriendlyByteBuf): StackRemovePacket {
|
||||
val id = buffer.readInt()
|
||||
val stackID = buffer.readInt()
|
||||
return StackRemovePacket(id, stackID)
|
||||
return StackRemovePacket(stackID)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -198,129 +150,105 @@ class StackRemovePacket(val id: Int, val stackID: Int) : MatteryPacket {
|
||||
/**
|
||||
* Creates a virtual, slotless container for Player to interaction with.
|
||||
*/
|
||||
open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote: Boolean) : IStorageEventConsumer<ItemStorageStack> {
|
||||
data class NetworkedItem(val id: Int, var stack: ItemStorageStack, val upstreamId: UUID? = null)
|
||||
class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val isRemote: Boolean) : IStorageEventConsumer<ItemStorageStack> {
|
||||
data class Tuple(val networkId: Int, var stack: ItemStorageStack, val upstreamId: UUID? = null) : Supplier<ItemStorageStack> {
|
||||
override fun get(): ItemStorageStack {
|
||||
return stack
|
||||
}
|
||||
}
|
||||
|
||||
override val storageType: StorageStack.Type<ItemStorageStack>
|
||||
get() = StorageStack.ITEMS
|
||||
|
||||
// this (how client see and interact with)
|
||||
val localState = Int2ObjectOpenHashMap<NetworkedItem>()
|
||||
|
||||
val sortedView = LinkedList<NetworkedItem>()
|
||||
var sorter: Comparator<ItemStack> = NAME_SORTER
|
||||
|
||||
companion object {
|
||||
val NAME_SORTER = Comparator<ItemStack> { o1, o2 ->
|
||||
val cmp = o1.displayName.string.compareTo(o2.displayName.string)
|
||||
|
||||
if (cmp != 0)
|
||||
return@Comparator cmp
|
||||
|
||||
return@Comparator o1.item.registryName.toString().compareTo(o2.item.registryName.toString())
|
||||
}
|
||||
|
||||
val COUNT_SORTER = Comparator<ItemStack> { o1, o2 ->
|
||||
val cmp = o1.count.compareTo(o2.count)
|
||||
|
||||
if (cmp != 0)
|
||||
return@Comparator cmp
|
||||
|
||||
return@Comparator o1.item.registryName.toString().compareTo(o2.item.registryName.toString())
|
||||
val id2tuple = Int2ObjectOpenHashMap<Tuple>()
|
||||
val sortedView = ArrayList<Tuple>()
|
||||
var sorter: Comparator<in ItemStorageStack> = ItemStorageStackSorter.DEFAULT
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
field = value
|
||||
resort()
|
||||
}
|
||||
}
|
||||
|
||||
val itemCount get() = id2tuple.size
|
||||
|
||||
private var nextItemID = 0
|
||||
private val uuid2tuple = Object2ObjectOpenHashMap<UUID, Tuple>()
|
||||
private val networkBacklog = ArrayList<Any>()
|
||||
|
||||
operator fun get(id: Int): Tuple? = id2tuple[id]
|
||||
|
||||
fun resort() {
|
||||
sortedView.sortWith { a, b ->
|
||||
return@sortWith sorter.compare(a.stack.toItemStack(), b.stack.toItemStack())
|
||||
if (isRemote) {
|
||||
sortedView.sortWith(sorter.map(Tuple::stack))
|
||||
}
|
||||
}
|
||||
|
||||
// parent (e.g. VirtualComponent)
|
||||
protected val upstreamState = HashMap<UUID, NetworkedItem>()
|
||||
protected val networkBacklog = ArrayList<Any>()
|
||||
var component: IStorageComponent<ItemStorageStack>? = null
|
||||
set(provider) {
|
||||
if (provider === field) return
|
||||
|
||||
operator fun get(id: Int): NetworkedItem? = localState[id]
|
||||
|
||||
var provider: IStorageComponent<ItemStorageStack>? = null
|
||||
private set
|
||||
|
||||
fun mouseClick(index: Int, mouseButton: Int) {
|
||||
if (minecraft.player?.isSpectator == true) {
|
||||
return
|
||||
}
|
||||
|
||||
val list = sortedView
|
||||
|
||||
val action =
|
||||
if (mouseButton == InputConstants.MOUSE_BUTTON_LEFT) ClickAction.PRIMARY else ClickAction.SECONDARY
|
||||
|
||||
val type =
|
||||
if (mouseButton == InputConstants.MOUSE_BUTTON_MIDDLE) ClickType.CLONE else if (Screen.hasShiftDown()) ClickType.QUICK_MOVE else ClickType.PICKUP
|
||||
|
||||
MenuNetworkChannel.sendToServer(
|
||||
ItemViewInteractPacket(if (index >= list.size) -1 else list[index].id, type, action)
|
||||
)
|
||||
}
|
||||
|
||||
fun setComponent(provider: IStorageComponent<ItemStorageStack>?) {
|
||||
if (provider === this.provider) return
|
||||
|
||||
this.provider?.removeListenerAndNotify(this)
|
||||
this.provider = provider
|
||||
field?.removeListenerAndNotify(this)
|
||||
field = provider
|
||||
provider?.addListenerAndNotify(this)
|
||||
}
|
||||
|
||||
fun removed() {
|
||||
provider?.removeListenerAndNotify(this)
|
||||
fun mouseClick(index: Int, mouseButton: Int) {
|
||||
if (minecraft.player?.isSpectator == true) return
|
||||
|
||||
MenuNetworkChannel.sendToServer(ItemViewInteractPacket(
|
||||
sortedView.getOrNull(index)?.networkId ?: -1,
|
||||
if (mouseButton == InputConstants.MOUSE_BUTTON_MIDDLE) ClickType.CLONE else if (Screen.hasShiftDown()) ClickType.QUICK_MOVE else ClickType.PICKUP,
|
||||
if (mouseButton == InputConstants.MOUSE_BUTTON_LEFT) ClickAction.PRIMARY else ClickAction.SECONDARY
|
||||
))
|
||||
}
|
||||
|
||||
val itemCount get() = localState.values.size
|
||||
fun removed() {
|
||||
component?.removeListenerAndNotify(this)
|
||||
}
|
||||
|
||||
override fun onStackAdded(stack: ItemStorageStack, id: UUID, provider: IStorageProvider<ItemStorageStack>) {
|
||||
check(!upstreamState.containsKey(id)) { "Already tracking ItemStack with upstream id $id!" }
|
||||
check(!uuid2tuple.containsKey(id)) { "Already tracking ItemStack with upstream id $id!" }
|
||||
|
||||
val state = NetworkedItem(nextItemID++, stack, id)
|
||||
val state = Tuple(nextItemID++, stack, id)
|
||||
|
||||
this.localState[state.id] = state
|
||||
upstreamState[id] = state
|
||||
network { StackAddPacket(menu.containerId, state.id, state.stack) }
|
||||
this.id2tuple[state.networkId] = state
|
||||
uuid2tuple[id] = state
|
||||
network { StackAddPacket(state.networkId, state.stack) }
|
||||
}
|
||||
|
||||
override fun onStackChanged(stack: ItemStorageStack, id: UUID) {
|
||||
val get = upstreamState[id] ?: throw IllegalStateException("Unknown ItemStack with upstream id $id!")
|
||||
val get = uuid2tuple[id] ?: throw IllegalStateException("Unknown ItemStack with upstream id $id!")
|
||||
get.stack = stack
|
||||
network { StackChangePacket(menu.containerId, get.id, stack.count) }
|
||||
network { StackChangePacket(get.networkId, stack.count) }
|
||||
}
|
||||
|
||||
protected fun network(fn: () -> Any) {
|
||||
if (!remote) {
|
||||
override fun onStackRemoved(id: UUID) {
|
||||
val get = uuid2tuple[id] ?: throw IllegalStateException("Unknown ItemStack with upstream id $id!")
|
||||
uuid2tuple.remove(id)
|
||||
id2tuple.remove(get.networkId)
|
||||
network { StackRemovePacket(get.networkId) }
|
||||
}
|
||||
|
||||
private inline fun network(fn: () -> Any) {
|
||||
if (!isRemote) {
|
||||
networkBacklog.add(fn())
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStackRemoved(id: UUID) {
|
||||
val get = upstreamState[id] ?: throw IllegalStateException("Unknown ItemStack with upstream id $id!")
|
||||
upstreamState.remove(id)
|
||||
localState.remove(get.id)
|
||||
network { StackRemovePacket(menu.containerId, get.id) }
|
||||
}
|
||||
|
||||
protected var nextItemID = 0
|
||||
|
||||
fun clear() {
|
||||
sortedView.clear()
|
||||
upstreamState.clear()
|
||||
localState.clear()
|
||||
uuid2tuple.clear()
|
||||
id2tuple.clear()
|
||||
|
||||
if (!remote) {
|
||||
if (!isRemote) {
|
||||
networkBacklog.clear()
|
||||
networkBacklog.add(ClearItemViewPacket)
|
||||
}
|
||||
}
|
||||
|
||||
fun network() {
|
||||
check(!remote) { "Not a server" }
|
||||
check(!isRemote) { "Not a server" }
|
||||
val consumer = PacketDistributor.PLAYER.with { ply as ServerPlayer }
|
||||
|
||||
for (packet in networkBacklog) {
|
||||
@ -331,82 +259,41 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
|
||||
}
|
||||
|
||||
fun playerInteract(packet: ItemViewInteractPacket) {
|
||||
val provider = provider ?: return
|
||||
|
||||
val click = packet.type
|
||||
val action = packet.action
|
||||
val stackId = packet.stackID
|
||||
|
||||
if (click == ClickType.CLONE) {
|
||||
if (stackId < 0 || !ply.abilities.instabuild) return
|
||||
|
||||
val state = get(stackId) ?: return
|
||||
val copy = state.stack.toItemStack().also { it.count = it.maxStackSize }
|
||||
|
||||
ply.containerMenu.carried = copy
|
||||
MenuNetworkChannel.send(ply as ServerPlayer, SetCarriedPacket(ply.containerMenu.carried))
|
||||
ply.containerMenu.setRemoteCarried(ply.containerMenu.carried.copy())
|
||||
val component = component ?: return
|
||||
val (stackId, type, action) = packet
|
||||
|
||||
if (type == ClickType.CLONE) {
|
||||
if (!ply.abilities.instabuild) return
|
||||
menu.syncCarried((get(stackId) ?: return).stack.toItemStack().also { it.count = it.maxStackSize })
|
||||
return
|
||||
}
|
||||
|
||||
if (click == ClickType.QUICK_MOVE && stackId > -1) {
|
||||
val state = get(stackId) ?: return
|
||||
val stack = state.stack.toItemStack()
|
||||
// забираем из системы с зажатым shift
|
||||
if (type == ClickType.QUICK_MOVE) {
|
||||
val tuple = get(stackId) ?: return
|
||||
val stack = tuple.stack.toItemStack()
|
||||
|
||||
val amount =
|
||||
if (action == ClickAction.PRIMARY)
|
||||
stack.maxStackSize
|
||||
else
|
||||
1.coerceAtLeast(stack.maxStackSize / 2)
|
||||
|
||||
val extracted = provider.extractStack(state.upstreamId!!, amount.toBigInteger(), true)
|
||||
|
||||
if (!extracted.isEmpty) {
|
||||
val remaining = menu.quickMoveToInventory(extracted.toItemStack(), false)
|
||||
|
||||
if (remaining.count != extracted.count.toInt()) {
|
||||
provider.extractStack(state.upstreamId, (extracted.count.toInt() - remaining.count).toBigInteger(), false)
|
||||
}
|
||||
}
|
||||
var amount = if (action == ClickAction.PRIMARY) all(stack) else half(stack)
|
||||
amount -= menu.quickMoveToInventory(tuple.stack.toItemStack(amount), true).count
|
||||
if (amount == 0) return
|
||||
|
||||
menu.quickMoveToInventory(component.extractStack(tuple.upstreamId!!, amount.toBigInteger(), false).toItemStack(), false)
|
||||
return
|
||||
}
|
||||
|
||||
if (!menu.carried.isEmpty && click != ClickType.QUICK_MOVE) {
|
||||
// try to put
|
||||
if (menu.carried.isNotEmpty) {
|
||||
if (action == ClickAction.PRIMARY) {
|
||||
val carried = menu.carried
|
||||
val amount = carried.count
|
||||
|
||||
if (amount == carried.count) {
|
||||
menu.carried = provider.insertStack(ItemStorageStack(menu.carried), false).toItemStack()
|
||||
MenuNetworkChannel.send(ply as ServerPlayer, SetCarriedPacket(menu.carried))
|
||||
menu.setRemoteCarried(menu.carried.copy())
|
||||
}
|
||||
} else {
|
||||
val copy = menu.carried.copy()
|
||||
copy.count = 1
|
||||
|
||||
if (provider.insertStack(ItemStorageStack(copy), false).isEmpty) {
|
||||
menu.syncCarried(component.insertStack(ItemStorageStack(menu.carried), false).toItemStack())
|
||||
} else if (component.insertStack(ItemStorageStack(menu.carried.copyWithCount(1)), false).isEmpty) {
|
||||
menu.carried.shrink(1)
|
||||
MenuNetworkChannel.send(ply as ServerPlayer, SetCarriedPacket(menu.carried))
|
||||
menu.setRemoteCarried(menu.carried.copy())
|
||||
}
|
||||
menu.syncCarried()
|
||||
}
|
||||
} else if (stackId > -1) {
|
||||
val state = get(stackId) ?: return
|
||||
val stack = state.stack.toItemStack()
|
||||
|
||||
val amount =
|
||||
if (action == ClickAction.PRIMARY)
|
||||
stack.maxStackSize
|
||||
else
|
||||
(stack.count / 2).coerceAtMost(stack.maxStackSize / 2).coerceAtLeast(1)
|
||||
|
||||
menu.carried = provider.extractStack(state.upstreamId!!, amount.toBigInteger(), false).toItemStack()
|
||||
MenuNetworkChannel.send(ply as ServerPlayer, SetCarriedPacket(menu.carried))
|
||||
menu.setRemoteCarried(menu.carried.copy())
|
||||
val amount = if (action == ClickAction.PRIMARY) all(stack) else half(stack)
|
||||
menu.carried = component.extractStack(state.upstreamId!!, amount.toBigInteger(), false).toItemStack()
|
||||
menu.syncCarried()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,10 @@ package ru.dbotthepony.mc.otm.menu.input
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import ru.dbotthepony.mc.otm.client.minecraft
|
||||
import ru.dbotthepony.mc.otm.core.GetterSetter
|
||||
import ru.dbotthepony.mc.otm.core.ISubscriptable
|
||||
import ru.dbotthepony.mc.otm.core.SentientGetterSetter
|
||||
import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
||||
import ru.dbotthepony.mc.otm.network.synchronizer.IField
|
||||
import java.util.function.Consumer
|
||||
import java.util.function.Predicate
|
||||
import java.util.function.Supplier
|
||||
@ -14,33 +17,7 @@ import kotlin.reflect.KMutableProperty0
|
||||
*
|
||||
* Getting and setting values should ONLY be done clientside
|
||||
*/
|
||||
interface IPlayerInputWithFeedback<V> : GetterSetter<V>, Predicate<Player?> {
|
||||
companion object {
|
||||
fun <V> of(getterSetter: GetterSetter<V>): IPlayerInputWithFeedback<V> {
|
||||
return object : IPlayerInputWithFeedback<V>, GetterSetter<V> by getterSetter {
|
||||
override fun test(t: Player?): Boolean {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun <V> of(getterSetter: GetterSetter<V>, filter: Predicate<Player?>): IPlayerInputWithFeedback<V> {
|
||||
return object : IPlayerInputWithFeedback<V>, GetterSetter<V> by getterSetter, Predicate<Player?> by filter {}
|
||||
}
|
||||
|
||||
fun <V> validPlayer(getterSetter: GetterSetter<V>): IPlayerInputWithFeedback<V> {
|
||||
return object : IPlayerInputWithFeedback<V>, GetterSetter<V> by getterSetter {
|
||||
override fun test(t: Player?): Boolean {
|
||||
return t != null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun <V> GetterSetter<V>.wrapAsPlayerInput(filter: Predicate<Player?> = Predicate { it != null }): IPlayerInputWithFeedback<V> {
|
||||
return IPlayerInputWithFeedback.of(this, filter)
|
||||
}
|
||||
interface IPlayerInputWithFeedback<V> : SentientGetterSetter<V>, Predicate<Player?>
|
||||
|
||||
/**
|
||||
* Represents Server to Client synchronization and Client to Server input
|
||||
@ -49,13 +26,19 @@ fun <V> GetterSetter<V>.wrapAsPlayerInput(filter: Predicate<Player?> = Predicate
|
||||
*/
|
||||
abstract class AbstractPlayerInputWithFeedback<V> : IPlayerInputWithFeedback<V> {
|
||||
abstract val input: MatteryMenu.PlayerInput<V>
|
||||
abstract val value: V
|
||||
abstract val field: IField<V>
|
||||
|
||||
override fun get(): V {
|
||||
final override fun addListener(listener: Consumer<V>): ISubscriptable.L {
|
||||
return field.addListener(listener)
|
||||
}
|
||||
|
||||
val value: V get() = this.field.value
|
||||
|
||||
final override fun get(): V {
|
||||
return value
|
||||
}
|
||||
|
||||
override fun accept(t: V) {
|
||||
final override fun accept(t: V) {
|
||||
input.input(t)
|
||||
}
|
||||
|
||||
|
@ -5,15 +5,27 @@ import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
||||
import java.util.function.BooleanSupplier
|
||||
import kotlin.reflect.KMutableProperty0
|
||||
|
||||
class BooleanInputWithFeedback(menu: MatteryMenu) : AbstractPlayerInputWithFeedback<Boolean>() {
|
||||
override val input = menu.booleanInput { consumer?.invoke(it) }
|
||||
override val value by menu.mSynchronizer.computedBool(BooleanSupplier { supplier?.invoke() ?: false }).property
|
||||
class BooleanInputWithFeedback(menu: MatteryMenu, allowSpectators: Boolean = false) : AbstractPlayerInputWithFeedback<Boolean>() {
|
||||
override val input = menu.booleanInput(allowSpectators) { consumer?.invoke(it) }
|
||||
override val field = menu.mSynchronizer.computedBool(BooleanSupplier { supplier?.invoke() ?: false })
|
||||
|
||||
constructor(menu: MatteryMenu, state: KMutableProperty0<Boolean>) : this(menu) {
|
||||
constructor(menu: MatteryMenu, allowSpectators: Boolean, state: KMutableProperty0<Boolean>?) : this(menu, allowSpectators) {
|
||||
if (state != null)
|
||||
with(state)
|
||||
}
|
||||
|
||||
constructor(menu: MatteryMenu, state: GetterSetter<Boolean>) : this(menu) {
|
||||
constructor(menu: MatteryMenu, allowSpectators: Boolean, state: GetterSetter<Boolean>?) : this(menu, allowSpectators) {
|
||||
if (state != null)
|
||||
with(state)
|
||||
}
|
||||
|
||||
constructor(menu: MatteryMenu, state: KMutableProperty0<Boolean>?) : this(menu) {
|
||||
if (state != null)
|
||||
with(state)
|
||||
}
|
||||
|
||||
constructor(menu: MatteryMenu, state: GetterSetter<Boolean>?) : this(menu) {
|
||||
if (state != null)
|
||||
with(state)
|
||||
}
|
||||
|
||||
|
@ -9,12 +9,16 @@ inline fun <reified E : Enum<E>> EnumInputWithFeedback(menu: MatteryMenu) = Enum
|
||||
inline fun <reified E : Enum<E>> EnumInputWithFeedback(menu: MatteryMenu, state: KMutableProperty0<E>?) = EnumInputWithFeedback(menu, E::class.java, state)
|
||||
inline fun <reified E : Enum<E>> EnumInputWithFeedback(menu: MatteryMenu, state: GetterSetter<E>) = EnumInputWithFeedback(menu, E::class.java, state)
|
||||
|
||||
class EnumInputWithFeedback<E : Enum<E>>(menu: MatteryMenu, clazz: Class<E>) : AbstractPlayerInputWithFeedback<E>() {
|
||||
inline fun <reified E : Enum<E>> EnumInputWithFeedback(menu: MatteryMenu, allowSpectators: Boolean) = EnumInputWithFeedback(menu, E::class.java, allowSpectators)
|
||||
inline fun <reified E : Enum<E>> EnumInputWithFeedback(menu: MatteryMenu, allowSpectators: Boolean, state: KMutableProperty0<E>?) = EnumInputWithFeedback(menu, E::class.java, allowSpectators, state)
|
||||
inline fun <reified E : Enum<E>> EnumInputWithFeedback(menu: MatteryMenu, allowSpectators: Boolean, state: GetterSetter<E>) = EnumInputWithFeedback(menu, E::class.java, allowSpectators, state)
|
||||
|
||||
class EnumInputWithFeedback<E : Enum<E>>(menu: MatteryMenu, clazz: Class<E>, allowSpectators: Boolean = false) : AbstractPlayerInputWithFeedback<E>() {
|
||||
val codec = EnumValueCodec(clazz)
|
||||
private val default = codec.values.first()
|
||||
|
||||
override val input = menu.PlayerInput(codec, false) { consumer?.invoke(it) }
|
||||
override val value by menu.mSynchronizer.ComputedField(getter = { supplier?.invoke() ?: default }, codec)
|
||||
override val input = menu.PlayerInput(codec, allowSpectators) { consumer?.invoke(it) }
|
||||
override val field = menu.mSynchronizer.ComputedField(getter = { supplier?.invoke() ?: default }, codec)
|
||||
|
||||
constructor(menu: MatteryMenu, clazz: Class<E>, state: KMutableProperty0<E>?) : this(menu, clazz) {
|
||||
if (state != null) {
|
||||
@ -22,7 +26,21 @@ class EnumInputWithFeedback<E : Enum<E>>(menu: MatteryMenu, clazz: Class<E>) : A
|
||||
}
|
||||
}
|
||||
|
||||
constructor(menu: MatteryMenu, clazz: Class<E>, state: GetterSetter<E>) : this(menu, clazz) {
|
||||
constructor(menu: MatteryMenu, clazz: Class<E>, state: GetterSetter<E>?) : this(menu, clazz) {
|
||||
if (state != null) {
|
||||
with(state)
|
||||
}
|
||||
}
|
||||
|
||||
constructor(menu: MatteryMenu, clazz: Class<E>, allowSpectators: Boolean, state: KMutableProperty0<E>?) : this(menu, clazz, allowSpectators) {
|
||||
if (state != null) {
|
||||
with(state)
|
||||
}
|
||||
}
|
||||
|
||||
constructor(menu: MatteryMenu, clazz: Class<E>, allowSpectators: Boolean, state: GetterSetter<E>?) : this(menu, clazz, allowSpectators) {
|
||||
if (state != null) {
|
||||
with(state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import kotlin.reflect.KMutableProperty0
|
||||
|
||||
class StringInputWithFeedback(menu: MatteryMenu) : AbstractPlayerInputWithFeedback<String>() {
|
||||
override val input = menu.stringInput { consumer?.invoke(it.replace('\u0000', ' ')) }
|
||||
override val value by menu.mSynchronizer.computedString { supplier?.invoke() ?: "" }
|
||||
override val field = menu.mSynchronizer.computedString { supplier?.invoke() ?: "" }
|
||||
|
||||
constructor(menu: MatteryMenu, state: KMutableProperty0<String>) : this(menu) {
|
||||
with(state)
|
||||
|
@ -190,8 +190,8 @@ class MatterPanelMenu(
|
||||
val sortingGS = GetterSetter.of(::sorting, changeSorting::input)
|
||||
val isAscendingGS = GetterSetter.of(::isAscending, changeIsAscending::input)
|
||||
|
||||
private val actualComparator = Comparator<PatternState> { o1, o2 -> sorting.comparator.compare(o1.item, o2.item) * (if (isAscending) 1 else -1) }
|
||||
private val actualTaskComparator = Comparator<ReplicationTask> { o1, o2 -> sorting.comparator.compare(o1.item, o2.item) * (if (isAscending) 1 else -1) }
|
||||
private val actualComparator = Comparator<PatternState> { o1, o2 -> sorting.compare(o1.item, o2.item) * (if (isAscending) 1 else -1) }
|
||||
private val actualTaskComparator = Comparator<ReplicationTask> { o1, o2 -> sorting.compare(o1.item, o2.item) * (if (isAscending) 1 else -1) }
|
||||
|
||||
private val patterns = ArrayList<PatternState>()
|
||||
private val tasks = ArrayList<ReplicationTask>()
|
||||
|
@ -1,57 +1,67 @@
|
||||
package ru.dbotthepony.mc.otm.menu.storage
|
||||
|
||||
import com.google.common.collect.ImmutableList
|
||||
import net.minecraft.world.SimpleContainer
|
||||
import net.minecraft.world.entity.player.Inventory
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.inventory.Slot
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraftforge.common.capabilities.ForgeCapabilities
|
||||
import ru.dbotthepony.mc.otm.block.entity.storage.DriveViewerBlockEntity
|
||||
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
||||
import ru.dbotthepony.mc.otm.capability.drive.IMatteryDrive
|
||||
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
|
||||
import ru.dbotthepony.mc.otm.container.ItemFilter
|
||||
import ru.dbotthepony.mc.otm.core.ifPresentK
|
||||
import ru.dbotthepony.mc.otm.core.util.ItemStorageStackSorter
|
||||
import ru.dbotthepony.mc.otm.item.PortableCondensationDriveItem
|
||||
import ru.dbotthepony.mc.otm.menu.MatteryPoweredMenu
|
||||
import ru.dbotthepony.mc.otm.menu.MatterySlot
|
||||
import ru.dbotthepony.mc.otm.menu.data.INetworkedItemViewProvider
|
||||
import ru.dbotthepony.mc.otm.menu.data.NetworkedItemView
|
||||
import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback
|
||||
import ru.dbotthepony.mc.otm.menu.input.EnergyConfigPlayerInput
|
||||
import ru.dbotthepony.mc.otm.menu.input.EnumInputWithFeedback
|
||||
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
|
||||
import ru.dbotthepony.mc.otm.registry.MMenus
|
||||
import ru.dbotthepony.mc.otm.storage.ItemStorageStack
|
||||
import ru.dbotthepony.mc.otm.storage.StorageStack
|
||||
import ru.dbotthepony.mc.otm.storage.powered.PoweredVirtualComponent
|
||||
|
||||
class DriveViewerMenu @JvmOverloads constructor(
|
||||
class DriveViewerMenu(
|
||||
containerID: Int,
|
||||
inventory: Inventory,
|
||||
tile: DriveViewerBlockEntity? = null
|
||||
) : MatteryPoweredMenu(
|
||||
MMenus.DRIVE_VIEWER, containerID, inventory, tile
|
||||
), INetworkedItemViewProvider {
|
||||
) : MatteryPoweredMenu(MMenus.DRIVE_VIEWER, containerID, inventory, tile), INetworkedItemViewProvider {
|
||||
override val networkedItemView = NetworkedItemView(inventory.player, this, tile == null)
|
||||
val driveSlot: MatterySlot
|
||||
|
||||
private val powered: PoweredVirtualComponent<ItemStorageStack>?
|
||||
private var lastDrive: IMatteryDrive<ItemStorageStack>? = null
|
||||
|
||||
init {
|
||||
val container = tile?.container ?: SimpleContainer(1)
|
||||
|
||||
driveSlot = object : MatterySlot(container, 0) {
|
||||
val driveSlot = object : MatterySlot(tile?.container ?: SimpleContainer(1), 0) {
|
||||
override fun mayPlace(itemStack: ItemStack): Boolean {
|
||||
return itemStack.getCapability(MatteryCapability.DRIVE).isPresent
|
||||
}
|
||||
}
|
||||
|
||||
if (tile != null) {
|
||||
powered = PoweredVirtualComponent(
|
||||
StorageStack.ITEMS,
|
||||
tile.getCapability(MatteryCapability.ENERGY).resolve()::get
|
||||
)
|
||||
private val powered: PoweredVirtualComponent<ItemStorageStack>?
|
||||
private var lastDrive: IMatteryDrive<ItemStorageStack>? = null
|
||||
val profiledEnergy = ProfiledLevelGaugeWidget<ProfiledEnergyStorage<*>>(this, energyWidget)
|
||||
val energyConfig = EnergyConfigPlayerInput(this, tile?.energyConfig)
|
||||
|
||||
this.networkedItemView.setComponent(powered)
|
||||
val settings = object : DriveViewerBlockEntity.ISettings {
|
||||
override var sorting: ItemStorageStackSorter by EnumInputWithFeedback(this@DriveViewerMenu, true, tile?.let { it.getSettingsFor(ply)::sorting }).also { it.addListener { changes() } }
|
||||
override var isAscending: Boolean by BooleanInputWithFeedback(this@DriveViewerMenu, true, tile?.let { it.getSettingsFor(ply)::isAscending }).also { it.addListener { changes() } }
|
||||
|
||||
private fun changes() {
|
||||
if (isAscending) {
|
||||
networkedItemView.sorter = sorting
|
||||
} else {
|
||||
networkedItemView.sorter = sorting.reversed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
if (tile != null) {
|
||||
profiledEnergy.with(tile.energy)
|
||||
powered = PoweredVirtualComponent(StorageStack.ITEMS, tile::energy)
|
||||
this.networkedItemView.component = powered
|
||||
} else {
|
||||
powered = null
|
||||
}
|
||||
@ -126,12 +136,9 @@ class DriveViewerMenu @JvmOverloads constructor(
|
||||
}
|
||||
}
|
||||
|
||||
override fun removed(p_38940_: Player) {
|
||||
super.removed(p_38940_)
|
||||
|
||||
if (lastDrive != null)
|
||||
powered?.remove(lastDrive!!)
|
||||
|
||||
override fun removed(player: Player) {
|
||||
super.removed(player)
|
||||
lastDrive?.let { powered?.remove(it) }
|
||||
this.networkedItemView.removed()
|
||||
}
|
||||
|
||||
|
@ -6,15 +6,20 @@ import net.minecraft.world.SimpleContainer
|
||||
import net.minecraft.world.entity.player.Inventory
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraftforge.network.PacketDistributor
|
||||
import ru.dbotthepony.mc.otm.block.entity.storage.IItemMonitorPlayerSettings
|
||||
import ru.dbotthepony.mc.otm.block.entity.storage.ItemMonitorBlockEntity
|
||||
import ru.dbotthepony.mc.otm.block.entity.storage.ItemMonitorPlayerSettings
|
||||
import ru.dbotthepony.mc.otm.container.get
|
||||
import ru.dbotthepony.mc.otm.core.collect.mapToInt
|
||||
import ru.dbotthepony.mc.otm.core.collect.reduce
|
||||
import ru.dbotthepony.mc.otm.core.isNotEmpty
|
||||
import ru.dbotthepony.mc.otm.menu.MatteryPoweredMenu
|
||||
import ru.dbotthepony.mc.otm.menu.MatterySlot
|
||||
import ru.dbotthepony.mc.otm.menu.data.INetworkedItemViewProvider
|
||||
import ru.dbotthepony.mc.otm.menu.data.NetworkedItemView
|
||||
import ru.dbotthepony.mc.otm.network.MenuNetworkChannel
|
||||
import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback
|
||||
import ru.dbotthepony.mc.otm.menu.input.EnumInputWithFeedback
|
||||
import ru.dbotthepony.mc.otm.menu.makeSlots
|
||||
import ru.dbotthepony.mc.otm.registry.MMenus
|
||||
import ru.dbotthepony.mc.otm.storage.*
|
||||
import java.util.*
|
||||
@ -73,73 +78,85 @@ private class ResultSlot(container: Container) : MatterySlot(container, 0) {
|
||||
}
|
||||
}
|
||||
|
||||
class ItemMonitorMenu @JvmOverloads constructor(
|
||||
class ItemMonitorMenu(
|
||||
containerId: Int,
|
||||
inventory: Inventory,
|
||||
tile: ItemMonitorBlockEntity? = null
|
||||
) : MatteryPoweredMenu(MMenus.ITEM_MONITOR, containerId, inventory, tile), INetworkedItemViewProvider {
|
||||
override val networkedItemView = NetworkedItemView(inventory.player, this, tile == null)
|
||||
val settings: ItemMonitorPlayerSettings = tile?.getSettings(inventory.player as ServerPlayer) ?: ItemMonitorPlayerSettings()
|
||||
private var settingsNetworked = false
|
||||
|
||||
val settings = object : IItemMonitorPlayerSettings {
|
||||
override var ingredientPriority by EnumInputWithFeedback(this@ItemMonitorMenu, true, tile?.let { it.getSettings(ply as ServerPlayer)::ingredientPriority })
|
||||
override var resultTarget by EnumInputWithFeedback(this@ItemMonitorMenu, true, tile?.let { it.getSettings(ply as ServerPlayer)::resultTarget })
|
||||
override var craftingAmount by EnumInputWithFeedback(this@ItemMonitorMenu, true, tile?.let { it.getSettings(ply as ServerPlayer)::craftingAmount })
|
||||
override var sorting by EnumInputWithFeedback(this@ItemMonitorMenu, true, tile?.let { it.getSettings(ply as ServerPlayer)::sorting }).also { it.addListener { changes() } }
|
||||
override var ascendingSort by BooleanInputWithFeedback(this@ItemMonitorMenu, true, tile?.let { it.getSettings(ply as ServerPlayer)::ascendingSort }).also { it.addListener { changes() } }
|
||||
|
||||
private fun changes() {
|
||||
if (ascendingSort) {
|
||||
networkedItemView.sorter = sorting
|
||||
} else {
|
||||
networkedItemView.sorter = sorting.reversed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val craftingResult: MatterySlot
|
||||
val craftingSlots: List<MatterySlot>
|
||||
|
||||
init {
|
||||
if (tile != null) {
|
||||
networkedItemView.setComponent(tile.poweredView)
|
||||
craftingResult = ResultSlot(tile.craftingResultContainer)
|
||||
craftingSlots = Collections.unmodifiableList(Array(9) { MatterySlot(tile.craftingGrid, it) }.asList())
|
||||
} else {
|
||||
craftingResult = ResultSlot(SimpleContainer(1))
|
||||
val container = SimpleContainer(9)
|
||||
craftingSlots = Collections.unmodifiableList(Array(9) { MatterySlot(container, it) }.asList())
|
||||
networkedItemView.component = tile.poweredView
|
||||
}
|
||||
|
||||
val slots = craftingSlots.map(this::addSlot)
|
||||
craftingResult = ResultSlot(tile?.craftingResultContainer ?: SimpleContainer(1))
|
||||
craftingSlots = makeSlots(tile?.craftingGrid ?: SimpleContainer(9), ::MatterySlot)
|
||||
craftingSlots.forEach(this::addSlot)
|
||||
addSlot(craftingResult)
|
||||
|
||||
addInventorySlots()
|
||||
}
|
||||
|
||||
override fun broadcastFullState() {
|
||||
super.broadcastFullState()
|
||||
MenuNetworkChannel.send(PacketDistributor.PLAYER.with { ply as ServerPlayer }, settings)
|
||||
settingsNetworked = true
|
||||
}
|
||||
|
||||
fun sendSettingsToServer() {
|
||||
MenuNetworkChannel.sendToServer(settings)
|
||||
}
|
||||
|
||||
override fun removed(p_38940_: Player) {
|
||||
super.removed(p_38940_)
|
||||
override fun removed(player: Player) {
|
||||
super.removed(player)
|
||||
networkedItemView.removed()
|
||||
}
|
||||
|
||||
override fun broadcastChanges() {
|
||||
super.broadcastChanges()
|
||||
networkedItemView.network()
|
||||
|
||||
if (!settingsNetworked) {
|
||||
MenuNetworkChannel.send(PacketDistributor.PLAYER.with { ply as ServerPlayer }, settings)
|
||||
settingsNetworked = true
|
||||
}
|
||||
|
||||
private fun moveCrafting(view: IStorageComponent<ItemStorageStack>, simulate: Boolean): Boolean {
|
||||
val itemStack = craftingResult.item
|
||||
var remaining: ItemStack
|
||||
|
||||
if (settings.resultTarget == ItemMonitorPlayerSettings.ResultTarget.ALL_SYSTEM) {
|
||||
remaining = view.insertStack(ItemStorageStack(itemStack), simulate).toItemStack()
|
||||
remaining = moveItemStackToSlots(remaining, playerInventorySlots, simulate)
|
||||
} else {
|
||||
remaining = moveItemStackToSlots(itemStack, playerInventorySlots, simulate)
|
||||
remaining = view.insertStack(ItemStorageStack(remaining), simulate).toItemStack()
|
||||
}
|
||||
|
||||
if (!simulate && remaining.isNotEmpty) {
|
||||
ply.spawnAtLocation(remaining)
|
||||
}
|
||||
|
||||
if (!simulate) {
|
||||
craftingResult.remove(itemStack.count)
|
||||
}
|
||||
|
||||
return remaining.count != itemStack.count
|
||||
}
|
||||
|
||||
override fun quickMoveStack(ply: Player, slotIndex: Int): ItemStack {
|
||||
if (playerInventorySlots.any { it.index == slotIndex }) {
|
||||
if (tile == null) {
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
|
||||
if (tile == null) return ItemStack.EMPTY
|
||||
val slot = slots[slotIndex]
|
||||
if (slot.item.isEmpty) return ItemStack.EMPTY
|
||||
|
||||
if (slot.item.isEmpty) {
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
|
||||
val leftover = networkedItemView.provider?.insertStack(ItemStorageStack(slot.item), false)?.toItemStack() ?: slot.item
|
||||
val leftover = networkedItemView.component?.insertStack(ItemStorageStack(slot.item), false)?.toItemStack() ?: slot.item
|
||||
|
||||
if (leftover.count == slot.item.count) {
|
||||
return ItemStack.EMPTY
|
||||
@ -148,77 +165,48 @@ class ItemMonitorMenu @JvmOverloads constructor(
|
||||
val old = slot.item.copy()
|
||||
slot.item.count = leftover.count
|
||||
return old
|
||||
} else if (slotIndex in craftingSlots[0].index .. craftingSlots[craftingSlots.size - 1].index) {
|
||||
} else if (craftingSlots.any { it.index == slotIndex }) {
|
||||
// from crafting grid to inventory
|
||||
val item = slots[slotIndex].item
|
||||
if (item.isEmpty) return ItemStack.EMPTY
|
||||
|
||||
if (item.isEmpty) {
|
||||
return ItemStack.EMPTY
|
||||
var remainder = item
|
||||
|
||||
when (settings.ingredientPriority) {
|
||||
ItemMonitorPlayerSettings.IngredientPriority.SYSTEM, ItemMonitorPlayerSettings.IngredientPriority.SYSTEM_FIRST -> {
|
||||
remainder = networkedItemView.component?.insertStack(ItemStorageStack(remainder), false)?.toItemStack() ?: remainder
|
||||
}
|
||||
|
||||
var remainder = moveItemStackToSlots(item, playerInventorySlots)
|
||||
else -> {}
|
||||
}
|
||||
|
||||
remainder = moveItemStackToSlots(remainder, playerInventorySlots)
|
||||
|
||||
slots[slotIndex].set(remainder)
|
||||
|
||||
if (remainder.isEmpty) {
|
||||
return item
|
||||
}
|
||||
|
||||
remainder = networkedItemView.provider?.insertStack(ItemStorageStack(remainder), false)?.toItemStack() ?: remainder
|
||||
slots[slotIndex].set(remainder)
|
||||
|
||||
if (remainder.isEmpty) {
|
||||
return item
|
||||
}
|
||||
|
||||
return if (remainder.count != item.count) item else ItemStack.EMPTY
|
||||
} else if (slotIndex == craftingResult.index) {
|
||||
// quickcraft... god damn it
|
||||
if (!craftingResult.hasItem()) {
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
|
||||
val item = craftingResult.item
|
||||
|
||||
var item = craftingResult.item
|
||||
if (item.isEmpty) return ItemStack.EMPTY
|
||||
val tile = tile as ItemMonitorBlockEntity? ?: return ItemStack.EMPTY
|
||||
|
||||
if (tile.lastCraftingRecipe(ply) != null && tile.craftingRecipe != tile.lastCraftingRecipe(ply)) {
|
||||
// recipe has changed
|
||||
// рецепт изменился
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
|
||||
val crafted = tile.howMuchPlayerCrafted(ply)
|
||||
|
||||
if (settings.craftingAmount == ItemMonitorPlayerSettings.Amount.ONE) {
|
||||
if (tile.howMuchPlayerCrafted(ply) > 0) {
|
||||
if (crafted > 0) {
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
} else {
|
||||
var hasUnstackables = false
|
||||
var maxStack = 64
|
||||
|
||||
if (settings.craftingAmount == ItemMonitorPlayerSettings.Amount.FULL) {
|
||||
for (gridItem in tile.craftingGrid.iterator()) {
|
||||
if (!gridItem.isStackable) {
|
||||
hasUnstackables = true
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
maxStack = 0
|
||||
|
||||
for (gridItem in tile.craftingGrid.iterator()) {
|
||||
maxStack = maxStack.coerceAtLeast(gridItem.maxStackSize)
|
||||
}
|
||||
}
|
||||
|
||||
if (settings.craftingAmount == ItemMonitorPlayerSettings.Amount.STACK || hasUnstackables) {
|
||||
val count = tile.howMuchPlayerCrafted(ply)
|
||||
|
||||
if (count > 0 && (count + 1) * craftingResult.item.count > craftingResult.item.maxStackSize) {
|
||||
if (settings.craftingAmount == ItemMonitorPlayerSettings.Amount.STACK || tile.craftingGrid.any { !it.isStackable }) {
|
||||
if (crafted > 0 && (crafted + 1) * item.count > item.maxStackSize) {
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
} else {
|
||||
val count = tile.howMuchPlayerCrafted(ply)
|
||||
|
||||
if (count > 0 && count >= maxStack) {
|
||||
if (crafted > 0 && crafted >= tile.craftingGrid.iterator().mapToInt { it.maxStackSize }.reduce(Int.MAX_VALUE, Int::coerceAtMost)) {
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
}
|
||||
@ -227,51 +215,13 @@ class ItemMonitorMenu @JvmOverloads constructor(
|
||||
tile.craftingResultContainer.craftingPlayer = ply as ServerPlayer
|
||||
|
||||
try {
|
||||
when (settings.resultTarget) {
|
||||
ItemMonitorPlayerSettings.ResultTarget.ALL_SYSTEM -> {
|
||||
val wrapper = ItemStorageStack(item)
|
||||
var remaining = tile.poweredView?.insertStack(wrapper, true)?.toItemStack() ?: return ItemStack.EMPTY
|
||||
|
||||
if (remaining.isEmpty) {
|
||||
tile.poweredView!!.insertStack(wrapper, false)
|
||||
craftingResult.remove(item.count)
|
||||
if (moveCrafting(tile.poweredView ?: return ItemStack.EMPTY, true)) {
|
||||
item = item.copy()
|
||||
moveCrafting(tile.poweredView ?: return ItemStack.EMPTY, false)
|
||||
return item
|
||||
}
|
||||
|
||||
remaining = moveItemStackToSlots(remaining, playerInventorySlots, simulate = true)
|
||||
|
||||
if (remaining.isEmpty) {
|
||||
remaining = tile.poweredView!!.insertStack(wrapper, false).toItemStack()
|
||||
moveItemStackToSlots(remaining, playerInventorySlots, simulate = false)
|
||||
craftingResult.remove(item.count)
|
||||
return item
|
||||
}
|
||||
|
||||
} else {
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
|
||||
ItemMonitorPlayerSettings.ResultTarget.MIXED, ItemMonitorPlayerSettings.ResultTarget.ALL_INVENTORY -> {
|
||||
var remaining = moveItemStackToSlots(item, playerInventorySlots, simulate = true)
|
||||
|
||||
if (remaining.isEmpty) {
|
||||
moveItemStackToSlots(item, playerInventorySlots, simulate = false)
|
||||
craftingResult.remove(item.count)
|
||||
return item
|
||||
}
|
||||
|
||||
val wrapper = ItemStorageStack(remaining)
|
||||
remaining = tile.poweredView?.insertStack(wrapper, true)?.toItemStack() ?: return ItemStack.EMPTY
|
||||
|
||||
if (remaining.isEmpty) {
|
||||
moveItemStackToSlots(item, playerInventorySlots, simulate = false)
|
||||
tile.poweredView!!.insertStack(wrapper, false)
|
||||
craftingResult.remove(item.count)
|
||||
return item
|
||||
}
|
||||
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
tile.craftingResultContainer.craftingPlayer = null
|
||||
}
|
||||
|
@ -81,8 +81,8 @@ object MenuNetworkChannel : MatteryNetworkChannel(
|
||||
add(SetCarriedPacket::class.java, SetCarriedPacket.Companion::read, NetworkDirection.PLAY_TO_CLIENT)
|
||||
add(ItemFilterSlotPacket::class.java, ItemFilterSlotPacket.Companion::read)
|
||||
|
||||
// networked view
|
||||
add(ClearItemViewPacket::class.java, ClearItemViewPacket::read, NetworkDirection.PLAY_TO_CLIENT)
|
||||
// networked item view
|
||||
add(ClearItemViewPacket::class.java, { ClearItemViewPacket }, NetworkDirection.PLAY_TO_CLIENT)
|
||||
add(ItemViewInteractPacket::class.java, ItemViewInteractPacket.Companion::read, NetworkDirection.PLAY_TO_SERVER)
|
||||
add(StackAddPacket::class.java, StackAddPacket.Companion::read, NetworkDirection.PLAY_TO_CLIENT)
|
||||
add(StackChangePacket::class.java, StackChangePacket.Companion::read, NetworkDirection.PLAY_TO_CLIENT)
|
||||
@ -94,9 +94,6 @@ object MenuNetworkChannel : MatteryNetworkChannel(
|
||||
// Client->Server
|
||||
add(MatteryMenu.PlayerInputPacket::class.java, MatteryMenu::PlayerInputPacket, NetworkDirection.PLAY_TO_SERVER)
|
||||
|
||||
// Item monitor
|
||||
add(ItemMonitorPlayerSettings::class.java, ItemMonitorPlayerSettings.Companion::read)
|
||||
|
||||
// matter panel menu
|
||||
add(CancelTaskPacket::class.java, CancelTaskPacket.Companion::read, NetworkDirection.PLAY_TO_SERVER)
|
||||
add(PatternsChangePacket::class.java, PatternsChangePacket.Companion::read, NetworkDirection.PLAY_TO_CLIENT)
|
||||
|
@ -33,6 +33,7 @@ import java.lang.ref.WeakReference
|
||||
import java.math.BigDecimal
|
||||
import java.util.*
|
||||
import java.util.function.BooleanSupplier
|
||||
import java.util.function.Consumer
|
||||
import java.util.function.DoubleConsumer
|
||||
import java.util.function.DoubleSupplier
|
||||
import java.util.function.IntConsumer
|
||||
@ -529,6 +530,11 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
isObserver: Boolean = false,
|
||||
) : AbstractField<V>(), IMutableField<V> {
|
||||
private var remote: V = codec.copy(field)
|
||||
private val subs = ISubscriptable.Impl<V>()
|
||||
|
||||
override fun addListener(listener: Consumer<V>): ISubscriptable.L {
|
||||
return subs.addListener(listener)
|
||||
}
|
||||
|
||||
init {
|
||||
if (isObserver) {
|
||||
@ -548,6 +554,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
}
|
||||
|
||||
this@Field.field = value
|
||||
subs.accept(value)
|
||||
}
|
||||
}
|
||||
|
||||
@ -577,15 +584,15 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
|
||||
if (setter != null) {
|
||||
setter.invoke(value, access, false)
|
||||
return
|
||||
}
|
||||
|
||||
} else {
|
||||
if (!isDirty && !codec.compare(remote, value)) {
|
||||
notifyEndpoints(this@Field)
|
||||
isDirty = true
|
||||
}
|
||||
|
||||
this.field = value
|
||||
subs.accept(value)
|
||||
}
|
||||
}
|
||||
|
||||
override fun write(stream: DataOutputStream, endpoint: Endpoint) {
|
||||
@ -602,10 +609,10 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
|
||||
if (setter != null) {
|
||||
setter.invoke(value, access, true)
|
||||
return
|
||||
}
|
||||
|
||||
} else {
|
||||
this.field = value
|
||||
subs.accept(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -619,7 +626,21 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
/**
|
||||
* Type specific field, storing primitive [Float] directly
|
||||
*/
|
||||
inner class FloatField(private var field: Float, private val getter: FloatFieldGetter? = null, private val setter: FloatFieldSetter? = null) : PrimitiveField<Float>(), IMutableFloatField {
|
||||
inner class FloatField(field: Float, private val getter: FloatFieldGetter? = null, private val setter: FloatFieldSetter? = null) : PrimitiveField<Float>(), IMutableFloatField {
|
||||
private val subs = IFloatSubcripable.Impl()
|
||||
|
||||
override fun addListener(listener: FloatConsumer): ISubscriptable.L {
|
||||
return subs.addListener(listener)
|
||||
}
|
||||
|
||||
private var field = field
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
field = value
|
||||
subs.accept(value)
|
||||
}
|
||||
}
|
||||
|
||||
override val property = object : IMutableFloatProperty {
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): Float {
|
||||
return this@FloatField.float
|
||||
@ -686,7 +707,21 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
/**
|
||||
* Type specific field, storing primitive [Double] directly
|
||||
*/
|
||||
inner class DoubleField(private var field: Double, private val getter: DoubleFieldGetter? = null, private val setter: DoubleFieldSetter? = null) : PrimitiveField<Double>(), IMutableDoubleField {
|
||||
inner class DoubleField(field: Double, private val getter: DoubleFieldGetter? = null, private val setter: DoubleFieldSetter? = null) : PrimitiveField<Double>(), IMutableDoubleField {
|
||||
private val subs = IDoubleSubcripable.Impl()
|
||||
|
||||
override fun addListener(listener: DoubleConsumer): ISubscriptable.L {
|
||||
return subs.addListener(listener)
|
||||
}
|
||||
|
||||
private var field = field
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
field = value
|
||||
subs.accept(value)
|
||||
}
|
||||
}
|
||||
|
||||
override val property = object : IMutableDoubleProperty {
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): Double {
|
||||
return this@DoubleField.double
|
||||
@ -750,7 +785,21 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
}
|
||||
}
|
||||
|
||||
abstract inner class AbstractIntField(protected var field: Int, private val getter: IntFieldGetter? = null, protected val setter: IntFieldSetter? = null) : PrimitiveField<Int>(), IMutableIntField {
|
||||
abstract inner class AbstractIntField(field: Int, private val getter: IntFieldGetter? = null, protected val setter: IntFieldSetter? = null) : PrimitiveField<Int>(), IMutableIntField {
|
||||
private val subs = IIntSubcripable.Impl()
|
||||
|
||||
override fun addListener(listener: IntConsumer): ISubscriptable.L {
|
||||
return subs.addListener(listener)
|
||||
}
|
||||
|
||||
protected var field = field
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
field = value
|
||||
subs.accept(value)
|
||||
}
|
||||
}
|
||||
|
||||
final override val property = object : IMutableIntProperty {
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): Int {
|
||||
return this@AbstractIntField.int
|
||||
@ -845,7 +894,21 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
/**
|
||||
* Type specific field, storing primitive [Long] directly
|
||||
*/
|
||||
abstract inner class AbstractLongField(protected var field: Long, private val getter: LongFieldGetter? = null, protected val setter: LongFieldSetter? = null) : PrimitiveField<Long>(), IMutableLongField {
|
||||
abstract inner class AbstractLongField(field: Long, private val getter: LongFieldGetter? = null, protected val setter: LongFieldSetter? = null) : PrimitiveField<Long>(), IMutableLongField {
|
||||
private val subs = ILongSubcripable.Impl()
|
||||
|
||||
override fun addListener(listener: LongConsumer): ISubscriptable.L {
|
||||
return subs.addListener(listener)
|
||||
}
|
||||
|
||||
protected var field = field
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
field = value
|
||||
subs.accept(value)
|
||||
}
|
||||
}
|
||||
|
||||
final override val property = object : IMutableLongProperty {
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): Long {
|
||||
return this@AbstractLongField.long
|
||||
@ -940,7 +1003,21 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
/**
|
||||
* Type specific field, storing primitive [Boolean] directly
|
||||
*/
|
||||
inner class BooleanField(private var field: Boolean, private val getter: BooleanFieldGetter? = null, private val setter: BooleanFieldSetter? = null) : PrimitiveField<Boolean>(), IMutableBooleanField {
|
||||
inner class BooleanField(field: Boolean, private val getter: BooleanFieldGetter? = null, private val setter: BooleanFieldSetter? = null) : PrimitiveField<Boolean>(), IMutableBooleanField {
|
||||
private val subs = IBooleanSubscriptable.Impl()
|
||||
|
||||
override fun addListener(listener: BooleanConsumer): ISubscriptable.L {
|
||||
return subs.addListener(listener)
|
||||
}
|
||||
|
||||
private var field = field
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
field = value
|
||||
subs.accept(value)
|
||||
}
|
||||
}
|
||||
|
||||
override val property = object : IMutableBooleanProperty {
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): Boolean {
|
||||
return this@BooleanField.boolean
|
||||
@ -1014,6 +1091,11 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
) : AbstractField<V>(), IField<V> {
|
||||
private var remote: Any? = Mark
|
||||
private var clientValue: Any? = Mark
|
||||
private val subs = ISubscriptable.Impl<V>()
|
||||
|
||||
override fun addListener(listener: Consumer<V>): ISubscriptable.L {
|
||||
return subs.addListener(listener)
|
||||
}
|
||||
|
||||
init {
|
||||
observers.add(this)
|
||||
@ -1055,6 +1137,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
val newValue = codec.read(stream)
|
||||
clientValue = newValue
|
||||
observer.invoke(newValue)
|
||||
subs.accept(newValue)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1063,11 +1146,22 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
*
|
||||
* This class has concrete implementation for [Float] primitive
|
||||
*/
|
||||
inner class ComputedFloatField(private val getter: FloatSupplier, private val observer: FloatConsumer = FloatConsumer {}) : AbstractField<Float>(), IFloatField {
|
||||
inner class ComputedFloatField(private val getter: FloatSupplier, observer: FloatConsumer? = null) : AbstractField<Float>(), IFloatField {
|
||||
private var remote: Float = 0f
|
||||
private var isRemoteSet = false
|
||||
private var clientValue: Float = 0f
|
||||
private var isClientValue = false
|
||||
private val subs = IFloatSubcripable.Impl()
|
||||
|
||||
init {
|
||||
if (observer != null) {
|
||||
subs.addListener(observer)
|
||||
}
|
||||
}
|
||||
|
||||
override fun addListener(listener: FloatConsumer): ISubscriptable.L {
|
||||
return subs.addListener(listener)
|
||||
}
|
||||
|
||||
override val property = object : IFloatProperty {
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): Float {
|
||||
@ -1120,7 +1214,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
val newValue = stream.readFloat()
|
||||
clientValue = newValue
|
||||
isClientValue = true
|
||||
observer.accept(newValue)
|
||||
subs.accept(newValue)
|
||||
}
|
||||
|
||||
@Deprecated("Use type specific property", replaceWith = ReplaceWith("this.property"))
|
||||
@ -1134,11 +1228,22 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
*
|
||||
* This class has concrete implementation for [Double] primitive
|
||||
*/
|
||||
inner class ComputedDoubleField(private val getter: DoubleSupplier, private val observer: DoubleConsumer = DoubleConsumer {}) : AbstractField<Double>(), IDoubleField {
|
||||
inner class ComputedDoubleField(private val getter: DoubleSupplier, observer: DoubleConsumer? = null) : AbstractField<Double>(), IDoubleField {
|
||||
private var remote: Double = 0.0
|
||||
private var isRemoteSet = false
|
||||
private var clientValue: Double = 0.0
|
||||
private var isClientValue = false
|
||||
private val subs = IDoubleSubcripable.Impl()
|
||||
|
||||
init {
|
||||
if (observer != null) {
|
||||
subs.addListener(observer)
|
||||
}
|
||||
}
|
||||
|
||||
override fun addListener(listener: DoubleConsumer): ISubscriptable.L {
|
||||
return subs.addListener(listener)
|
||||
}
|
||||
|
||||
override val property = object : IDoubleProperty {
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): Double {
|
||||
@ -1185,7 +1290,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
val newValue = stream.readDouble()
|
||||
clientValue = newValue
|
||||
isClientValue = true
|
||||
observer.accept(newValue)
|
||||
subs.accept(newValue)
|
||||
}
|
||||
|
||||
@Deprecated("Use type specific property", replaceWith = ReplaceWith("this.property"))
|
||||
@ -1199,11 +1304,29 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
*
|
||||
* This class has concrete implementation for [Int] primitive
|
||||
*/
|
||||
abstract inner class AbstractComputedIntField(protected val getter: IntSupplier, protected val observer: IntConsumer = IntConsumer {}) : AbstractField<Int>(), IIntField {
|
||||
abstract inner class AbstractComputedIntField(protected val getter: IntSupplier, observer: IntConsumer? = null) : AbstractField<Int>(), IIntField {
|
||||
private var remote: Int = 0
|
||||
private var isRemoteSet = false
|
||||
protected var clientValue: Int = 0
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
field = value
|
||||
subs.accept(value)
|
||||
}
|
||||
}
|
||||
|
||||
protected var isClientValue = false
|
||||
private val subs = IIntSubcripable.Impl()
|
||||
|
||||
init {
|
||||
if (observer != null) {
|
||||
subs.addListener(observer)
|
||||
}
|
||||
}
|
||||
|
||||
override fun addListener(listener: IntConsumer): ISubscriptable.L {
|
||||
return subs.addListener(listener)
|
||||
}
|
||||
|
||||
final override val property = object : IIntProperty {
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): Int {
|
||||
@ -1259,10 +1382,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
|
||||
override fun read(stream: DataInputStream) {
|
||||
check(!isRemoved) { "Field was removed" }
|
||||
val newValue = stream.readVarIntLE()
|
||||
clientValue = newValue
|
||||
isClientValue = true
|
||||
observer.accept(newValue)
|
||||
clientValue = stream.readVarIntLE()
|
||||
}
|
||||
}
|
||||
|
||||
@ -1280,10 +1400,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
|
||||
override fun read(stream: DataInputStream) {
|
||||
check(!isRemoved) { "Field was removed" }
|
||||
val newValue = stream.readInt()
|
||||
clientValue = newValue
|
||||
isClientValue = true
|
||||
observer.accept(newValue)
|
||||
clientValue = stream.readInt()
|
||||
}
|
||||
}
|
||||
|
||||
@ -1292,11 +1409,31 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
*
|
||||
* This class has concrete implementation for [Long] primitive
|
||||
*/
|
||||
abstract inner class AbstractComputedLongField(protected val getter: LongSupplier, protected val observer: LongConsumer = LongConsumer {}) : AbstractField<Long>(), ILongField {
|
||||
abstract inner class AbstractComputedLongField(protected val getter: LongSupplier, observer: LongConsumer? = null) : AbstractField<Long>(), ILongField {
|
||||
private var remote: Long = 0L
|
||||
private var isRemoteSet = false
|
||||
protected var clientValue: Long = 0L
|
||||
set(value) {
|
||||
isClientValue = true
|
||||
|
||||
if (field != value) {
|
||||
field = value
|
||||
subs.accept(value)
|
||||
}
|
||||
}
|
||||
|
||||
protected var isClientValue = false
|
||||
private val subs = ILongSubcripable.Impl()
|
||||
|
||||
init {
|
||||
if (observer != null) {
|
||||
subs.addListener(observer)
|
||||
}
|
||||
}
|
||||
|
||||
override fun addListener(listener: LongConsumer): ISubscriptable.L {
|
||||
return subs.addListener(listener)
|
||||
}
|
||||
|
||||
final override val property = object : ILongProperty {
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): Long {
|
||||
@ -1352,10 +1489,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
|
||||
override fun read(stream: DataInputStream) {
|
||||
check(!isRemoved) { "Field was removed" }
|
||||
val newValue = stream.readVarLongLE()
|
||||
clientValue = newValue
|
||||
isClientValue = true
|
||||
observer.accept(newValue)
|
||||
clientValue = stream.readVarLongLE()
|
||||
}
|
||||
}
|
||||
|
||||
@ -1373,10 +1507,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
|
||||
override fun read(stream: DataInputStream) {
|
||||
check(!isRemoved) { "Field was removed" }
|
||||
val newValue = stream.readLong()
|
||||
clientValue = newValue
|
||||
isClientValue = true
|
||||
observer.accept(newValue)
|
||||
clientValue = stream.readLong()
|
||||
}
|
||||
}
|
||||
|
||||
@ -1385,11 +1516,22 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
*
|
||||
* This class has concrete implementation for [Boolean] primitive
|
||||
*/
|
||||
inner class ComputedBooleanField(private val getter: BooleanSupplier, private val observer: BooleanConsumer = BooleanConsumer {}) : AbstractField<Boolean>(), IBooleanField {
|
||||
inner class ComputedBooleanField(private val getter: BooleanSupplier, observer: BooleanConsumer? = null) : AbstractField<Boolean>(), IBooleanField {
|
||||
private var remote: Boolean = false
|
||||
private var isRemoteSet = false
|
||||
private var clientValue: Boolean = false
|
||||
private var isClientValue = false
|
||||
private val subs = IBooleanSubscriptable.Impl()
|
||||
|
||||
init {
|
||||
if (observer != null) {
|
||||
subs.addListener(observer)
|
||||
}
|
||||
}
|
||||
|
||||
override fun addListener(listener: BooleanConsumer): ISubscriptable.L {
|
||||
return subs.addListener(listener)
|
||||
}
|
||||
|
||||
override val property = object : IBooleanProperty {
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): Boolean {
|
||||
@ -1436,7 +1578,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
val newValue = stream.readBoolean()
|
||||
clientValue = newValue
|
||||
isClientValue = true
|
||||
observer.accept(newValue)
|
||||
subs.accept(newValue)
|
||||
}
|
||||
|
||||
@Deprecated("Use type specific property", replaceWith = ReplaceWith("this.property"))
|
||||
@ -1453,6 +1595,11 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
private val getter: () -> V
|
||||
private val setter: (V) -> Unit
|
||||
private var remote: V
|
||||
private val subs = ISubscriptable.Impl<V>()
|
||||
|
||||
override fun addListener(listener: Consumer<V>): ISubscriptable.L {
|
||||
return subs.addListener(listener)
|
||||
}
|
||||
|
||||
override var value: V
|
||||
get() = getter.invoke()
|
||||
@ -1499,6 +1646,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
override fun read(stream: DataInputStream) {
|
||||
check(!isRemoved) { "Field was removed" }
|
||||
this.value = codec.read(stream)
|
||||
subs.accept(this.value)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1506,7 +1654,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
private val codec: IStreamCodec<E>,
|
||||
private val backingSet: MutableSet<E>,
|
||||
private val callback: ((changes: Collection<SetChangeset<E>>) -> Unit)? = null,
|
||||
) : AbstractField<MutableSet<E>>() {
|
||||
) : AbstractField<MutableSet<E>>(), ISubscriptable<MutableSet<E>> by ISubscriptable.empty() {
|
||||
private var isRemote = false
|
||||
|
||||
private fun pushBacklog(element: E, action: (DataOutputStream) -> Unit) {
|
||||
@ -1745,7 +1893,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
||||
private val valueCodec: IStreamCodec<V>,
|
||||
private val backingMap: MutableMap<K, V>,
|
||||
private val callback: ((changes: Collection<MapChangeset<K, V>>) -> Unit)? = null,
|
||||
) : AbstractField<MutableMap<K, V>>(), IField<MutableMap<K, V>> {
|
||||
) : AbstractField<MutableMap<K, V>>(), IField<MutableMap<K, V>>, ISubscriptable<MutableMap<K, V>> by ISubscriptable.empty() {
|
||||
private var sentAllValues = false
|
||||
private var isRemote = false
|
||||
|
||||
|
@ -1,6 +1,12 @@
|
||||
package ru.dbotthepony.mc.otm.network.synchronizer
|
||||
|
||||
import ru.dbotthepony.mc.otm.core.FloatSupplier
|
||||
import ru.dbotthepony.mc.otm.core.IBooleanSubscriptable
|
||||
import ru.dbotthepony.mc.otm.core.IDoubleSubcripable
|
||||
import ru.dbotthepony.mc.otm.core.IFloatSubcripable
|
||||
import ru.dbotthepony.mc.otm.core.IIntSubcripable
|
||||
import ru.dbotthepony.mc.otm.core.ILongSubcripable
|
||||
import ru.dbotthepony.mc.otm.core.ISubscriptable
|
||||
import java.io.DataInputStream
|
||||
import java.io.DataOutputStream
|
||||
import java.util.function.BooleanSupplier
|
||||
@ -11,7 +17,7 @@ import java.util.function.Supplier
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
sealed interface IField<V> : ReadOnlyProperty<Any?, V>, Supplier<V>, () -> V {
|
||||
sealed interface IField<V> : ReadOnlyProperty<Any?, V>, Supplier<V>, () -> V, ISubscriptable<V> {
|
||||
fun observe(): Boolean
|
||||
fun markDirty()
|
||||
fun markDirty(endpoint: FieldSynchronizer.Endpoint)
|
||||
@ -40,7 +46,7 @@ interface IFloatProperty {
|
||||
operator fun getValue(thisRef: Any?, property: KProperty<*>): Float
|
||||
}
|
||||
|
||||
sealed interface IFloatField : IField<Float>, FloatSupplier {
|
||||
sealed interface IFloatField : IField<Float>, FloatSupplier, IFloatSubcripable {
|
||||
val float: Float
|
||||
val property: IFloatProperty
|
||||
|
||||
@ -66,7 +72,7 @@ interface IDoubleProperty {
|
||||
operator fun getValue(thisRef: Any?, property: KProperty<*>): Double
|
||||
}
|
||||
|
||||
sealed interface IDoubleField : IField<Double>, DoubleSupplier {
|
||||
sealed interface IDoubleField : IField<Double>, DoubleSupplier, IDoubleSubcripable {
|
||||
val double: Double
|
||||
val property: IDoubleProperty
|
||||
|
||||
@ -92,7 +98,7 @@ interface IIntProperty {
|
||||
operator fun getValue(thisRef: Any?, property: KProperty<*>): Int
|
||||
}
|
||||
|
||||
sealed interface IIntField : IField<Int>, IntSupplier {
|
||||
sealed interface IIntField : IField<Int>, IntSupplier, IIntSubcripable {
|
||||
val int: Int
|
||||
val property: IIntProperty
|
||||
|
||||
@ -118,7 +124,7 @@ interface ILongProperty {
|
||||
operator fun getValue(thisRef: Any?, property: KProperty<*>): Long
|
||||
}
|
||||
|
||||
sealed interface ILongField : IField<Long>, LongSupplier {
|
||||
sealed interface ILongField : IField<Long>, LongSupplier, ILongSubcripable {
|
||||
val long: Long
|
||||
val property: ILongProperty
|
||||
|
||||
@ -144,7 +150,7 @@ interface IBooleanProperty {
|
||||
operator fun getValue(thisRef: Any?, property: KProperty<*>): Boolean
|
||||
}
|
||||
|
||||
sealed interface IBooleanField : IField<Boolean>, BooleanSupplier {
|
||||
sealed interface IBooleanField : IField<Boolean>, BooleanSupplier, IBooleanSubscriptable {
|
||||
val boolean: Boolean
|
||||
val property: IBooleanProperty
|
||||
|
||||
|
@ -3,12 +3,13 @@ package ru.dbotthepony.mc.otm.network.synchronizer
|
||||
import it.unimi.dsi.fastutil.booleans.BooleanConsumer
|
||||
import it.unimi.dsi.fastutil.floats.FloatConsumer
|
||||
import ru.dbotthepony.mc.otm.core.GetterSetter
|
||||
import ru.dbotthepony.mc.otm.core.SentientGetterSetter
|
||||
import java.util.function.DoubleConsumer
|
||||
import java.util.function.IntConsumer
|
||||
import java.util.function.LongConsumer
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
sealed interface IMutableField<V> : IField<V>, GetterSetter<V> {
|
||||
sealed interface IMutableField<V> : IField<V>, SentientGetterSetter<V> {
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): V {
|
||||
return this.value
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ import net.minecraftforge.registries.ForgeRegistries
|
||||
import ru.dbotthepony.mc.otm.OverdriveThatMatters
|
||||
import ru.dbotthepony.mc.otm.capability.matter.matter
|
||||
import ru.dbotthepony.mc.otm.capability.matteryEnergy
|
||||
import ru.dbotthepony.mc.otm.core.util.CreativeMenuComparator
|
||||
import ru.dbotthepony.mc.otm.core.util.CreativeMenuItemComparator
|
||||
import ru.dbotthepony.mc.otm.core.TranslatableComponent
|
||||
import ru.dbotthepony.mc.otm.core.ifPresentK
|
||||
import ru.dbotthepony.mc.otm.core.registryName
|
||||
@ -177,6 +177,9 @@ private fun addMainCreativeTabItems(consumer: CreativeModeTab.Output) {
|
||||
accept(MItems.PATTERN_DRIVE_CREATIVE)
|
||||
accept(MItems.PATTERN_DRIVE_CREATIVE2)
|
||||
|
||||
accept(MItems.PORTABLE_CONDENSATION_DRIVE)
|
||||
accept(MItems.PORTABLE_DENSE_CONDENSATION_DRIVE)
|
||||
|
||||
fluids(MItems.FLUID_CAPSULE)
|
||||
fluids(MItems.FLUID_TANK)
|
||||
|
||||
@ -285,7 +288,7 @@ object MCreativeTabs {
|
||||
}
|
||||
|
||||
fun register(event: BuildCreativeModeTabContentsEvent) {
|
||||
CreativeMenuComparator.invalidate()
|
||||
CreativeMenuItemComparator.invalidate()
|
||||
|
||||
when (event.tab) {
|
||||
MAIN -> addMainCreativeTabItems(event)
|
||||
|
@ -156,8 +156,8 @@ object MItems {
|
||||
::MATTER_SCANNER, ::MATTER_PANEL, ::MATTER_REPLICATOR, ::MATTER_BOTTLER, ::ENERGY_COUNTER, ::CHEMICAL_GENERATOR,
|
||||
::MATTER_RECYCLER, ::PLATE_PRESS, ::TWIN_PLATE_PRESS, ::POWERED_FURNACE, ::POWERED_BLAST_FURNACE,
|
||||
::POWERED_SMOKER,
|
||||
// ::STORAGE_BUS, ::STORAGE_IMPORTER, ::STORAGE_EXPORTER, ::DRIVE_VIEWER,
|
||||
// ::DRIVE_RACK, ::ITEM_MONITOR, ::STORAGE_CABLE, ::STORAGE_POWER_SUPPLIER,
|
||||
::STORAGE_BUS, ::STORAGE_IMPORTER, ::STORAGE_EXPORTER, ::DRIVE_VIEWER,
|
||||
::DRIVE_RACK, ::ITEM_MONITOR, ::STORAGE_CABLE, ::STORAGE_POWER_SUPPLIER,
|
||||
::ENERGY_SERVO,
|
||||
::PHANTOM_ATTRACTOR, ::GRAVITATION_STABILIZER, ::COBBLESTONE_GENERATOR, ::INFINITE_WATER_SOURCE,
|
||||
::ESSENCE_STORAGE, ::MATTER_RECONSTRUCTOR
|
||||
|
@ -121,6 +121,10 @@ interface IStorageProvider<T : StorageStack<T>> : IStorageEventProducer<T> {
|
||||
* @return copy of object, with amount of units actually extracted
|
||||
*/
|
||||
fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): T
|
||||
|
||||
fun extractStack(id: T, amount: BigInteger, simulate: Boolean): T {
|
||||
return extractStack(get(id) ?: return storageType.empty, amount, simulate)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,7 +1,10 @@
|
||||
package ru.dbotthepony.mc.otm.storage
|
||||
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.world.item.Item
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import ru.dbotthepony.mc.otm.core.getValue
|
||||
import ru.dbotthepony.mc.otm.core.lazy2
|
||||
import ru.dbotthepony.mc.otm.core.math.toIntSafe
|
||||
import java.math.BigInteger
|
||||
|
||||
@ -43,6 +46,8 @@ class ItemStorageStack private constructor(private val stack: ItemStack, count:
|
||||
override val isEmpty: Boolean = stack.isEmpty || super.isEmpty
|
||||
override val maxStackSize: BigInteger = BigInteger.valueOf(stack.maxStackSize.toLong())
|
||||
|
||||
val hoverName: Component by lazy2({ toItemStack().hoverName }, { copy() })
|
||||
|
||||
fun toItemStack(count: Int = this.count.toIntSafe()): ItemStack {
|
||||
return stack.copyWithCount(count)
|
||||
}
|
||||
|
@ -63,9 +63,19 @@ abstract class StorageStack<S : StorageStack<S>>(val count: BigInteger) {
|
||||
fun read(buff: FriendlyByteBuf): T
|
||||
fun write(buff: FriendlyByteBuf, value: T)
|
||||
|
||||
fun energyPerUpkeep(stack: T): Decimal = Decimal.ZERO
|
||||
/**
|
||||
* If there is not enough energy for operation, it is completely cancelled
|
||||
*/
|
||||
fun energyPerInsert(stack: T): Decimal = energyPerOperation(stack)
|
||||
|
||||
/**
|
||||
* If there is not enough energy for operation, it is completely cancelled
|
||||
*/
|
||||
fun energyPerExtract(stack: T): Decimal = energyPerOperation(stack)
|
||||
|
||||
/**
|
||||
* If there is not enough energy for operation, it is completely cancelled
|
||||
*/
|
||||
fun energyPerOperation(stack: T): Decimal
|
||||
}
|
||||
|
||||
@ -113,7 +123,7 @@ abstract class StorageStack<S : StorageStack<S>>(val count: BigInteger) {
|
||||
ItemStorageStack.unsafe(it.readItem(), it.readBigInteger())
|
||||
},
|
||||
{ buff, v ->
|
||||
buff.writeItem(v.toItemStack())
|
||||
buff.writeItem(v.toItemStack(1))
|
||||
buff.writeBigInteger(v.count)
|
||||
}
|
||||
)
|
||||
|
@ -3,6 +3,7 @@ package ru.dbotthepony.mc.otm.storage
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
||||
import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet
|
||||
import ru.dbotthepony.mc.otm.core.math.isPositive
|
||||
import ru.dbotthepony.mc.otm.core.math.isZero
|
||||
import java.math.BigInteger
|
||||
@ -41,23 +42,19 @@ class LocalTuple<T : StorageStack<T>>(override var stack: T, override val id: UU
|
||||
}
|
||||
}
|
||||
|
||||
open class VirtualComponent<T : StorageStack<T>>(override val storageType: StorageStack.Type<T>) : IVirtualStorageComponent<T> {
|
||||
class VirtualComponent<T : StorageStack<T>>(override val storageType: StorageStack.Type<T>) : IVirtualStorageComponent<T> {
|
||||
// удаленный UUID -> Кортеж
|
||||
protected val remoteTuples = Object2ObjectOpenHashMap<UUID, RemoteTuple<T>>()
|
||||
private val remoteTuples = Object2ObjectOpenHashMap<UUID, RemoteTuple<T>>()
|
||||
|
||||
// локальный UUID -> Локальный кортеж
|
||||
protected val localTuples = Object2ObjectOpenHashMap<UUID, LocalTuple<T>>()
|
||||
private val localTuples = Object2ObjectOpenHashMap<UUID, LocalTuple<T>>()
|
||||
|
||||
// Стак -> Локальный кортеж стака
|
||||
protected val item2tuple = Object2ObjectOpenCustomHashMap<T, LocalTuple<T>>(StorageStack.Companion)
|
||||
private val item2tuple = Object2ObjectOpenCustomHashMap<T, LocalTuple<T>>(StorageStack.Companion)
|
||||
|
||||
// ArrayList для скорости работы
|
||||
protected val listeners: MutableSet<IStorageEventConsumer<T>> = ObjectArraySet()
|
||||
protected val children: MutableSet<IStorage<T>> = ObjectArraySet()
|
||||
protected val consumers: MutableSet<IStorageAcceptor<T>> = ObjectArraySet()
|
||||
|
||||
protected open fun onAdd(identity: IStorage<T>) {}
|
||||
protected open fun onRemove(identity: IStorage<T>) {}
|
||||
private val listeners = ObjectLinkedOpenHashSet<IStorageEventConsumer<T>>()
|
||||
private val children = ObjectLinkedOpenHashSet<IStorage<T>>()
|
||||
private val consumers = ObjectLinkedOpenHashSet<IStorageAcceptor<T>>()
|
||||
|
||||
override fun add(identity: IStorage<T>) {
|
||||
require(identity.storageType == storageType) { "Attempt to add component of type ${identity.storageType} to virtual component of type $storageType" }
|
||||
@ -76,8 +73,6 @@ open class VirtualComponent<T : StorageStack<T>>(override val storageType: Stora
|
||||
if (identity is IStorageAcceptor<T>) {
|
||||
consumers.add(identity)
|
||||
}
|
||||
|
||||
onAdd(identity)
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,8 +93,6 @@ open class VirtualComponent<T : StorageStack<T>>(override val storageType: Stora
|
||||
if (identity is IStorageAcceptor<T>) {
|
||||
consumers.remove(identity)
|
||||
}
|
||||
|
||||
onRemove(identity)
|
||||
}
|
||||
}
|
||||
|
||||
@ -231,4 +224,8 @@ open class VirtualComponent<T : StorageStack<T>>(override val storageType: Stora
|
||||
|
||||
return this.storageType.empty
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "VirtualComponent[$storageType; listeners: ${listeners.size}; size: ${localTuples.size}]"
|
||||
}
|
||||
}
|
||||
|
@ -13,4 +13,16 @@ class PoweredComponent<T : StorageStack<T>>(
|
||||
) : IStorageComponent<T>, IStorageProvider<T> by PoweredStorageProvider(parent, energy), IStorageAcceptor<T> by PoweredStorageAcceptor(parent, energy) {
|
||||
override val storageType: StorageStack.Type<T>
|
||||
get() = parent.storageType
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is PoweredComponent<*> && parent == other.parent
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return parent.hashCode()
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "PoweredComponent[$parent]"
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,18 @@ import ru.dbotthepony.mc.otm.storage.StorageStack
|
||||
import java.util.function.Supplier
|
||||
|
||||
class PoweredStorageAcceptor<T : StorageStack<T>>(val parent: IStorageAcceptor<T>, val energy: Supplier<IMatteryEnergyStorage>) : IStorageAcceptor<T> by parent {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is PoweredStorageAcceptor<*> && parent == other.parent
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return parent.hashCode()
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "PoweredStorageAcceptor[$parent]"
|
||||
}
|
||||
|
||||
override fun insertStack(stack: T, simulate: Boolean): T {
|
||||
val leftover = parent.insertStack(stack, true)
|
||||
if (leftover == stack) return stack
|
||||
|
@ -1,6 +1,10 @@
|
||||
package ru.dbotthepony.mc.otm.storage.powered
|
||||
|
||||
import it.unimi.dsi.fastutil.HashCommon.mix
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
||||
import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet
|
||||
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
|
||||
import ru.dbotthepony.mc.otm.storage.IStorageEventConsumer
|
||||
import ru.dbotthepony.mc.otm.storage.IStorageProvider
|
||||
import ru.dbotthepony.mc.otm.storage.StorageStack
|
||||
import java.math.BigInteger
|
||||
@ -8,8 +12,57 @@ import java.util.*
|
||||
import java.util.function.Supplier
|
||||
|
||||
class PoweredStorageProvider<T : StorageStack<T>>(val parent: IStorageProvider<T>, val energy: Supplier<IMatteryEnergyStorage>) : IStorageProvider<T> by parent {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is PoweredStorageProvider<*> && other.parent == parent
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return parent.hashCode()
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "PoweredStorageProvider[$parent]"
|
||||
}
|
||||
|
||||
private class Proxy<T : StorageStack<T>>(private val parent: IStorageEventConsumer<T>, private val powered: PoweredStorageProvider<T>) : IStorageEventConsumer<T> {
|
||||
override val storageType: StorageStack.Type<T>
|
||||
get() = parent.storageType
|
||||
|
||||
override fun onStackAdded(stack: T, id: UUID, provider: IStorageProvider<T>) {
|
||||
parent.onStackAdded(stack, id, powered)
|
||||
}
|
||||
|
||||
override fun onStackChanged(stack: T, id: UUID) {
|
||||
parent.onStackChanged(stack, id)
|
||||
}
|
||||
|
||||
override fun onStackRemoved(id: UUID) {
|
||||
parent.onStackRemoved(id)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "PoweredStorageProvider.Proxy[$parent with $powered]"
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is Proxy<*> && parent == other.parent && powered == other.powered
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return mix(parent.hashCode() * 31 + powered.hashCode())
|
||||
}
|
||||
}
|
||||
|
||||
override fun addListener(listener: IStorageEventConsumer<T>): Boolean {
|
||||
return parent.addListener(Proxy(listener, this))
|
||||
}
|
||||
|
||||
override fun removeListener(listener: IStorageEventConsumer<T>): Boolean {
|
||||
return parent.removeListener(Proxy(listener, this))
|
||||
}
|
||||
|
||||
override fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): T {
|
||||
val extracted = parent.extractStack(id, amount, simulate)
|
||||
val extracted = parent.extractStack(id, amount, true)
|
||||
if (extracted.isEmpty) return extracted
|
||||
|
||||
val energy = energy.get()
|
||||
|
@ -16,6 +16,18 @@ class PoweredVirtualComponent<T : StorageStack<T>>(
|
||||
) : IVirtualStorageComponent<T>, IStorageComponent<T> by PoweredComponent(parent, energy) {
|
||||
constructor(type: StorageStack.Type<T>, energy: Supplier<IMatteryEnergyStorage>) : this(VirtualComponent(type), energy)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is PoweredVirtualComponent<*> && parent == other.parent
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return parent.hashCode()
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "PoweredVirtualComponent[$parent]"
|
||||
}
|
||||
|
||||
override fun onStackAdded(stack: T, id: UUID, provider: IStorageProvider<T>) = parent.onStackAdded(stack, id, provider)
|
||||
override fun onStackChanged(stack: T, id: UUID) = parent.onStackChanged(stack, id)
|
||||
override fun onStackRemoved(id: UUID) = parent.onStackRemoved(id)
|
||||
|
Loading…
Reference in New Issue
Block a user