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.zepto", "%s z%s")
|
||||||
misc("suffix.yocto", "%s y%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.kilo", "k")
|
||||||
misc("suffix_raw.mega", "M")
|
misc("suffix_raw.mega", "M")
|
||||||
misc("suffix_raw.giga", "G")
|
misc("suffix_raw.giga", "G")
|
||||||
@ -776,7 +797,7 @@ private fun gui(provider: MatteryLanguageProvider) {
|
|||||||
gui("sorting.name", "Sort by name")
|
gui("sorting.name", "Sort by name")
|
||||||
gui("sorting.id", "Sort by ID")
|
gui("sorting.id", "Sort by ID")
|
||||||
gui("sorting.modid", "Sort by Namespace (mod) 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.ascending", "Ascending")
|
||||||
gui("sorting.descending", "Descending")
|
gui("sorting.descending", "Descending")
|
||||||
gui("sorting.matter_value", "Matter value")
|
gui("sorting.matter_value", "Matter value")
|
||||||
|
@ -221,6 +221,26 @@ private fun misc(provider: MatteryLanguageProvider) {
|
|||||||
misc("suffix.zepto", "%s з%s")
|
misc("suffix.zepto", "%s з%s")
|
||||||
misc("suffix.yocto", "%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.kilo", "к")
|
||||||
misc("suffix_raw.mega", "М")
|
misc("suffix_raw.mega", "М")
|
||||||
misc("suffix_raw.giga", "Г")
|
misc("suffix_raw.giga", "Г")
|
||||||
|
@ -142,7 +142,6 @@ public final class OverdriveThatMatters {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setup(final FMLCommonSetupEvent event) {
|
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.HIGHEST, DrivePool.INSTANCE::serverStopEvent);
|
||||||
EVENT_BUS.addListener(EventPriority.LOWEST, DrivePool.INSTANCE::serverStartEvent);
|
EVENT_BUS.addListener(EventPriority.LOWEST, DrivePool.INSTANCE::serverStartEvent);
|
||||||
EVENT_BUS.addListener(EventPriority.NORMAL, DrivePool.INSTANCE::onWorldSave);
|
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.ChunkWatchEvent
|
||||||
import net.minecraftforge.event.level.LevelEvent
|
import net.minecraftforge.event.level.LevelEvent
|
||||||
import net.minecraftforge.event.server.ServerStoppingEvent
|
import net.minecraftforge.event.server.ServerStoppingEvent
|
||||||
import net.minecraftforge.items.IItemHandler
|
|
||||||
import ru.dbotthepony.mc.otm.SERVER_IS_LIVE
|
import ru.dbotthepony.mc.otm.SERVER_IS_LIVE
|
||||||
import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock
|
import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock
|
||||||
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
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.capability.isMekanismLoaded
|
||||||
import ru.dbotthepony.mc.otm.compat.mekanism.Mattery2MekanismEnergyWrapper
|
import ru.dbotthepony.mc.otm.compat.mekanism.Mattery2MekanismEnergyWrapper
|
||||||
import ru.dbotthepony.mc.otm.compat.mekanism.Mekanism2MatteryEnergyWrapper
|
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.collect.WeakHashSet
|
||||||
import ru.dbotthepony.mc.otm.core.forValidRefs
|
import ru.dbotthepony.mc.otm.core.forValidRefs
|
||||||
import ru.dbotthepony.mc.otm.core.get
|
import ru.dbotthepony.mc.otm.core.get
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package ru.dbotthepony.mc.otm.block.entity
|
package ru.dbotthepony.mc.otm.block.entity
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet
|
import com.google.common.collect.ImmutableSet
|
||||||
import mekanism.common.tile.interfaces.IRedstoneControl
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityType
|
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||||
import net.minecraft.core.BlockPos
|
import net.minecraft.core.BlockPos
|
||||||
import net.minecraft.world.level.block.state.BlockState
|
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.HandlerFilter
|
||||||
import ru.dbotthepony.mc.otm.container.MatteryContainer
|
import ru.dbotthepony.mc.otm.container.MatteryContainer
|
||||||
import ru.dbotthepony.mc.otm.container.get
|
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.isNotEmpty
|
||||||
import ru.dbotthepony.mc.otm.core.orNull
|
import ru.dbotthepony.mc.otm.core.orNull
|
||||||
import ru.dbotthepony.mc.otm.core.util.FluidStackValueCodec
|
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.Level
|
||||||
import net.minecraft.world.level.block.Block
|
import net.minecraft.world.level.block.Block
|
||||||
import net.minecraft.world.level.block.state.BlockState
|
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.matter.MatterBottlerBlock
|
||||||
import ru.dbotthepony.mc.otm.block.entity.MatteryPoweredBlockEntity
|
import ru.dbotthepony.mc.otm.block.entity.MatteryPoweredBlockEntity
|
||||||
import ru.dbotthepony.mc.otm.block.entity.WorkerState
|
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.FlowDirection
|
||||||
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
||||||
import ru.dbotthepony.mc.otm.capability.ProxiedItemHandler
|
import ru.dbotthepony.mc.otm.capability.ProxiedItemHandler
|
||||||
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
|
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
|
||||||
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
|
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.MatterStorageImpl
|
||||||
import ru.dbotthepony.mc.otm.capability.matter.ProfiledMatterStorage
|
import ru.dbotthepony.mc.otm.capability.matter.ProfiledMatterStorage
|
||||||
import ru.dbotthepony.mc.otm.config.MachinesConfig
|
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.menu.storage.DriveRackMenu
|
||||||
import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
||||||
import ru.dbotthepony.mc.otm.storage.*
|
import ru.dbotthepony.mc.otm.storage.*
|
||||||
|
import ru.dbotthepony.mc.otm.storage.powered.PoweredComponent
|
||||||
import ru.dbotthepony.mc.otm.storage.powered.PoweredVirtualComponent
|
import ru.dbotthepony.mc.otm.storage.powered.PoweredVirtualComponent
|
||||||
|
|
||||||
class DriveRackBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
|
class DriveRackBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryPoweredBlockEntity(MBlockEntities.DRIVE_RACK, blockPos, blockState) {
|
||||||
MatteryPoweredBlockEntity(MBlockEntities.DRIVE_RACK, p_155229_, p_155230_) {
|
|
||||||
val energy = WorkerEnergyStorage(this::setChangedLight, MachinesConfig.DRIVE_RACK)
|
val energy = WorkerEnergyStorage(this::setChangedLight, MachinesConfig.DRIVE_RACK)
|
||||||
|
val cell = StorageNode(energy)
|
||||||
|
|
||||||
val container: MatteryContainer = object : MatteryContainer(this::setChanged, 4) {
|
val container: MatteryContainer = object : MatteryContainer(this::setChanged, 4) {
|
||||||
override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) {
|
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
|
// but since we don't know generics of upvalue mattery drive, its storage type
|
||||||
// is defined as out variant
|
// is defined as out variant
|
||||||
old.getCapability(MatteryCapability.DRIVE).ifPresent {
|
old.getCapability(MatteryCapability.DRIVE).ifPresent {
|
||||||
cell.computeIfAbsent(it.storageType as StorageStack.Type<ItemStorageStack>) {
|
cell.removeStorageComponent(PoweredComponent(it, ::energy))
|
||||||
PoweredVirtualComponent(VirtualComponent(it), ::energy)
|
|
||||||
}.remove(it as IMatteryDrive<ItemStorageStack>)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
new.getCapability(MatteryCapability.DRIVE).ifPresent {
|
new.getCapability(MatteryCapability.DRIVE).ifPresent {
|
||||||
cell.computeIfAbsent(it.storageType as StorageStack.Type<ItemStorageStack>) {
|
cell.addStorageComponent(PoweredComponent(it, ::energy))
|
||||||
PoweredVirtualComponent(VirtualComponent(it), ::energy)
|
|
||||||
}.add(it as IMatteryDrive<ItemStorageStack>)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.also(::addDroppableContainer)
|
}.also(::addDroppableContainer)
|
||||||
|
|
||||||
val cell = StorageNode(energy)
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
savetable(::energy, ENERGY_KEY)
|
savetable(::energy, ENERGY_KEY)
|
||||||
savetable(::container, INVENTORY_KEY)
|
savetable(::container, INVENTORY_KEY)
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package ru.dbotthepony.mc.otm.block.entity.storage
|
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.core.BlockPos
|
||||||
import net.minecraft.server.level.ServerLevel
|
import net.minecraft.server.level.ServerLevel
|
||||||
import net.minecraft.world.entity.player.Inventory
|
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.item.ItemStack
|
||||||
import net.minecraft.world.level.block.Block
|
import net.minecraft.world.level.block.Block
|
||||||
import net.minecraft.world.level.block.state.BlockState
|
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.entity.MatteryPoweredBlockEntity
|
||||||
import ru.dbotthepony.mc.otm.block.storage.DriveViewerBlock
|
|
||||||
import ru.dbotthepony.mc.otm.block.entity.WorkerState
|
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.MatteryCapability
|
||||||
|
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
|
||||||
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
|
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
|
||||||
import ru.dbotthepony.mc.otm.config.MachinesConfig
|
import ru.dbotthepony.mc.otm.config.MachinesConfig
|
||||||
import ru.dbotthepony.mc.otm.container.MatteryContainer
|
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.registry.MBlockEntities
|
||||||
import ru.dbotthepony.mc.otm.menu.storage.DriveViewerMenu
|
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_) {
|
class DriveViewerBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryPoweredBlockEntity(MBlockEntities.DRIVE_VIEWER, blockPos, blockState) {
|
||||||
override fun setChanged() {
|
val energy = ProfiledEnergyStorage(WorkerEnergyStorage(this::energyUpdated, MachinesConfig.DRIVE_VIEWER))
|
||||||
super.setChanged()
|
val energyConfig = ConfigurableEnergy(energy)
|
||||||
|
|
||||||
val level = level
|
val container: MatteryContainer = object : MatteryContainer(this::setChangedLight, 1) {
|
||||||
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) {
|
|
||||||
override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) {
|
override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) {
|
||||||
super.setChanged(slot, new, old)
|
super.setChanged(slot, new, old)
|
||||||
|
|
||||||
val level = level
|
val level = level
|
||||||
|
|
||||||
if (level is ServerLevel)
|
if (level is ServerLevel) {
|
||||||
tickList.once {
|
tickList.once {
|
||||||
if (!isRemoved) {
|
val isPresent = new.getCapability(MatteryCapability.DRIVE).isPresent
|
||||||
var state = blockState
|
var state = this@DriveViewerBlockEntity.blockState.setValue(DriveViewerBlock.DRIVE_PRESENT, isPresent)
|
||||||
|
|
||||||
if (new.getCapability(MatteryCapability.DRIVE).isPresent) {
|
if (!isPresent) {
|
||||||
state = state.setValue(DriveViewerBlock.DRIVE_PRESENT, true)
|
state = state.setValue(WorkerState.SEMI_WORKER_STATE, WorkerState.IDLE)
|
||||||
} else {
|
} else if (energy.batteryLevel >= Decimal.TEN && !redstoneControl.isBlockedByRedstone) {
|
||||||
state = state.setValue(DriveViewerBlock.DRIVE_PRESENT, false)
|
state = state.setValue(WorkerState.SEMI_WORKER_STATE, WorkerState.WORKING)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state !== blockState) {
|
if (state !== this@DriveViewerBlockEntity.blockState) {
|
||||||
level.setBlock(blockPos, state, Block.UPDATE_CLIENTS)
|
level.setBlock(this@DriveViewerBlockEntity.blockPos, state, Block.UPDATE_CLIENTS)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}.also(::addDroppableContainer)
|
}.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 {
|
init {
|
||||||
savetable(::energy, ENERGY_KEY)
|
savetable(::energy, ENERGY_KEY)
|
||||||
savetable(::container, INVENTORY_KEY)
|
savetable(::container, INVENTORY_KEY)
|
||||||
|
|
||||||
exposeEnergyGlobally(energy)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {
|
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package ru.dbotthepony.mc.otm.block.entity.storage
|
package ru.dbotthepony.mc.otm.block.entity.storage
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
|
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
|
||||||
import net.minecraft.core.BlockPos
|
import net.minecraft.core.BlockPos
|
||||||
import net.minecraft.core.NonNullList
|
import net.minecraft.core.NonNullList
|
||||||
import net.minecraft.nbt.CompoundTag
|
import net.minecraft.nbt.CompoundTag
|
||||||
import net.minecraft.network.FriendlyByteBuf
|
|
||||||
import net.minecraft.network.chat.Component
|
import net.minecraft.network.chat.Component
|
||||||
import net.minecraft.server.level.ServerPlayer
|
import net.minecraft.server.level.ServerPlayer
|
||||||
import net.minecraft.world.Container
|
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.minecraft.world.level.block.state.BlockState
|
||||||
import net.minecraftforge.common.ForgeHooks
|
import net.minecraftforge.common.ForgeHooks
|
||||||
import net.minecraftforge.common.util.INBTSerializable
|
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.core.TranslatableComponent
|
||||||
import ru.dbotthepony.mc.otm.block.entity.MatteryPoweredBlockEntity
|
import ru.dbotthepony.mc.otm.block.entity.MatteryPoweredBlockEntity
|
||||||
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
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.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.config.MachinesConfig
|
||||||
import ru.dbotthepony.mc.otm.container.MatteryContainer
|
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.StorageNode
|
||||||
import ru.dbotthepony.mc.otm.graph.storage.StorageGraph
|
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.registry.MBlockEntities
|
||||||
import ru.dbotthepony.mc.otm.container.set
|
import ru.dbotthepony.mc.otm.container.set
|
||||||
import ru.dbotthepony.mc.otm.core.nbt.mapString
|
import ru.dbotthepony.mc.otm.core.nbt.mapString
|
||||||
@ -41,122 +40,32 @@ import ru.dbotthepony.mc.otm.storage.*
|
|||||||
import ru.dbotthepony.mc.otm.storage.powered.PoweredVirtualComponent
|
import ru.dbotthepony.mc.otm.storage.powered.PoweredVirtualComponent
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.function.Supplier
|
|
||||||
import kotlin.collections.HashMap
|
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 {
|
interface IItemMonitorPlayerSettings {
|
||||||
enum class IngredientPriority(val component: Component) {
|
var ingredientPriority: ItemMonitorPlayerSettings.IngredientPriority
|
||||||
// Refill everything from system
|
var resultTarget: ItemMonitorPlayerSettings.ResultTarget
|
||||||
SYSTEM(TranslatableComponent("otm.gui.item_monitor.refill_source.system")),
|
var craftingAmount: ItemMonitorPlayerSettings.Amount
|
||||||
|
var sorting: ItemStorageStackSorter
|
||||||
// Refill everything from player's inventory
|
var ascendingSort: Boolean
|
||||||
INVENTORY(TranslatableComponent("otm.gui.item_monitor.refill_source.inventory")),
|
|
||||||
|
|
||||||
// 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")),
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class ResultTarget(val component: Component) {
|
|
||||||
// Everything goes into storage system
|
|
||||||
ALL_SYSTEM(TranslatableComponent("otm.gui.item_monitor.result_target.all_system")),
|
|
||||||
|
|
||||||
// Result goes to player inventory
|
|
||||||
// Crafting remainder goes to storage system
|
|
||||||
MIXED(TranslatableComponent("otm.gui.item_monitor.result_target.mixed")),
|
|
||||||
|
|
||||||
// Everything goes into player inventory
|
|
||||||
ALL_INVENTORY(TranslatableComponent("otm.gui.item_monitor.result_target.all_inventory")),
|
|
||||||
}
|
|
||||||
|
|
||||||
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"))
|
|
||||||
}
|
|
||||||
|
|
||||||
var ingredientPriority = IngredientPriority.SYSTEM
|
|
||||||
var resultTarget = ResultTarget.MIXED
|
|
||||||
var craftingAmount = Amount.STACK
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
private fun takeOne(inventory: CombinedContainer?, item: ItemStack): Boolean {
|
||||||
for (i in 0 until inventory.containerSize) {
|
val iterator = inventory?.optimizedIterator() ?: return false
|
||||||
if (!inventory[i].isEmpty && ItemStack.isSameItemSameTags(inventory[i], item)) {
|
|
||||||
inventory.removeItem(i, 1)
|
for (stack in iterator) {
|
||||||
|
if (stack.equals(item, false)) {
|
||||||
|
stack.shrink(1)
|
||||||
|
inventory.setChanged()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -164,21 +73,110 @@ private fun takeOne(inventory: Inventory, item: ItemStack): Boolean {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun takeOne(id: UUID?, view: IStorageProvider<ItemStorageStack>): Boolean {
|
private fun takeOne(id: UUID?, view: IStorageProvider<ItemStorageStack>?): Boolean {
|
||||||
val extracted = view.extractStack(id ?: return false, BigInteger.ONE, false)
|
return view?.extractStack(id ?: return false, BigInteger.ONE, false)?.isNotEmpty == true
|
||||||
|
}
|
||||||
|
|
||||||
if (!extracted.isEmpty) {
|
class ItemMonitorPlayerSettings : INBTSerializable<CompoundTag>, IItemMonitorPlayerSettings {
|
||||||
return true
|
interface Setting {
|
||||||
|
val component: Component
|
||||||
|
val icon: IGUIRenderable
|
||||||
|
val winding: UVWindingOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
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"), 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"), lazy { Widgets8.ARROW_SIDEWAYS }),
|
||||||
|
|
||||||
|
// Everything goes into player 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(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
|
||||||
|
}
|
||||||
|
|
||||||
|
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["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("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")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ItemMonitorBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryPoweredBlockEntity(MBlockEntities.ITEM_MONITOR, blockPos, blockState), IStorageEventConsumer<ItemStorageStack> {
|
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 {
|
init {
|
||||||
exposeEnergyGlobally(energy)
|
|
||||||
savetable(::energy, ENERGY_KEY)
|
savetable(::energy, ENERGY_KEY)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,79 +201,74 @@ class ItemMonitorBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matte
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val settings = HashMap<UUID, ItemMonitorPlayerSettings>()
|
private val settings = HashMap<UUID, ItemMonitorPlayerSettings>()
|
||||||
private val craftingAmount = Object2ObjectArrayMap<Player, Int>()
|
private val craftingAmount = Object2IntOpenHashMap<Player>()
|
||||||
private val lastCraftingRecipe = Object2ObjectArrayMap<Player, CraftingRecipe>()
|
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]
|
fun lastCraftingRecipe(ply: Player) = lastCraftingRecipe[ply]
|
||||||
|
|
||||||
// a lot of code is hardcoded to take CraftingContainer as it's input
|
// 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
|
// 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) {
|
override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) {
|
||||||
super.setChanged(slot, new, old)
|
setChangedLight()
|
||||||
craftingGridDummy[slot] = new
|
craftingGridVanilla[slot] = new
|
||||||
|
|
||||||
if (!inProcessOfCraft)
|
if (!inProcessOfCraft) {
|
||||||
scanCraftingGrid()
|
scanCraftingGrid(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}.also(::addDroppableContainer)
|
}.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() {
|
private fun scanCraftingGrid(justCheckForRecipeChange: Boolean): Boolean {
|
||||||
val server = level?.server ?: return
|
val level = level ?: return false
|
||||||
val oldRecipe = craftingRecipe
|
val server = level.server ?: return false
|
||||||
|
|
||||||
if (oldRecipe != null && oldRecipe.matches(craftingGridDummy, level!!)) {
|
var craftingRecipe = craftingRecipe
|
||||||
return
|
if (craftingRecipe != null && craftingRecipe.matches(craftingGridVanilla, level)) return true
|
||||||
}
|
if (justCheckForRecipeChange) return false
|
||||||
|
|
||||||
craftingRecipe = server.recipeManager.getRecipeFor(RecipeType.CRAFTING, craftingGridDummy, level!!).orElse(null)
|
craftingRecipe = server.recipeManager.getRecipeFor(RecipeType.CRAFTING, craftingGridVanilla, level).orElse(null)
|
||||||
val craftingRecipe = craftingRecipe
|
Arrays.fill(craftingGridTuples, null)
|
||||||
|
val poweredView = poweredView
|
||||||
|
|
||||||
if (oldRecipe != craftingRecipe) {
|
if (craftingRecipe != null && poweredView != null) {
|
||||||
Arrays.fill(craftingGridTuples, null)
|
for (i in craftingGridTuples.indices) {
|
||||||
|
val item = craftingGrid[i]
|
||||||
|
|
||||||
val poweredView = poweredView
|
if (!item.isEmpty) {
|
||||||
|
craftingGridTuples[i] = poweredView[ItemStorageStack.unsafe(item)]
|
||||||
if (craftingRecipe != null && poweredView != null) {
|
|
||||||
for (i in craftingGridTuples.indices) {
|
|
||||||
val item = craftingGrid[i]
|
|
||||||
|
|
||||||
if (!item.isEmpty) {
|
|
||||||
craftingGridTuples[i] = poweredView[ItemStorageStack.unsafe(item)]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.craftingRecipe = craftingRecipe
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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>
|
override val storageType: StorageStack.Type<ItemStorageStack>
|
||||||
get() = StorageStack.ITEMS
|
get() = StorageStack.ITEMS
|
||||||
|
|
||||||
override fun onStackAdded(stack: ItemStorageStack, id: UUID, provider: IStorageProvider<ItemStorageStack>) {
|
override fun onStackAdded(stack: ItemStorageStack, id: UUID, provider: IStorageProvider<ItemStorageStack>) {
|
||||||
// no op
|
if (craftingRecipe != null) {
|
||||||
}
|
for (i in craftingGridTuples.indices) {
|
||||||
|
val item = craftingGrid[i]
|
||||||
override fun onStackChanged(stack: ItemStorageStack, id: UUID) {
|
|
||||||
// no op
|
if (!item.isEmpty && stack.equalsWithoutCount(ItemStorageStack.unsafe(item))) {
|
||||||
|
craftingGridTuples[i] = id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onStackChanged(stack: ItemStorageStack, id: UUID) {}
|
||||||
override fun onStackRemoved(id: UUID) {
|
override fun onStackRemoved(id: UUID) {
|
||||||
for (i in craftingGridTuples.indices) {
|
for (i in craftingGridTuples.indices) {
|
||||||
if (craftingGridTuples[i] == id) {
|
if (craftingGridTuples[i] == id) {
|
||||||
@ -300,137 +293,112 @@ class ItemMonitorBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matte
|
|||||||
|
|
||||||
override fun getContainerSize() = 1
|
override fun getContainerSize() = 1
|
||||||
override fun isEmpty() = craftingRecipe == null
|
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 {
|
override fun getItem(slot: Int): ItemStack {
|
||||||
require(p_18941_ == 0) { "Invalid slot ID: $p_18941_" }
|
require(slot == 0) { "Invalid slot: $slot" }
|
||||||
return craftingRecipe?.getResultItem(level?.registryAccess() ?: return ItemStack.EMPTY)?.copy() ?: ItemStack.EMPTY
|
return craftingRecipe?.getResultItem(level?.registryAccess() ?: return ItemStack.EMPTY)?.copy() ?: ItemStack.EMPTY
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun removeItem(index: Int, amount: Int): ItemStack {
|
override fun removeItem(index: Int, amount: Int): ItemStack {
|
||||||
require(index == 0) { "Invalid index $index" }
|
require(index == 0) { "Invalid index $index" }
|
||||||
|
|
||||||
val craftingRecipe = craftingRecipe
|
val level = level ?: return ItemStack.EMPTY
|
||||||
val craftingPlayer = craftingPlayer
|
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 {
|
||||||
return ItemStack.EMPTY
|
ForgeHooks.setCraftingPlayer(craftingPlayer)
|
||||||
|
|
||||||
|
if (craftingRecipe.getResultItem(level.registryAccess()).count != amount) {
|
||||||
|
return ItemStack.EMPTY
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
ForgeHooks.setCraftingPlayer(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
var crafts = craftingAmount[craftingPlayer] ?: 0
|
craftingAmount[craftingPlayer] = craftingAmount.getInt(craftingPlayer) + 1
|
||||||
crafts++
|
|
||||||
craftingAmount[craftingPlayer] = crafts
|
|
||||||
lastCraftingRecipe[craftingPlayer] = craftingRecipe
|
lastCraftingRecipe[craftingPlayer] = craftingRecipe
|
||||||
|
|
||||||
|
tickList.namedTimer(craftingPlayer, 4) {
|
||||||
|
craftingAmount.removeInt(craftingPlayer)
|
||||||
|
lastCraftingRecipe.remove(craftingPlayer)
|
||||||
|
}
|
||||||
|
|
||||||
val settings = getSettings(craftingPlayer)
|
val settings = getSettings(craftingPlayer)
|
||||||
|
|
||||||
inProcessOfCraft = true
|
inProcessOfCraft = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ForgeHooks.setCraftingPlayer(craftingPlayer)
|
ForgeHooks.setCraftingPlayer(craftingPlayer)
|
||||||
val remainingItems: NonNullList<ItemStack> = craftingRecipe.getRemainingItems(craftingGridDummy)
|
val residue: NonNullList<ItemStack>
|
||||||
ForgeHooks.setCraftingPlayer(null)
|
val result: ItemStack
|
||||||
|
|
||||||
check(remainingItems.size == craftingGrid.containerSize) { "${remainingItems.size} != ${craftingGrid.containerSize} !!!" }
|
try {
|
||||||
|
residue = craftingRecipe.getRemainingItems(craftingGridVanilla)
|
||||||
|
result = craftingRecipe.getResultItem(level.registryAccess())
|
||||||
|
} finally {
|
||||||
|
ForgeHooks.setCraftingPlayer(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
for (slot in 0 until craftingGrid.containerSize) {
|
||||||
val oldItem = craftingGrid[slot].let { if (!it.isEmpty || it.count < 2) it.copy() else it }
|
craftingGrid.removeItem(slot, 1)
|
||||||
|
|
||||||
if (!craftingGrid[slot].isEmpty) {
|
if (residue[slot].isNotEmpty) {
|
||||||
craftingGrid.removeItem(slot, 1)
|
craftingGrid[slot] = residue[slot]
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var newItem = craftingGrid[slot]
|
if (!scanCraftingGrid(true)) {
|
||||||
|
// заполняем опустевшие слоты
|
||||||
if (newItem.isEmpty) {
|
for (slot in 0 until craftingGrid.containerSize) {
|
||||||
when (settings.ingredientPriority) {
|
if (
|
||||||
ItemMonitorPlayerSettings.IngredientPriority.SYSTEM -> {
|
craftingGrid[slot].isEmpty &&
|
||||||
if (poweredView != null && takeOne(craftingGridTuples[slot], poweredView!!)) {
|
copy[slot].isNotEmpty &&
|
||||||
newItem = oldItem
|
settings.ingredientPriority.takeOne(copy[slot], poweredView, combinedInventory, craftingGridTuples[slot])
|
||||||
craftingGrid[slot] = newItem
|
) {
|
||||||
}
|
craftingGrid[slot] = copy[slot].copyWithCount(1)
|
||||||
}
|
|
||||||
|
|
||||||
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 (!scanCraftingGrid(true)) {
|
||||||
|
// избавляемся от остатков, если они мешают
|
||||||
|
for (slot in 0 until craftingGrid.containerSize) {
|
||||||
|
if (residue[slot].isNotEmpty) {
|
||||||
|
var remaining = residue[slot]
|
||||||
|
|
||||||
if (!remainder.isEmpty) {
|
if (settings.resultTarget != ItemMonitorPlayerSettings.ResultTarget.ALL_INVENTORY) {
|
||||||
when (settings.resultTarget) {
|
remaining = poweredView?.insertStack(ItemStorageStack(remaining), false)?.toItemStack() ?: remaining
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ItemMonitorPlayerSettings.ResultTarget.ALL_INVENTORY -> {
|
remaining = combinedInventory?.addItem(remaining, false) ?: remaining
|
||||||
if (!craftingPlayer.inventory.add(remainder)) {
|
|
||||||
val remaining = poweredView?.insertStack(ItemStorageStack(remainder), false)?.toItemStack() ?: remainder
|
|
||||||
|
|
||||||
if (!remaining.isEmpty) {
|
if (remaining.isNotEmpty) {
|
||||||
if (newItem.isEmpty) {
|
craftingPlayer.spawnAtLocation(remaining)
|
||||||
craftingGrid[slot] = remaining
|
}
|
||||||
} else if (ItemStack.isSameItemSameTags(newItem, remaining)) {
|
|
||||||
newItem.grow(remaining.count)
|
if (copy[slot].isNotEmpty && settings.ingredientPriority.takeOne(copy[slot], poweredView, combinedInventory, craftingGridTuples[slot])) {
|
||||||
craftingGrid.setChanged(slot)
|
craftingGrid[slot] = copy[slot].copyWithCount(1)
|
||||||
} else {
|
|
||||||
craftingPlayer.drop(remaining, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result.copy()
|
||||||
} finally {
|
} finally {
|
||||||
inProcessOfCraft = false
|
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()
|
throw UnsupportedOperationException()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -464,11 +432,7 @@ class ItemMonitorBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matte
|
|||||||
val settings = nbt.getCompound("player_settings")
|
val settings = nbt.getCompound("player_settings")
|
||||||
|
|
||||||
for (key in settings.allKeys) {
|
for (key in settings.allKeys) {
|
||||||
val uuid = UUID.fromString(key)
|
check(this.settings.put(UUID.fromString(key), ItemMonitorPlayerSettings().also { it.deserializeNBT(settings.getCompound(key)) }) == null)
|
||||||
val deserialized = ItemMonitorPlayerSettings()
|
|
||||||
deserialized.deserializeNBT(settings.getCompound(key))
|
|
||||||
|
|
||||||
check(this.settings.put(uuid, deserialized) == null) { "Duplicate UUID??? $uuid" }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
craftingGrid.deserializeNBT(nbt["crafting_grid"])
|
craftingGrid.deserializeNBT(nbt["crafting_grid"])
|
||||||
@ -480,14 +444,7 @@ class ItemMonitorBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matte
|
|||||||
|
|
||||||
override fun tick() {
|
override fun tick() {
|
||||||
super.tick()
|
super.tick()
|
||||||
|
|
||||||
cell.tickEnergyDemanding()
|
cell.tickEnergyDemanding()
|
||||||
|
|
||||||
if (craftingAmount.size != 0)
|
|
||||||
craftingAmount.clear()
|
|
||||||
|
|
||||||
if (lastCraftingRecipe.size != 0)
|
|
||||||
lastCraftingRecipe.clear()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {
|
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) {
|
override fun setLevel(level: Level) {
|
||||||
super.setLevel(level)
|
super.setLevel(level)
|
||||||
cell.discover(this)
|
cell.discover(this)
|
||||||
|
scanCraftingGrid(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setRemoved() {
|
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.Int2ObjectAVLTreeMap
|
||||||
import it.unimi.dsi.fastutil.ints.IntAVLTreeSet
|
import it.unimi.dsi.fastutil.ints.IntAVLTreeSet
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntArrayList
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap
|
||||||
import net.minecraft.core.BlockPos
|
import net.minecraft.core.BlockPos
|
||||||
import net.minecraft.world.entity.player.Inventory
|
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 inner class ItemHandlerComponent(private val parent: IItemHandler) : IStorageComponent<ItemStorageStack> {
|
||||||
private fun updateTuple(tuple: TrackedTuple, diff: BigInteger) {
|
private fun updateTuple(tuple: TrackedTuple, diff: BigInteger) {
|
||||||
|
val stack = tuple.stack
|
||||||
tuple.stack = tuple.stack.grow(diff)
|
tuple.stack = tuple.stack.grow(diff)
|
||||||
|
|
||||||
if (tuple.stack.isNotEmpty) {
|
if (tuple.stack.isNotEmpty) {
|
||||||
@ -138,7 +140,7 @@ class StorageBusBlockEntity(blockPos: BlockPos, blockState: BlockState) : Matter
|
|||||||
}
|
}
|
||||||
|
|
||||||
id2tuples.remove(tuple.id)
|
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 tuple = id2tuples[id] ?: return ItemStorageStack.EMPTY
|
||||||
val slots = tuple.children.values.iterator()
|
val slots = tuple.children.values.iterator()
|
||||||
val lstack = tuple.stack
|
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 (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))
|
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) {
|
if (simulate) {
|
||||||
total += extracted.count.toBigInteger()
|
total += extracted.count.toBigInteger()
|
||||||
} else {
|
} else {
|
||||||
|
affectedSlots.add(slot)
|
||||||
val extracted2 = parent.extractItem(slot, extracted.count, false)
|
val extracted2 = parent.extractItem(slot, extracted.count, false)
|
||||||
|
total += extracted2.count.toBigInteger()
|
||||||
|
|
||||||
if (extracted2.count == extracted.count) {
|
if (extracted2.count == extracted.count) {
|
||||||
energy.extractEnergy(required, false)
|
energy.extractEnergy(required, false)
|
||||||
} else {
|
} else {
|
||||||
energy.extractEnergy(StorageStack.ITEMS.energyPerExtract(ItemStorageStack.unsafe(extracted2)), false)
|
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)
|
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.Block
|
||||||
import net.minecraft.world.level.block.state.BlockState
|
import net.minecraft.world.level.block.state.BlockState
|
||||||
import net.minecraft.world.phys.AABB
|
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.MatteryPoweredBlockEntity
|
||||||
import ru.dbotthepony.mc.otm.block.entity.WorkerState
|
import ru.dbotthepony.mc.otm.block.entity.WorkerState
|
||||||
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
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.capability.moveEnergy
|
||||||
import ru.dbotthepony.mc.otm.config.MachinesConfig
|
import ru.dbotthepony.mc.otm.config.MachinesConfig
|
||||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
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.core.ifPresentK
|
||||||
import ru.dbotthepony.mc.otm.menu.tech.AndroidStationMenu
|
import ru.dbotthepony.mc.otm.menu.tech.AndroidStationMenu
|
||||||
import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
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) :
|
class AndroidStationBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
|
||||||
MatteryPoweredBlockEntity(MBlockEntities.ANDROID_STATION, p_155229_, p_155230_), MenuProvider {
|
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.Inventory
|
||||||
import net.minecraft.world.entity.player.Player
|
import net.minecraft.world.entity.player.Player
|
||||||
import net.minecraft.world.inventory.AbstractContainerMenu
|
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.Block
|
||||||
import net.minecraft.world.level.block.state.BlockState
|
import net.minecraft.world.level.block.state.BlockState
|
||||||
import net.minecraftforge.common.ForgeConfigSpec
|
|
||||||
import net.minecraftforge.common.ForgeHooks
|
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.MatteryDeviceBlockEntity
|
||||||
import ru.dbotthepony.mc.otm.block.entity.WorkerState
|
import ru.dbotthepony.mc.otm.block.entity.WorkerState
|
||||||
import ru.dbotthepony.mc.otm.capability.*
|
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.config.MachinesConfig
|
||||||
import ru.dbotthepony.mc.otm.container.MatteryContainer
|
import ru.dbotthepony.mc.otm.container.MatteryContainer
|
||||||
import ru.dbotthepony.mc.otm.container.HandlerFilter
|
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.menu.tech.ChemicalGeneratorMenu
|
||||||
import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
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.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) {
|
class ChemicalGeneratorBlockEntity(pos: BlockPos, state: BlockState) : MatteryDeviceBlockEntity(MBlockEntities.CHEMICAL_GENERATOR, pos, state) {
|
||||||
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {
|
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.Inventory
|
||||||
import net.minecraft.world.entity.player.Player
|
import net.minecraft.world.entity.player.Player
|
||||||
import net.minecraft.world.inventory.AbstractContainerMenu
|
import net.minecraft.world.inventory.AbstractContainerMenu
|
||||||
import net.minecraft.world.item.ItemStack
|
|
||||||
import net.minecraft.world.level.block.state.BlockState
|
import net.minecraft.world.level.block.state.BlockState
|
||||||
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
|
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
|
||||||
import ru.dbotthepony.mc.otm.capability.FlowDirection
|
import ru.dbotthepony.mc.otm.capability.FlowDirection
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package ru.dbotthepony.mc.otm.block.entity.tech
|
package ru.dbotthepony.mc.otm.block.entity.tech
|
||||||
|
|
||||||
import net.minecraft.core.BlockPos
|
import net.minecraft.core.BlockPos
|
||||||
import net.minecraft.server.level.ServerPlayer
|
|
||||||
import net.minecraft.world.entity.player.Inventory
|
import net.minecraft.world.entity.player.Inventory
|
||||||
import net.minecraft.world.entity.player.Player
|
import net.minecraft.world.entity.player.Player
|
||||||
import net.minecraft.world.inventory.AbstractContainerMenu
|
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.Object2ObjectOpenCustomHashMap
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet
|
||||||
import kotlin.jvm.JvmOverloads
|
import kotlin.jvm.JvmOverloads
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import net.minecraft.nbt.CompoundTag
|
import net.minecraft.nbt.CompoundTag
|
||||||
@ -30,7 +31,7 @@ abstract class AbstractMatteryDrive<T : StorageStack<T>> @JvmOverloads construct
|
|||||||
|
|
||||||
override var isDirty = false
|
override var isDirty = false
|
||||||
set(value) {
|
set(value) {
|
||||||
if (value != field && value && DrivePool.isLegalAccess()) {
|
if (value != field && value) {
|
||||||
DrivePool.markDirty(uuid)
|
DrivePool.markDirty(uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,6 +44,24 @@ abstract class AbstractMatteryDrive<T : StorageStack<T>> @JvmOverloads construct
|
|||||||
override var storedCount: BigInteger = BigInteger.ZERO
|
override var storedCount: BigInteger = BigInteger.ZERO
|
||||||
protected set
|
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 {
|
override fun insertStack(stack: T, simulate: Boolean): T {
|
||||||
val maxInsert = driveCapacity.minus(storedCount).coerceAtMost(stack.count)
|
val maxInsert = driveCapacity.minus(storedCount).coerceAtMost(stack.count)
|
||||||
if (maxInsert <= BigInteger.ZERO) return stack
|
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() {
|
override val stacks: Stream<IStorageTuple<T>> get() {
|
||||||
return ArrayList<IStorageTuple<T>>(stack2tuples.size).also { it.addAll(stack2tuples.values) }.stream()
|
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
|
package ru.dbotthepony.mc.otm.capability.drive
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectAVLTreeMap
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import net.minecraft.ReportedException
|
import net.minecraft.ReportedException
|
||||||
import net.minecraft.nbt.CompoundTag
|
import net.minecraft.nbt.CompoundTag
|
||||||
import net.minecraft.nbt.NbtIo
|
import net.minecraft.nbt.NbtIo
|
||||||
import net.minecraft.CrashReport
|
import net.minecraft.CrashReport
|
||||||
|
import net.minecraft.world.level.storage.LevelResource
|
||||||
import java.util.concurrent.locks.LockSupport
|
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.ServerAboutToStartEvent
|
||||||
import net.minecraftforge.event.server.ServerStoppingEvent
|
import net.minecraftforge.event.server.ServerStoppingEvent
|
||||||
import net.minecraftforge.event.level.LevelEvent
|
import net.minecraftforge.event.level.LevelEvent
|
||||||
@ -19,51 +17,82 @@ import ru.dbotthepony.mc.otm.SERVER_IS_LIVE
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
import java.util.ArrayList
|
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:
|
* Separate thread and separate files are way faster for enormous volumes of data (if mod is being run on public dedicated server)
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
object DrivePool {
|
object DrivePool {
|
||||||
|
private val resource = LevelResource("otm_drives")
|
||||||
private val LOGGER = LogManager.getLogger()
|
private val LOGGER = LogManager.getLogger()
|
||||||
private val pool = Object2ObjectOpenHashMap<UUID, WeakDriveReference>()
|
private val pool = Object2ObjectOpenHashMap<UUID, WeakDriveReference>()
|
||||||
private var thread: Thread? = null
|
private var thread: Thread? = null
|
||||||
private var stopping = false
|
private var stopping = false
|
||||||
private var exception: ReportedException? = null
|
|
||||||
private const val DATA_PATH = "data/otm_drives"
|
|
||||||
|
|
||||||
@JvmStatic
|
private val backlog = ConcurrentLinkedQueue<BacklogLine>()
|
||||||
operator fun <T : IMatteryDrive<*>> get(
|
private var knownBaseDirectory: File? = null
|
||||||
id: UUID,
|
|
||||||
deserializer: (CompoundTag) -> T,
|
private var serverThread: Thread? = null
|
||||||
factory: () -> T
|
|
||||||
): T {
|
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())
|
if (!isLegalAccess())
|
||||||
throw IllegalAccessException("Can not access drive pool from outside of server thread.")
|
throw IllegalAccessException("Can not access drive pool from outside of server thread.")
|
||||||
|
|
||||||
if (!SERVER_IS_LIVE)
|
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) {
|
if (get != null) {
|
||||||
val accessed = get.access()
|
return get as T
|
||||||
|
|
||||||
if (accessed != null) {
|
|
||||||
return accessed as T
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val file = File(baseDirectory, "$id.dat")
|
val file = File(baseDirectory, "$id.dat")
|
||||||
@ -85,69 +114,9 @@ object DrivePool {
|
|||||||
return factory().also { pool[id] = WeakDriveReference(it) }
|
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) {
|
fun markDirty(id: UUID) {
|
||||||
if (!isLegalAccess())
|
if (isLegalAccess()) {
|
||||||
throw IllegalAccessException("Can not access drive pool from outside of server thread.")
|
pool[id]?.access()
|
||||||
|
|
||||||
if (!SERVER_IS_LIVE)
|
|
||||||
throw IllegalStateException("Fail-fast: Server is shutting down")
|
|
||||||
|
|
||||||
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.
|
* 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 {
|
fun isLegalAccess(): Boolean {
|
||||||
if (serverThread == null)
|
return serverThread != null && Thread.currentThread() === serverThread
|
||||||
return false
|
|
||||||
|
|
||||||
return Thread.currentThread() === serverThread
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun serverStartEvent(event: ServerAboutToStartEvent) {
|
fun serverStartEvent(event: ServerAboutToStartEvent) {
|
||||||
@ -175,7 +140,7 @@ object DrivePool {
|
|||||||
serverThread = Thread.currentThread()
|
serverThread = Thread.currentThread()
|
||||||
|
|
||||||
stopping = false
|
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) {
|
fun serverStopEvent(event: ServerStoppingEvent) {
|
||||||
@ -186,11 +151,8 @@ object DrivePool {
|
|||||||
LockSupport.unpark(thread)
|
LockSupport.unpark(thread)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exception == null) {
|
writeBacklog()
|
||||||
writeBacklog()
|
sync()
|
||||||
sync()
|
|
||||||
}
|
|
||||||
|
|
||||||
pool.clear()
|
pool.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,34 +162,26 @@ object DrivePool {
|
|||||||
|
|
||||||
private fun writeBacklog() {
|
private fun writeBacklog() {
|
||||||
var needsSync = false
|
var needsSync = false
|
||||||
var removeKeys: ArrayList<UUID>? = null
|
val removeKeys = ArrayList<UUID>()
|
||||||
|
|
||||||
for ((key, value) in pool) {
|
for ((key, value) in pool) {
|
||||||
val tag = value.sync()
|
try {
|
||||||
|
val tag = value.sync()
|
||||||
|
|
||||||
if (tag != null) {
|
if (tag != null) {
|
||||||
LOGGER.info("Serializing OTM Drive {}", key)
|
LOGGER.debug("Serializing OTM Drive {}", key)
|
||||||
val index = backlog.indexOf<Any>(key)
|
backlog.add(BacklogLine(key, tag))
|
||||||
|
needsSync = true
|
||||||
if (index != -1) {
|
} else if (!value.isValid) {
|
||||||
backlog[index] = BacklogLine(key, tag, value.drive()!!)
|
removeKeys.add(key)
|
||||||
} else {
|
|
||||||
backlog.add(BacklogLine(key, tag, value.drive()!!))
|
|
||||||
}
|
}
|
||||||
|
} catch (err: Throwable) {
|
||||||
needsSync = true
|
LOGGER.error("Failed to serialize OTM Drive for persistent storage with ID $key", err)
|
||||||
} else if (!value.valid()) {
|
|
||||||
if (removeKeys == null)
|
|
||||||
removeKeys = ArrayList()
|
|
||||||
|
|
||||||
removeKeys.add(key)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (removeKeys != null) {
|
for (key in removeKeys) {
|
||||||
for (key in removeKeys) {
|
pool.remove(key)
|
||||||
pool.remove(key)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needsSync) {
|
if (needsSync) {
|
||||||
@ -235,90 +189,31 @@ object DrivePool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun run() {
|
||||||
|
while (!stopping) {
|
||||||
|
sync()
|
||||||
|
LockSupport.park()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun sync() {
|
private fun sync() {
|
||||||
val copy = backlog
|
while (true) {
|
||||||
backlog = ArrayList()
|
val next = backlog.poll() ?: return
|
||||||
|
val (uuid, tag) = next
|
||||||
|
|
||||||
for (entry in copy) {
|
|
||||||
try {
|
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()) {
|
if (oldFile.exists()) check(oldFile.delete()) { "Unable to delete old dat file" }
|
||||||
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")
|
NbtIo.writeCompressed(tag, newFile)
|
||||||
|
|
||||||
if (newFile.exists()) {
|
|
||||||
check(newFile.renameTo(oldFile)) { "Unable to move old dat file" }
|
|
||||||
}
|
|
||||||
|
|
||||||
NbtIo.writeCompressed(entry.tag, newFile)
|
|
||||||
} catch (error: Throwable) {
|
} catch (error: Throwable) {
|
||||||
val report = CrashReport("Syncing OTM Drives state to disk", error)
|
LOGGER.error("Failed to sync OTM Drive to persistent storage with ID $uuid", 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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
val controls = DeviceControls(this, frame)
|
||||||
|
|
||||||
LargeBooleanRectangleButtonPanel(
|
controls.sortingButtons(menu.isAscendingGS, menu.sortingGS, ItemSorter.DEFAULT) {
|
||||||
this,
|
for (v in ItemSorter.entries) {
|
||||||
controls,
|
add(v, skinElement = v.icon, tooltip = v.title)
|
||||||
prop = menu.isAscendingGS,
|
}
|
||||||
skinElementActive = Widgets18.ARROW_UP,
|
|
||||||
skinElementInactive = Widgets18.ARROW_DOWN,
|
|
||||||
tooltipActive = TranslatableComponent("otm.gui.sorting.ascending"),
|
|
||||||
tooltipInactive = TranslatableComponent("otm.gui.sorting.descending"),
|
|
||||||
).also {
|
|
||||||
controls.addButton(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
LargeEnumRectangleButtonPanel(this, controls, enum = ItemSorter::class.java, prop = menu.sortingGS, defaultValue = ItemSorter.DEFAULT).also {
|
|
||||||
controls.addButton(it)
|
|
||||||
it.add(ItemSorter.DEFAULT, skinElement = Widgets18.SORT_DEFAULT, tooltip = ItemSorter.DEFAULT.title)
|
|
||||||
it.add(ItemSorter.NAME, skinElement = Widgets18.SORT_ALPHABET, tooltip = ItemSorter.NAME.title)
|
|
||||||
it.add(ItemSorter.ID, skinElement = Widgets18.SORT_ID, tooltip = ItemSorter.ID.title)
|
|
||||||
it.add(ItemSorter.MOD, skinElement = Widgets18.SORT_MODID, tooltip = ItemSorter.MOD.title)
|
|
||||||
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, {
|
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()
|
val list = super.getItemStackTooltip(stack).toMutableList()
|
||||||
|
|
||||||
if (isPatternView) {
|
if (isPatternView) {
|
||||||
@ -326,7 +309,7 @@ class MatterPanelScreen(
|
|||||||
return pattern.stack()
|
return pattern.stack()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemStackTooltip(stack: ItemStack): List<Component> {
|
override fun getItemStackTooltip(stack: ItemStack): MutableList<Component> {
|
||||||
return super.getItemStackTooltip(stack).toMutableList().also {
|
return super.getItemStackTooltip(stack).toMutableList().also {
|
||||||
it.add(TranslatableComponent(
|
it.add(TranslatableComponent(
|
||||||
"otm.item.pattern.research",
|
"otm.item.pattern.research",
|
||||||
|
@ -31,8 +31,26 @@ open class FramePanel<out S : Screen>(
|
|||||||
var onOpen: Runnable? = null,
|
var onOpen: Runnable? = null,
|
||||||
var onClose: Runnable? = null,
|
var onClose: Runnable? = null,
|
||||||
var activeIcon: IGUIRenderable? = null,
|
var activeIcon: IGUIRenderable? = null,
|
||||||
var inactiveIcon: IGUIRenderable? = null,
|
var inactiveIcon: IGUIRenderable? = activeIcon,
|
||||||
) : AbstractButtonPanel<S>(this@FramePanel.screen, this@FramePanel, 0f, 0f, 26f, 28f) {
|
) : 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()
|
var isActive = tabs.isEmpty()
|
||||||
protected set
|
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.TextComponent
|
||||||
import ru.dbotthepony.mc.otm.core.TranslatableComponent
|
import ru.dbotthepony.mc.otm.core.TranslatableComponent
|
||||||
import ru.dbotthepony.mc.otm.core.math.RelativeSide
|
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.core.value
|
||||||
import ru.dbotthepony.mc.otm.menu.UpgradeSlots
|
import ru.dbotthepony.mc.otm.menu.UpgradeSlots
|
||||||
import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback
|
import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback
|
||||||
@ -315,7 +316,6 @@ class DeviceControls<out S : MatteryScreen<*>>(
|
|||||||
val upgradesButton: LargeRectangleButtonPanel<S>?
|
val upgradesButton: LargeRectangleButtonPanel<S>?
|
||||||
|
|
||||||
private var upgradeWindow: FramePanel<S>? = null
|
private var upgradeWindow: FramePanel<S>? = null
|
||||||
|
|
||||||
private var nextY = 0f
|
private var nextY = 0f
|
||||||
|
|
||||||
fun <P : EditablePanel<@UnsafeVariance S>> addButton(button: P): P {
|
fun <P : EditablePanel<@UnsafeVariance S>> addButton(button: P): P {
|
||||||
@ -328,6 +328,40 @@ class DeviceControls<out S : MatteryScreen<*>>(
|
|||||||
return button
|
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 {
|
init {
|
||||||
for (button in extra) {
|
for (button in extra) {
|
||||||
addButton(button)
|
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)
|
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.Dock
|
||||||
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
|
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,
|
screen: S,
|
||||||
parent: EditablePanel<*>?,
|
parent: EditablePanel<*>?,
|
||||||
x: Float = 0f,
|
x: Float = 0f,
|
||||||
y: Float = 0f,
|
y: Float = 0f,
|
||||||
width: Float,
|
width: Float = 0f,
|
||||||
height: Float,
|
height: Float = 0f,
|
||||||
protected var columns: Int,
|
columns: Int,
|
||||||
protected var rows: Int
|
rows: Int
|
||||||
) : EditablePanel<S>(screen, parent, x, y, width, height) {
|
) : 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() {
|
override fun performLayout() {
|
||||||
var currentX = 0f
|
var currentX = 0f
|
||||||
var currentY = 0f
|
var currentY = 0f
|
||||||
|
@ -1,23 +1,26 @@
|
|||||||
package ru.dbotthepony.mc.otm.client.screen.storage
|
package ru.dbotthepony.mc.otm.client.screen.storage
|
||||||
|
|
||||||
import net.minecraft.client.gui.GuiGraphics
|
|
||||||
import net.minecraft.network.chat.Component
|
import net.minecraft.network.chat.Component
|
||||||
import net.minecraft.world.entity.player.Inventory
|
import net.minecraft.world.entity.player.Inventory
|
||||||
import net.minecraft.world.item.ItemStack
|
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.client.screen.MatteryScreen
|
||||||
import ru.dbotthepony.mc.otm.core.TranslatableComponent
|
import ru.dbotthepony.mc.otm.core.TranslatableComponent
|
||||||
import ru.dbotthepony.mc.otm.client.screen.panels.*
|
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.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.AbstractSlotPanel
|
||||||
import ru.dbotthepony.mc.otm.client.screen.panels.slot.BatterySlotPanel
|
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.FilterSlotPanel
|
||||||
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
|
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.panels.util.GridPanel
|
||||||
import ru.dbotthepony.mc.otm.client.screen.widget.PowerGaugePanel
|
import ru.dbotthepony.mc.otm.client.screen.widget.WideProfiledPowerGaugePanel
|
||||||
import ru.dbotthepony.mc.otm.core.math.integerDivisionDown
|
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.item.PortableCondensationDriveItem
|
||||||
import ru.dbotthepony.mc.otm.menu.storage.DriveViewerMenu
|
import ru.dbotthepony.mc.otm.menu.storage.DriveViewerMenu
|
||||||
|
import ru.dbotthepony.mc.otm.registry.MItems
|
||||||
import yalter.mousetweaks.api.MouseTweaksDisableWheelTweak
|
import yalter.mousetweaks.api.MouseTweaksDisableWheelTweak
|
||||||
|
|
||||||
@MouseTweaksDisableWheelTweak
|
@MouseTweaksDisableWheelTweak
|
||||||
@ -25,106 +28,58 @@ class DriveViewerScreen(menu: DriveViewerMenu, inventory: Inventory, title: Comp
|
|||||||
MatteryScreen<DriveViewerMenu>(menu, inventory, title) {
|
MatteryScreen<DriveViewerMenu>(menu, inventory, title) {
|
||||||
|
|
||||||
override fun makeMainFrame(): FramePanel<MatteryScreen<*>> {
|
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.makeCloseButton()
|
||||||
frame.onClose { onClose() }
|
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<*>>()
|
val settings = ArrayList<EditablePanel<*>>()
|
||||||
|
|
||||||
frame.Tab(onOpen = {
|
view.add(EditablePanel(this, frame, width = AbstractSlotPanel.SIZE).also {
|
||||||
for (panel in views) {
|
it.dock = Dock.LEFT
|
||||||
panel.visible = true
|
WideProfiledPowerGaugePanel(this, it, menu.profiledEnergy).also {
|
||||||
}
|
it.dock = Dock.TOP
|
||||||
}, onClose = {
|
it.dockBottom = 6f
|
||||||
for (panel in views) {
|
|
||||||
panel.visible = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BatterySlotPanel(this, it, menu.batterySlot).dock = Dock.TOP
|
||||||
|
SlotPanel(this, it, menu.driveSlot).dock = Dock.TOP
|
||||||
})
|
})
|
||||||
|
|
||||||
frame.Tab(onOpen = {
|
view.add(NetworkedItemGridPanel(this, frame, menu.networkedItemView).also {
|
||||||
for (panel in settings) {
|
it.dock = Dock.FILL
|
||||||
panel.visible = true
|
it.setDockMargin(4f, 0f, 0f, 0f)
|
||||||
}
|
|
||||||
}, onClose = {
|
|
||||||
for (panel in settings) {
|
|
||||||
panel.visible = false
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
views.add(PowerGaugePanel(this, frame, menu.energyWidget, 8f, 16f))
|
val filterGrid = GridPanel(this, frame, width = AbstractSlotPanel.SIZE * 3f, height = AbstractSlotPanel.SIZE * 4f, rows = 3, columns = 4)
|
||||||
views.add(BatterySlotPanel(this, frame, menu.batterySlot, 8f, 67f))
|
filterGrid.dock = Dock.FILL
|
||||||
views.add(SlotPanel(this, frame, menu.driveSlot, 8f, 85f))
|
filterGrid.dockResize = DockResizeMode.NONE
|
||||||
|
filterGrid.dockTop = 20f
|
||||||
val grid = GridPanel(this, frame, 28f, 16f, GRID_WIDTH * 18f, GRID_HEIGHT * 18f, GRID_WIDTH, GRID_HEIGHT)
|
settings.add(filterGrid)
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
for (i in 0 until PortableCondensationDriveItem.MAX_FILTERS) {
|
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) }
|
settings.add(EditablePanel(this, frame, width = 90f).also {
|
||||||
CheckBoxLabelInputPanel(this, dock_left, menu.matchTag, TranslatableComponent("otm.gui.filter.match_tag"), width = 90f, height = 20f).also { it.setDockMargin(0f, 4f, 0f, 0f) }
|
it.dock = Dock.LEFT
|
||||||
CheckBoxLabelInputPanel(this, dock_left, menu.matchNBT, TranslatableComponent("otm.gui.filter.match_nbt"), width = 90f, height = 20f).also { it.setDockMargin(0f, 4f, 0f, 0f) }
|
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) {
|
frame.Tab(view, activeIcon = ItemStackIcon(ItemStack(MItems.PORTABLE_CONDENSATION_DRIVE)))
|
||||||
panel.visible = false
|
frame.Tab(settings, activeIcon = ItemStackIcon(ItemStack(Items.HOPPER)))
|
||||||
}
|
|
||||||
|
|
||||||
return frame
|
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
|
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.client.gui.GuiGraphics
|
||||||
import net.minecraft.network.chat.Component
|
import net.minecraft.network.chat.Component
|
||||||
import net.minecraft.world.entity.player.Inventory
|
import net.minecraft.world.entity.player.Inventory
|
||||||
import net.minecraft.world.item.ItemStack
|
import net.minecraft.world.item.ItemStack
|
||||||
import net.minecraft.world.item.Items
|
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.block.entity.storage.ItemMonitorPlayerSettings
|
||||||
import ru.dbotthepony.mc.otm.client.render.UVWindingOrder
|
import ru.dbotthepony.mc.otm.client.render.Widgets18
|
||||||
import ru.dbotthepony.mc.otm.client.render.Widgets8
|
|
||||||
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
|
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.Dock
|
||||||
import ru.dbotthepony.mc.otm.client.screen.panels.DockResizeMode
|
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.EditablePanel
|
||||||
import ru.dbotthepony.mc.otm.client.screen.panels.FramePanel
|
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.button.SmallEnumRectangleButtonPanel
|
||||||
import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel
|
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.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.panels.util.GridPanel
|
||||||
import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel
|
import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel
|
||||||
import ru.dbotthepony.mc.otm.client.screen.widget.WidePowerGaugePanel
|
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.asGetterSetter
|
||||||
import ru.dbotthepony.mc.otm.core.math.integerDivisionDown
|
import ru.dbotthepony.mc.otm.core.util.ItemStorageStackSorter
|
||||||
import ru.dbotthepony.mc.otm.core.util.formatReadableNumber
|
|
||||||
import ru.dbotthepony.mc.otm.core.util.formatSiComponent
|
|
||||||
import ru.dbotthepony.mc.otm.menu.storage.ItemMonitorMenu
|
import ru.dbotthepony.mc.otm.menu.storage.ItemMonitorMenu
|
||||||
import ru.dbotthepony.mc.otm.storage.StorageStack
|
|
||||||
import yalter.mousetweaks.api.MouseTweaksDisableWheelTweak
|
import yalter.mousetweaks.api.MouseTweaksDisableWheelTweak
|
||||||
|
|
||||||
@MouseTweaksDisableWheelTweak
|
@MouseTweaksDisableWheelTweak
|
||||||
@ -57,71 +53,17 @@ class ItemMonitorScreen(menu: ItemMonitorMenu, inventory: Inventory, title: Comp
|
|||||||
frame.height = topPanel.height + bottomPanel.height + frame.dockPadding.top + frame.dockPadding.bottom + 3f
|
frame.height = topPanel.height + bottomPanel.height + frame.dockPadding.top + frame.dockPadding.bottom + 3f
|
||||||
frame.width = 178f + frame.dockPadding.left + frame.dockPadding.right
|
frame.width = 178f + frame.dockPadding.left + frame.dockPadding.right
|
||||||
|
|
||||||
val viewScrollBar = DiscreteScrollBarPanel(this, topPanel,
|
val controls = DeviceControls(this, frame)
|
||||||
{ integerDivisionDown(menu.networkedItemView.itemCount, ITEM_GRID_WIDTH) },
|
|
||||||
{ _, _, _ -> },
|
|
||||||
28f + ITEM_GRID_WIDTH * 18f + 2f, 16f, ITEM_GRID_HEIGHT * 18f)
|
|
||||||
|
|
||||||
viewScrollBar.dock = Dock.RIGHT
|
controls.sortingButtons(menu.settings::ascendingSort.asGetterSetter(), menu.settings::sorting.asGetterSetter(), ItemStorageStackSorter.DEFAULT) {
|
||||||
viewScrollBar.setDockMargin(left = 2f)
|
for (v in ItemStorageStackSorter.entries) {
|
||||||
|
add(v, skinElement = v.icon, tooltip = v.title)
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
val craftingGrid = GridPanel(this, bottomPanel, width = 3 * 18f, height = 3 * 18f, columns = 3, rows = 3)
|
||||||
craftingGrid.dock = Dock.LEFT
|
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 arrowLine = EditablePanel(this, arrowAndButtons, y = 38f, height = 8f, width = arrowAndButtons.width)
|
||||||
|
|
||||||
val refillPriority = SmallEnumRectangleButtonPanel(this, arrowLine,
|
SmallEnumRectangleButtonPanel(this, arrowLine,
|
||||||
enum = ItemMonitorPlayerSettings.IngredientPriority::class.java,
|
enum = ItemMonitorPlayerSettings.IngredientPriority::class.java,
|
||||||
prop = menu.settings::ingredientPriority.asGetterSetter(watch = { _, _ -> menu.sendSettingsToServer() }),
|
prop = menu.settings::ingredientPriority.asGetterSetter(),
|
||||||
defaultValue = ItemMonitorPlayerSettings.IngredientPriority.SYSTEM)
|
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"))
|
for (setting in ItemMonitorPlayerSettings.IngredientPriority.entries) {
|
||||||
refillPriority.add(ItemMonitorPlayerSettings.IngredientPriority.SYSTEM, tooltip = ItemMonitorPlayerSettings.IngredientPriority.SYSTEM.component, skinElement = Widgets8.WHITE_ARROW_DOWN, winding = UVWindingOrder.FLIP)
|
it.add(setting, setting.icon, setting.component, setting.winding)
|
||||||
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)
|
|
||||||
|
|
||||||
refillPriority.dock = Dock.LEFT
|
it.dock = Dock.LEFT
|
||||||
|
}
|
||||||
|
|
||||||
val resultAndButtons = EditablePanel(this, bottomPanel, width = 18f)
|
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)
|
SlotPanel(this, resultAndButtons, menu.craftingResult, y = 18f)
|
||||||
|
|
||||||
val resultTarget = SmallEnumRectangleButtonPanel(this, resultAndButtons, y = 38f,
|
SmallEnumRectangleButtonPanel(this, resultAndButtons, y = 38f,
|
||||||
enum = ItemMonitorPlayerSettings.ResultTarget::class.java,
|
enum = ItemMonitorPlayerSettings.ResultTarget::class.java,
|
||||||
prop = menu.settings::resultTarget.asGetterSetter(watch = { _, _ -> menu.sendSettingsToServer() }),
|
prop = menu.settings::resultTarget.asGetterSetter(),
|
||||||
defaultValue = ItemMonitorPlayerSettings.ResultTarget.MIXED)
|
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"))
|
for (setting in ItemMonitorPlayerSettings.ResultTarget.entries) {
|
||||||
resultTarget.add(ItemMonitorPlayerSettings.ResultTarget.MIXED, tooltip = ItemMonitorPlayerSettings.ResultTarget.MIXED.component, skinElement = Widgets8.ARROW_SIDEWAYS)
|
it.add(setting, setting.icon, setting.component, setting.winding)
|
||||||
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)
|
}
|
||||||
|
|
||||||
val craftingAmount = SmallEnumRectangleButtonPanel(this, resultAndButtons, x = 10f, y = 38f,
|
SmallEnumRectangleButtonPanel(this, resultAndButtons, x = 10f, y = 38f,
|
||||||
enum = ItemMonitorPlayerSettings.Amount::class.java,
|
enum = ItemMonitorPlayerSettings.Amount::class.java,
|
||||||
prop = menu.settings::craftingAmount.asGetterSetter(watch = { _, _ -> menu.sendSettingsToServer() }),
|
prop = menu.settings::craftingAmount.asGetterSetter(),
|
||||||
defaultValue = ItemMonitorPlayerSettings.Amount.STACK)
|
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"))
|
for (setting in ItemMonitorPlayerSettings.Amount.entries) {
|
||||||
craftingAmount.add(ItemMonitorPlayerSettings.Amount.ONE, tooltip = ItemMonitorPlayerSettings.Amount.ONE.component, skinElement = Widgets8.ONE)
|
it.add(setting, setting.icon, setting.component, setting.winding)
|
||||||
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)
|
}
|
||||||
|
|
||||||
val craftingHistory = GridPanel(this, bottomPanel, width = 3 * 18f, height = 3 * 18f, columns = 3, rows = 3)
|
val craftingHistory = GridPanel(this, bottomPanel, width = 3 * 18f, height = 3 * 18f, columns = 3, rows = 3)
|
||||||
craftingHistory.dock = Dock.LEFT
|
craftingHistory.dock = Dock.LEFT
|
||||||
@ -227,7 +173,5 @@ class ItemMonitorScreen(menu: ItemMonitorMenu, inventory: Inventory, title: Comp
|
|||||||
companion object {
|
companion object {
|
||||||
const val ITEM_GRID_WIDTH = 9
|
const val ITEM_GRID_WIDTH = 9
|
||||||
const val ITEM_GRID_HEIGHT = 5
|
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_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 STORAGE_INTERFACES = conciseValues("STORAGE_INTERFACES", Decimal(10_000), Decimal(10_000))
|
||||||
val ITEM_MONITOR = conciseValues(MNames.ITEM_MONITOR, Decimal(10_000), Decimal(100))
|
val ITEM_MONITOR = conciseValues(MNames.ITEM_MONITOR, Decimal(10_000), Decimal(10_000))
|
||||||
val DRIVE_VIEWER = conciseValues(MNames.DRIVE_VIEWER, Decimal(10_000), Decimal(100))
|
val DRIVE_VIEWER = conciseValues(MNames.DRIVE_VIEWER, Decimal(10_000), Decimal(10_000))
|
||||||
val DRIVE_RACK = conciseValues(MNames.DRIVE_RACK, Decimal(10_000), Decimal(100))
|
val DRIVE_RACK = conciseValues(MNames.DRIVE_RACK, Decimal(10_000), Decimal(10_000))
|
||||||
|
|
||||||
object AndroidCharger {
|
object AndroidCharger {
|
||||||
val RADIUS_WIDTH: Double by builder
|
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.entity.player.Player
|
||||||
import net.minecraft.world.item.ItemStack
|
import net.minecraft.world.item.ItemStack
|
||||||
import ru.dbotthepony.mc.otm.core.GetterSetter
|
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.filter
|
||||||
import ru.dbotthepony.mc.otm.core.collect.flatMap
|
import ru.dbotthepony.mc.otm.core.collect.flatMap
|
||||||
import ru.dbotthepony.mc.otm.core.collect.map
|
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> {
|
fun optimizedIterator(): Iterator<ItemStack> {
|
||||||
return listOf(
|
return concatIterators(
|
||||||
fullCoverage.iterator().flatMap { it.iterator() },
|
fullCoverage.iterator().flatMap { it.iterator() },
|
||||||
notFullCoverage.values.iterator().flatMap { it.iterator() }.map { it.item }.filter { it.isNotEmpty }
|
notFullCoverage.values.iterator().flatMap { it.iterator() }.map { it.item }.filter { it.isNotEmpty }
|
||||||
).iterator().flatMap { it }
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
class Builder {
|
class Builder {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package ru.dbotthepony.mc.otm.container
|
package ru.dbotthepony.mc.otm.container
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectIterators
|
||||||
import net.minecraft.world.Container
|
import net.minecraft.world.Container
|
||||||
import net.minecraft.world.item.ItemStack
|
import net.minecraft.world.item.ItemStack
|
||||||
import ru.dbotthepony.mc.otm.core.isNotEmpty
|
import ru.dbotthepony.mc.otm.core.isNotEmpty
|
||||||
@ -50,3 +51,32 @@ fun Container.iterator(): IContainerIterator {
|
|||||||
ContainerIterator(this)
|
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.Spliterators
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import java.util.function.Consumer
|
import java.util.function.Consumer
|
||||||
|
import java.util.function.Supplier
|
||||||
import java.util.stream.Stream
|
import java.util.stream.Stream
|
||||||
import java.util.stream.StreamSupport
|
import java.util.stream.StreamSupport
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
@ -367,12 +368,20 @@ fun <T> Comparator<in T>.nullsLast(): Comparator<T?> {
|
|||||||
return Comparator.nullsLast(this as Comparator<in 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]
|
* 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
|
* 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(toIndex >= fromIndex) { "Invalid range: to $toIndex >= from $fromIndex" }
|
||||||
require(fromIndex >= 0) { "Invalid from index: $fromIndex" }
|
require(fromIndex >= 0) { "Invalid from index: $fromIndex" }
|
||||||
require(toIndex >= 0) { "Invalid to index: $toIndex" }
|
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
|
* 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)
|
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) {
|
fun <E : Comparable<E>> MutableList<E>.addSorted(element: E) {
|
||||||
add(searchInsertionIndex(element, ObjectComparators.NATURAL_COMPARATOR), element)
|
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> {
|
fun <V> box(value: V): SentientGetterSetter<V> {
|
||||||
return object : GetterSetter<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
|
private var value = value
|
||||||
|
|
||||||
override fun get(): V {
|
override fun get(): V {
|
||||||
@ -103,12 +109,15 @@ interface GetterSetter<V> : Supplier<V>, Consumer<V>, ReadWriteProperty<Any?, V>
|
|||||||
|
|
||||||
override fun accept(t: V) {
|
override fun accept(t: V) {
|
||||||
this.value = t
|
this.value = t
|
||||||
|
subs.accept(t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface SentientGetterSetter<V> : GetterSetter<V>, ISubscriptable<V>
|
||||||
|
|
||||||
operator fun <T> Supplier<T>.getValue(thisRef: Any?, property: KProperty<*>): T {
|
operator fun <T> Supplier<T>.getValue(thisRef: Any?, property: KProperty<*>): T {
|
||||||
return get()
|
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() {
|
override fun remove() {
|
||||||
throw UnsupportedOperationException()
|
(this@mapToDouble as MutableIterator).remove()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun nextDouble(): Double {
|
override fun nextDouble(): Double {
|
||||||
@ -267,7 +267,7 @@ fun <T> Iterator<T>.mapToInt(mapper: O2IFunction<T>): it.unimi.dsi.fastutil.ints
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun remove() {
|
override fun remove() {
|
||||||
throw UnsupportedOperationException()
|
(this@mapToInt as MutableIterator).remove()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun nextInt(): Int {
|
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)
|
return collector.finisher().apply(instance)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> Iterator<T>.toList(): List<T> {
|
fun <T> Iterator<T>.toList(): MutableList<T> {
|
||||||
val result = ArrayList<T>()
|
val result = ArrayList<T>()
|
||||||
result.addAll(this)
|
result.addAll(this)
|
||||||
return result
|
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 {
|
fun <T> CompoundTag.mapString(index: String, mapper: (String) -> T, orElse: T): T {
|
||||||
val tag = this[index] as? StringTag ?: return orElse
|
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
|
fun CompoundTag.getItemStack(key: String): ItemStack = map(key, ItemStack::of) ?: ItemStack.EMPTY
|
||||||
|
@ -96,7 +96,10 @@ fun BigInteger.formatSiComponent(suffix: Any = "", decimalPlaces: Int = 3, forma
|
|||||||
buffer[add + i + divided.length + 1] = prefix.paddedIndex(remainder, i)
|
buffer[add + i + divided.length + 1] = prefix.paddedIndex(remainder, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
return TranslatableComponent(prefix.formatLocaleKey, String(buffer), suffix)
|
if (suffix == "")
|
||||||
|
return TranslatableComponent(prefix.conciseFormatLocaleKey, String(buffer))
|
||||||
|
else
|
||||||
|
return TranslatableComponent(prefix.formatLocaleKey, String(buffer), suffix)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val never = BooleanSupplier { false }
|
private val never = BooleanSupplier { false }
|
||||||
|
@ -3,17 +3,31 @@ package ru.dbotthepony.mc.otm.core.util
|
|||||||
import it.unimi.dsi.fastutil.objects.Reference2IntFunction
|
import it.unimi.dsi.fastutil.objects.Reference2IntFunction
|
||||||
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap
|
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap
|
||||||
import net.minecraft.network.chat.Component
|
import net.minecraft.network.chat.Component
|
||||||
|
import net.minecraft.network.chat.MutableComponent
|
||||||
import net.minecraft.world.item.CreativeModeTabs
|
import net.minecraft.world.item.CreativeModeTabs
|
||||||
import net.minecraft.world.item.Item
|
import net.minecraft.world.item.Item
|
||||||
|
import net.minecraft.world.item.ItemStack
|
||||||
import net.minecraftforge.common.CreativeModeTabRegistry
|
import net.minecraftforge.common.CreativeModeTabRegistry
|
||||||
import ru.dbotthepony.mc.otm.client.minecraft
|
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.TranslatableComponent
|
||||||
import ru.dbotthepony.mc.otm.core.nullsFirst
|
import ru.dbotthepony.mc.otm.core.nullsFirst
|
||||||
import ru.dbotthepony.mc.otm.core.nullsLast
|
import ru.dbotthepony.mc.otm.core.nullsLast
|
||||||
import ru.dbotthepony.mc.otm.core.registryName
|
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.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 {
|
override fun compare(o1: Item, o2: Item): Int {
|
||||||
rebuild()
|
rebuild()
|
||||||
return item2index.getInt(o1).compareTo(item2index.getInt(o2))
|
return item2index.getInt(o1).compareTo(item2index.getInt(o2))
|
||||||
@ -84,6 +98,24 @@ object ItemLocalizedNameComparator : Comparator<Item> {
|
|||||||
val NullsLast = nullsLast()
|
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> {
|
object ItemModComparator : Comparator<Item> {
|
||||||
override fun compare(o1: Item, o2: Item): Int {
|
override fun compare(o1: Item, o2: Item): Int {
|
||||||
val a = o1.registryName?.namespace ?: return 0
|
val a = o1.registryName?.namespace ?: return 0
|
||||||
@ -106,14 +138,65 @@ object ItemIDComparator : Comparator<Item> {
|
|||||||
val NullsLast = nullsLast()
|
val NullsLast = nullsLast()
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class ItemSorter(val comparator: Comparator<Item?>, private val sTitle: Component) {
|
object ItemStackCountComparator : Comparator<ItemStack> {
|
||||||
DEFAULT(CreativeMenuComparator.NullsFirst, TranslatableComponent("otm.gui.sorting.default")),
|
override fun compare(o1: ItemStack, o2: ItemStack): Int {
|
||||||
NAME(ItemLocalizedNameComparator.NullsFirst.thenComparing(CreativeMenuComparator.NullsFirst), TranslatableComponent("otm.gui.sorting.name")),
|
return o1.count.compareTo(o2.count)
|
||||||
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")),
|
val NullsFirst = nullsFirst()
|
||||||
MATTER_COMPLEXITY(MatterComplexityComparator.NullsFirst.thenComparing(MatterValueComparator.NullsFirst).thenComparing(CreativeMenuComparator.NullsFirst), TranslatableComponent("otm.gui.sorting.matter_complexity")),
|
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
|
open val isEmpty: Boolean get() = false
|
||||||
|
|
||||||
val formatLocaleKey = "otm.suffix.${name.lowercase()}".intern()
|
val formatLocaleKey = "otm.suffix.${name.lowercase()}".intern()
|
||||||
|
val conciseFormatLocaleKey = "otm.suffix_concise.${name.lowercase()}".intern()
|
||||||
val rawLocaleKey = "otm.suffix_raw.${name.lowercase()}".intern()
|
val rawLocaleKey = "otm.suffix_raw.${name.lowercase()}".intern()
|
||||||
|
|
||||||
val string: String
|
val string: String
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package ru.dbotthepony.mc.otm.core.util
|
package ru.dbotthepony.mc.otm.core.util
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
|
||||||
import org.apache.logging.log4j.LogManager
|
import org.apache.logging.log4j.LogManager
|
||||||
import ru.dbotthepony.mc.otm.core.addSorted
|
import ru.dbotthepony.mc.otm.core.addSorted
|
||||||
|
|
||||||
@ -15,6 +16,7 @@ class TickList : ITickable {
|
|||||||
private val toRemoveFromAlways = ArrayList<ITickable>()
|
private val toRemoveFromAlways = ArrayList<ITickable>()
|
||||||
|
|
||||||
private val timers = ArrayDeque<Timer>()
|
private val timers = ArrayDeque<Timer>()
|
||||||
|
private val namedTimers = Object2ObjectOpenHashMap<Any, Timer>(0)
|
||||||
|
|
||||||
var inTicker = false
|
var inTicker = false
|
||||||
private set
|
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 {
|
inner class Ticker(parent: ITickable) : ITickable by parent {
|
||||||
init {
|
init {
|
||||||
add(this, always, alwaysQueued)
|
add(this, always, alwaysQueued)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package ru.dbotthepony.mc.otm.graph.storage
|
package ru.dbotthepony.mc.otm.graph.storage
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity
|
import net.minecraft.world.level.block.entity.BlockEntity
|
||||||
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
||||||
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
|
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
|
||||||
@ -13,7 +14,7 @@ import java.util.*
|
|||||||
import java.util.function.Supplier
|
import java.util.function.Supplier
|
||||||
|
|
||||||
open class StorageNode(private val energyDemander: IMatteryEnergyStorage? = null) : GraphNode<StorageNode, StorageGraph>(::StorageGraph) {
|
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],
|
* 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<*>) {
|
fun addStorageComponent(component: IStorage<*>) {
|
||||||
if (!components.any { component === it || it.storageType === component.storageType }) {
|
if (components.add(component))
|
||||||
components.add(component)
|
|
||||||
|
|
||||||
if (isValid && !manualAttaching)
|
if (isValid && !manualAttaching)
|
||||||
graph.add(component)
|
graph.add(component)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeStorageComponent(component: IStorage<*>) {
|
fun removeStorageComponent(component: IStorage<*>) {
|
||||||
val indexOf = components.indexOfFirst { component === it || it.storageType === component.storageType }
|
if (components.remove(component) && isValid)
|
||||||
if (indexOf == -1) return
|
graph.remove(component)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun invalidate() {
|
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.MatteryPacket
|
||||||
import ru.dbotthepony.mc.otm.network.MenuFieldPacket
|
import ru.dbotthepony.mc.otm.network.MenuFieldPacket
|
||||||
import ru.dbotthepony.mc.otm.network.MenuNetworkChannel
|
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.enqueueWork
|
||||||
import ru.dbotthepony.mc.otm.network.packetHandled
|
import ru.dbotthepony.mc.otm.network.packetHandled
|
||||||
import ru.dbotthepony.mc.otm.network.sender
|
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
|
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 externalSlots = ConditionalSet<Slot>()
|
||||||
private val quickMoveMapping = Reference2ObjectOpenHashMap<Slot, ReferenceArrayList<Collection<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
|
// first pass - stack with existing slots
|
||||||
if (copy.isStackable) {
|
if (copy.isStackable) {
|
||||||
for (slot in slots) {
|
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
|
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
|
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.GetterSetter
|
||||||
import ru.dbotthepony.mc.otm.core.immutableList
|
import ru.dbotthepony.mc.otm.core.immutableList
|
||||||
import ru.dbotthepony.mc.otm.runOnClient
|
import ru.dbotthepony.mc.otm.runOnClient
|
||||||
|
import java.util.function.Predicate
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make slots for single container
|
* 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
|
var hasSetFilter = false
|
||||||
private set
|
private set
|
||||||
|
|
||||||
@ -60,6 +61,10 @@ open class UserFilteredSlot(container: Container, index: Int, x: Int = 0, y: Int
|
|||||||
return filter?.get() == null
|
return filter?.get() == null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun test(t: ItemStack): Boolean {
|
||||||
|
return filter?.get() == null || filter?.get() == t.item
|
||||||
|
}
|
||||||
|
|
||||||
fun isSameFilter(other: Slot): Boolean {
|
fun isSameFilter(other: Slot): Boolean {
|
||||||
if (other !is UserFilteredSlot)
|
if (other !is UserFilteredSlot)
|
||||||
return filter?.get() == null
|
return filter?.get() == null
|
||||||
|
@ -2,6 +2,7 @@ package ru.dbotthepony.mc.otm.menu.data
|
|||||||
|
|
||||||
import com.mojang.blaze3d.platform.InputConstants
|
import com.mojang.blaze3d.platform.InputConstants
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
|
||||||
import net.minecraft.client.Minecraft
|
import net.minecraft.client.Minecraft
|
||||||
import net.minecraft.client.gui.screens.Screen
|
import net.minecraft.client.gui.screens.Screen
|
||||||
import net.minecraft.network.FriendlyByteBuf
|
import net.minecraft.network.FriendlyByteBuf
|
||||||
@ -13,21 +14,28 @@ import net.minecraft.world.item.ItemStack
|
|||||||
import net.minecraftforge.network.NetworkEvent
|
import net.minecraftforge.network.NetworkEvent
|
||||||
import net.minecraftforge.network.PacketDistributor
|
import net.minecraftforge.network.PacketDistributor
|
||||||
import ru.dbotthepony.mc.otm.client.minecraft
|
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.readBigInteger
|
||||||
import ru.dbotthepony.mc.otm.core.writeBigInteger
|
import ru.dbotthepony.mc.otm.core.writeBigInteger
|
||||||
import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
||||||
import ru.dbotthepony.mc.otm.network.*
|
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 ru.dbotthepony.mc.otm.storage.*
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.function.Supplier
|
import java.util.function.Supplier
|
||||||
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
interface INetworkedItemViewProvider {
|
interface INetworkedItemViewProvider {
|
||||||
val networkedItemView: NetworkedItemView
|
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) {
|
override fun write(buff: FriendlyByteBuf) {
|
||||||
buff.writeInt(stackID)
|
buff.writeInt(stackID)
|
||||||
buff.writeEnum(type)
|
buff.writeEnum(type)
|
||||||
@ -53,144 +61,88 @@ class ItemViewInteractPacket(val stackID: Int, val type: ClickType, val action:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object ClearItemViewPacket : MatteryPacket {
|
abstract class NetworkedItemViewPacket : MatteryPacket {
|
||||||
override fun write(buff: FriendlyByteBuf) {
|
final override fun play(context: Supplier<NetworkEvent.Context>) {
|
||||||
// NO-OP
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun play(context: Supplier<NetworkEvent.Context>) {
|
|
||||||
context.packetHandled = true
|
context.packetHandled = true
|
||||||
context.enqueueWork {
|
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 {
|
protected abstract fun action(view: NetworkedItemView)
|
||||||
return ClearItemViewPacket
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fun send(ply: ServerPlayer) {
|
object ClearItemViewPacket : NetworkedItemViewPacket() {
|
||||||
MenuNetworkChannel.send(ply, this)
|
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) {
|
override fun write(buff: FriendlyByteBuf) {
|
||||||
buff.writeInt(containerId)
|
buff.writeInt(stackId)
|
||||||
buff.writeInt(id)
|
|
||||||
stack.write(buff)
|
stack.write(buff)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun play(context: Supplier<NetworkEvent.Context>) {
|
override fun action(view: NetworkedItemView) {
|
||||||
context.get().packetHandled = true
|
if (view.id2tuple.containsKey(stackId)) {
|
||||||
context.get().enqueueWork {
|
throw IllegalStateException("NetworkedItemView $view already contains stack with id $stackId")
|
||||||
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")
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
companion object {
|
||||||
fun read(buffer: FriendlyByteBuf): StackAddPacket {
|
fun read(buffer: FriendlyByteBuf): StackAddPacket {
|
||||||
val containerId = buffer.readInt()
|
|
||||||
val id = buffer.readInt()
|
val id = buffer.readInt()
|
||||||
val item = StorageStack.ITEMS.read(buffer)
|
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) {
|
override fun write(buff: FriendlyByteBuf) {
|
||||||
buff.writeInt(id)
|
buff.writeInt(stackId)
|
||||||
buff.writeInt(stackID)
|
|
||||||
buff.writeBigInteger(newCount)
|
buff.writeBigInteger(newCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun play(context: Supplier<NetworkEvent.Context>) {
|
override fun action(view: NetworkedItemView) {
|
||||||
context.get().packetHandled = true
|
val tuple = view.id2tuple[stackId] ?: throw IllegalStateException("No such stack with id $stackId in $view")
|
||||||
context.get().enqueueWork {
|
tuple.stack = tuple.stack.copy(newCount)
|
||||||
val get = Minecraft.getInstance().player?.containerMenu ?: return@enqueueWork
|
view.resort()
|
||||||
|
|
||||||
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)
|
|
||||||
view.resort()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun read(buffer: FriendlyByteBuf): StackChangePacket {
|
fun read(buffer: FriendlyByteBuf): StackChangePacket {
|
||||||
val id = buffer.readInt()
|
|
||||||
val stackID = buffer.readInt()
|
val stackID = buffer.readInt()
|
||||||
val newCount = buffer.readBigInteger()
|
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) {
|
override fun write(buff: FriendlyByteBuf) {
|
||||||
buff.writeInt(id)
|
buff.writeInt(stackId)
|
||||||
buff.writeInt(stackID)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun play(context: Supplier<NetworkEvent.Context>) {
|
override fun action(view: NetworkedItemView) {
|
||||||
context.get().packetHandled = true
|
val obj = view.id2tuple.remove(stackId) ?: throw IllegalStateException("No such stack with id $stackId in $view")
|
||||||
context.get().enqueueWork {
|
view.sortedView.remove(obj)
|
||||||
val get = Minecraft.getInstance().player?.containerMenu ?: return@enqueueWork
|
view.resort()
|
||||||
|
|
||||||
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")
|
|
||||||
view.sortedView.remove(obj)
|
|
||||||
view.resort()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun read(buffer: FriendlyByteBuf): StackRemovePacket {
|
fun read(buffer: FriendlyByteBuf): StackRemovePacket {
|
||||||
val id = buffer.readInt()
|
|
||||||
val stackID = 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.
|
* Creates a virtual, slotless container for Player to interaction with.
|
||||||
*/
|
*/
|
||||||
open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote: Boolean) : IStorageEventConsumer<ItemStorageStack> {
|
class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val isRemote: Boolean) : IStorageEventConsumer<ItemStorageStack> {
|
||||||
data class NetworkedItem(val id: Int, var stack: ItemStorageStack, val upstreamId: UUID? = null)
|
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>
|
override val storageType: StorageStack.Type<ItemStorageStack>
|
||||||
get() = StorageStack.ITEMS
|
get() = StorageStack.ITEMS
|
||||||
|
|
||||||
// this (how client see and interact with)
|
val id2tuple = Int2ObjectOpenHashMap<Tuple>()
|
||||||
val localState = Int2ObjectOpenHashMap<NetworkedItem>()
|
val sortedView = ArrayList<Tuple>()
|
||||||
|
var sorter: Comparator<in ItemStorageStack> = ItemStorageStackSorter.DEFAULT
|
||||||
val sortedView = LinkedList<NetworkedItem>()
|
set(value) {
|
||||||
var sorter: Comparator<ItemStack> = NAME_SORTER
|
if (field != value) {
|
||||||
|
field = value
|
||||||
companion object {
|
resort()
|
||||||
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 itemCount get() = id2tuple.size
|
||||||
val cmp = o1.count.compareTo(o2.count)
|
|
||||||
|
|
||||||
if (cmp != 0)
|
private var nextItemID = 0
|
||||||
return@Comparator cmp
|
private val uuid2tuple = Object2ObjectOpenHashMap<UUID, Tuple>()
|
||||||
|
private val networkBacklog = ArrayList<Any>()
|
||||||
|
|
||||||
return@Comparator o1.item.registryName.toString().compareTo(o2.item.registryName.toString())
|
operator fun get(id: Int): Tuple? = id2tuple[id]
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun resort() {
|
fun resort() {
|
||||||
sortedView.sortWith { a, b ->
|
if (isRemote) {
|
||||||
return@sortWith sorter.compare(a.stack.toItemStack(), b.stack.toItemStack())
|
sortedView.sortWith(sorter.map(Tuple::stack))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// parent (e.g. VirtualComponent)
|
var component: IStorageComponent<ItemStorageStack>? = null
|
||||||
protected val upstreamState = HashMap<UUID, NetworkedItem>()
|
set(provider) {
|
||||||
protected val networkBacklog = ArrayList<Any>()
|
if (provider === field) return
|
||||||
|
|
||||||
operator fun get(id: Int): NetworkedItem? = localState[id]
|
field?.removeListenerAndNotify(this)
|
||||||
|
field = provider
|
||||||
var provider: IStorageComponent<ItemStorageStack>? = null
|
provider?.addListenerAndNotify(this)
|
||||||
private set
|
}
|
||||||
|
|
||||||
fun mouseClick(index: Int, mouseButton: Int) {
|
fun mouseClick(index: Int, mouseButton: Int) {
|
||||||
if (minecraft.player?.isSpectator == true) {
|
if (minecraft.player?.isSpectator == true) return
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val list = sortedView
|
MenuNetworkChannel.sendToServer(ItemViewInteractPacket(
|
||||||
|
sortedView.getOrNull(index)?.networkId ?: -1,
|
||||||
val action =
|
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
|
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
|
|
||||||
provider?.addListenerAndNotify(this)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removed() {
|
fun removed() {
|
||||||
provider?.removeListenerAndNotify(this)
|
component?.removeListenerAndNotify(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
val itemCount get() = localState.values.size
|
|
||||||
|
|
||||||
override fun onStackAdded(stack: ItemStorageStack, id: UUID, provider: IStorageProvider<ItemStorageStack>) {
|
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
|
this.id2tuple[state.networkId] = state
|
||||||
upstreamState[id] = state
|
uuid2tuple[id] = state
|
||||||
network { StackAddPacket(menu.containerId, state.id, state.stack) }
|
network { StackAddPacket(state.networkId, state.stack) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStackChanged(stack: ItemStorageStack, id: UUID) {
|
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
|
get.stack = stack
|
||||||
network { StackChangePacket(menu.containerId, get.id, stack.count) }
|
network { StackChangePacket(get.networkId, stack.count) }
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun network(fn: () -> Any) {
|
override fun onStackRemoved(id: UUID) {
|
||||||
if (!remote) {
|
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())
|
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() {
|
fun clear() {
|
||||||
sortedView.clear()
|
sortedView.clear()
|
||||||
upstreamState.clear()
|
uuid2tuple.clear()
|
||||||
localState.clear()
|
id2tuple.clear()
|
||||||
|
|
||||||
if (!remote) {
|
if (!isRemote) {
|
||||||
networkBacklog.clear()
|
networkBacklog.clear()
|
||||||
networkBacklog.add(ClearItemViewPacket)
|
networkBacklog.add(ClearItemViewPacket)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun network() {
|
fun network() {
|
||||||
check(!remote) { "Not a server" }
|
check(!isRemote) { "Not a server" }
|
||||||
val consumer = PacketDistributor.PLAYER.with { ply as ServerPlayer }
|
val consumer = PacketDistributor.PLAYER.with { ply as ServerPlayer }
|
||||||
|
|
||||||
for (packet in networkBacklog) {
|
for (packet in networkBacklog) {
|
||||||
@ -331,82 +259,41 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun playerInteract(packet: ItemViewInteractPacket) {
|
fun playerInteract(packet: ItemViewInteractPacket) {
|
||||||
val provider = provider ?: return
|
val component = component ?: return
|
||||||
|
val (stackId, type, action) = packet
|
||||||
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())
|
|
||||||
|
|
||||||
|
if (type == ClickType.CLONE) {
|
||||||
|
if (!ply.abilities.instabuild) return
|
||||||
|
menu.syncCarried((get(stackId) ?: return).stack.toItemStack().also { it.count = it.maxStackSize })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (click == ClickType.QUICK_MOVE && stackId > -1) {
|
// забираем из системы с зажатым shift
|
||||||
val state = get(stackId) ?: return
|
if (type == ClickType.QUICK_MOVE) {
|
||||||
val stack = state.stack.toItemStack()
|
val tuple = get(stackId) ?: return
|
||||||
|
val stack = tuple.stack.toItemStack()
|
||||||
|
|
||||||
val amount =
|
var amount = if (action == ClickAction.PRIMARY) all(stack) else half(stack)
|
||||||
if (action == ClickAction.PRIMARY)
|
amount -= menu.quickMoveToInventory(tuple.stack.toItemStack(amount), true).count
|
||||||
stack.maxStackSize
|
if (amount == 0) return
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
menu.quickMoveToInventory(component.extractStack(tuple.upstreamId!!, amount.toBigInteger(), false).toItemStack(), false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!menu.carried.isEmpty && click != ClickType.QUICK_MOVE) {
|
if (menu.carried.isNotEmpty) {
|
||||||
// try to put
|
|
||||||
if (action == ClickAction.PRIMARY) {
|
if (action == ClickAction.PRIMARY) {
|
||||||
val carried = menu.carried
|
menu.syncCarried(component.insertStack(ItemStorageStack(menu.carried), false).toItemStack())
|
||||||
val amount = carried.count
|
} else if (component.insertStack(ItemStorageStack(menu.carried.copyWithCount(1)), false).isEmpty) {
|
||||||
|
menu.carried.shrink(1)
|
||||||
if (amount == carried.count) {
|
menu.syncCarried()
|
||||||
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.carried.shrink(1)
|
|
||||||
MenuNetworkChannel.send(ply as ServerPlayer, SetCarriedPacket(menu.carried))
|
|
||||||
menu.setRemoteCarried(menu.carried.copy())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (stackId > -1) {
|
} else if (stackId > -1) {
|
||||||
val state = get(stackId) ?: return
|
val state = get(stackId) ?: return
|
||||||
val stack = state.stack.toItemStack()
|
val stack = state.stack.toItemStack()
|
||||||
|
val amount = if (action == ClickAction.PRIMARY) all(stack) else half(stack)
|
||||||
val amount =
|
menu.carried = component.extractStack(state.upstreamId!!, amount.toBigInteger(), false).toItemStack()
|
||||||
if (action == ClickAction.PRIMARY)
|
menu.syncCarried()
|
||||||
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())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,10 @@ package ru.dbotthepony.mc.otm.menu.input
|
|||||||
import net.minecraft.world.entity.player.Player
|
import net.minecraft.world.entity.player.Player
|
||||||
import ru.dbotthepony.mc.otm.client.minecraft
|
import ru.dbotthepony.mc.otm.client.minecraft
|
||||||
import ru.dbotthepony.mc.otm.core.GetterSetter
|
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.menu.MatteryMenu
|
||||||
|
import ru.dbotthepony.mc.otm.network.synchronizer.IField
|
||||||
import java.util.function.Consumer
|
import java.util.function.Consumer
|
||||||
import java.util.function.Predicate
|
import java.util.function.Predicate
|
||||||
import java.util.function.Supplier
|
import java.util.function.Supplier
|
||||||
@ -14,33 +17,7 @@ import kotlin.reflect.KMutableProperty0
|
|||||||
*
|
*
|
||||||
* Getting and setting values should ONLY be done clientside
|
* Getting and setting values should ONLY be done clientside
|
||||||
*/
|
*/
|
||||||
interface IPlayerInputWithFeedback<V> : GetterSetter<V>, Predicate<Player?> {
|
interface IPlayerInputWithFeedback<V> : SentientGetterSetter<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)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents Server to Client synchronization and Client to Server input
|
* 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 class AbstractPlayerInputWithFeedback<V> : IPlayerInputWithFeedback<V> {
|
||||||
abstract val input: MatteryMenu.PlayerInput<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
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(t: V) {
|
final override fun accept(t: V) {
|
||||||
input.input(t)
|
input.input(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,16 +5,28 @@ import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
|||||||
import java.util.function.BooleanSupplier
|
import java.util.function.BooleanSupplier
|
||||||
import kotlin.reflect.KMutableProperty0
|
import kotlin.reflect.KMutableProperty0
|
||||||
|
|
||||||
class BooleanInputWithFeedback(menu: MatteryMenu) : AbstractPlayerInputWithFeedback<Boolean>() {
|
class BooleanInputWithFeedback(menu: MatteryMenu, allowSpectators: Boolean = false) : AbstractPlayerInputWithFeedback<Boolean>() {
|
||||||
override val input = menu.booleanInput { consumer?.invoke(it) }
|
override val input = menu.booleanInput(allowSpectators) { consumer?.invoke(it) }
|
||||||
override val value by menu.mSynchronizer.computedBool(BooleanSupplier { supplier?.invoke() ?: false }).property
|
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) {
|
||||||
with(state)
|
if (state != null)
|
||||||
|
with(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(menu: MatteryMenu, state: GetterSetter<Boolean>) : this(menu) {
|
constructor(menu: MatteryMenu, allowSpectators: Boolean, state: GetterSetter<Boolean>?) : this(menu, allowSpectators) {
|
||||||
with(state)
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun switchValue() {
|
fun switchValue() {
|
||||||
|
@ -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: 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)
|
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)
|
val codec = EnumValueCodec(clazz)
|
||||||
private val default = codec.values.first()
|
private val default = codec.values.first()
|
||||||
|
|
||||||
override val input = menu.PlayerInput(codec, false) { consumer?.invoke(it) }
|
override val input = menu.PlayerInput(codec, allowSpectators) { consumer?.invoke(it) }
|
||||||
override val value by menu.mSynchronizer.ComputedField(getter = { supplier?.invoke() ?: default }, codec)
|
override val field = menu.mSynchronizer.ComputedField(getter = { supplier?.invoke() ?: default }, codec)
|
||||||
|
|
||||||
constructor(menu: MatteryMenu, clazz: Class<E>, state: KMutableProperty0<E>?) : this(menu, clazz) {
|
constructor(menu: MatteryMenu, clazz: Class<E>, state: KMutableProperty0<E>?) : this(menu, clazz) {
|
||||||
if (state != null) {
|
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) {
|
||||||
with(state)
|
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>() {
|
class StringInputWithFeedback(menu: MatteryMenu) : AbstractPlayerInputWithFeedback<String>() {
|
||||||
override val input = menu.stringInput { consumer?.invoke(it.replace('\u0000', ' ')) }
|
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) {
|
constructor(menu: MatteryMenu, state: KMutableProperty0<String>) : this(menu) {
|
||||||
with(state)
|
with(state)
|
||||||
|
@ -190,8 +190,8 @@ class MatterPanelMenu(
|
|||||||
val sortingGS = GetterSetter.of(::sorting, changeSorting::input)
|
val sortingGS = GetterSetter.of(::sorting, changeSorting::input)
|
||||||
val isAscendingGS = GetterSetter.of(::isAscending, changeIsAscending::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 actualComparator = Comparator<PatternState> { o1, o2 -> sorting.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 actualTaskComparator = Comparator<ReplicationTask> { o1, o2 -> sorting.compare(o1.item, o2.item) * (if (isAscending) 1 else -1) }
|
||||||
|
|
||||||
private val patterns = ArrayList<PatternState>()
|
private val patterns = ArrayList<PatternState>()
|
||||||
private val tasks = ArrayList<ReplicationTask>()
|
private val tasks = ArrayList<ReplicationTask>()
|
||||||
|
@ -1,57 +1,67 @@
|
|||||||
package ru.dbotthepony.mc.otm.menu.storage
|
package ru.dbotthepony.mc.otm.menu.storage
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList
|
|
||||||
import net.minecraft.world.SimpleContainer
|
import net.minecraft.world.SimpleContainer
|
||||||
import net.minecraft.world.entity.player.Inventory
|
import net.minecraft.world.entity.player.Inventory
|
||||||
import net.minecraft.world.entity.player.Player
|
import net.minecraft.world.entity.player.Player
|
||||||
import net.minecraft.world.inventory.Slot
|
|
||||||
import net.minecraft.world.item.ItemStack
|
import net.minecraft.world.item.ItemStack
|
||||||
import net.minecraftforge.common.capabilities.ForgeCapabilities
|
import net.minecraftforge.common.capabilities.ForgeCapabilities
|
||||||
import ru.dbotthepony.mc.otm.block.entity.storage.DriveViewerBlockEntity
|
import ru.dbotthepony.mc.otm.block.entity.storage.DriveViewerBlockEntity
|
||||||
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
||||||
import ru.dbotthepony.mc.otm.capability.drive.IMatteryDrive
|
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.container.ItemFilter
|
||||||
import ru.dbotthepony.mc.otm.core.ifPresentK
|
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.item.PortableCondensationDriveItem
|
||||||
import ru.dbotthepony.mc.otm.menu.MatteryPoweredMenu
|
import ru.dbotthepony.mc.otm.menu.MatteryPoweredMenu
|
||||||
import ru.dbotthepony.mc.otm.menu.MatterySlot
|
import ru.dbotthepony.mc.otm.menu.MatterySlot
|
||||||
import ru.dbotthepony.mc.otm.menu.data.INetworkedItemViewProvider
|
import ru.dbotthepony.mc.otm.menu.data.INetworkedItemViewProvider
|
||||||
import ru.dbotthepony.mc.otm.menu.data.NetworkedItemView
|
import ru.dbotthepony.mc.otm.menu.data.NetworkedItemView
|
||||||
import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback
|
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.registry.MMenus
|
||||||
import ru.dbotthepony.mc.otm.storage.ItemStorageStack
|
import ru.dbotthepony.mc.otm.storage.ItemStorageStack
|
||||||
import ru.dbotthepony.mc.otm.storage.StorageStack
|
import ru.dbotthepony.mc.otm.storage.StorageStack
|
||||||
import ru.dbotthepony.mc.otm.storage.powered.PoweredVirtualComponent
|
import ru.dbotthepony.mc.otm.storage.powered.PoweredVirtualComponent
|
||||||
|
|
||||||
class DriveViewerMenu @JvmOverloads constructor(
|
class DriveViewerMenu(
|
||||||
containerID: Int,
|
containerID: Int,
|
||||||
inventory: Inventory,
|
inventory: Inventory,
|
||||||
tile: DriveViewerBlockEntity? = null
|
tile: DriveViewerBlockEntity? = null
|
||||||
) : MatteryPoweredMenu(
|
) : MatteryPoweredMenu(MMenus.DRIVE_VIEWER, containerID, inventory, tile), INetworkedItemViewProvider {
|
||||||
MMenus.DRIVE_VIEWER, containerID, inventory, tile
|
|
||||||
), INetworkedItemViewProvider {
|
|
||||||
override val networkedItemView = NetworkedItemView(inventory.player, this, tile == null)
|
override val networkedItemView = NetworkedItemView(inventory.player, this, tile == null)
|
||||||
val driveSlot: MatterySlot
|
|
||||||
|
val driveSlot = object : MatterySlot(tile?.container ?: SimpleContainer(1), 0) {
|
||||||
|
override fun mayPlace(itemStack: ItemStack): Boolean {
|
||||||
|
return itemStack.getCapability(MatteryCapability.DRIVE).isPresent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private val powered: PoweredVirtualComponent<ItemStorageStack>?
|
private val powered: PoweredVirtualComponent<ItemStorageStack>?
|
||||||
private var lastDrive: IMatteryDrive<ItemStorageStack>? = null
|
private var lastDrive: IMatteryDrive<ItemStorageStack>? = null
|
||||||
|
val profiledEnergy = ProfiledLevelGaugeWidget<ProfiledEnergyStorage<*>>(this, energyWidget)
|
||||||
|
val energyConfig = EnergyConfigPlayerInput(this, tile?.energyConfig)
|
||||||
|
|
||||||
init {
|
val settings = object : DriveViewerBlockEntity.ISettings {
|
||||||
val container = tile?.container ?: SimpleContainer(1)
|
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() } }
|
||||||
|
|
||||||
driveSlot = object : MatterySlot(container, 0) {
|
private fun changes() {
|
||||||
override fun mayPlace(itemStack: ItemStack): Boolean {
|
if (isAscending) {
|
||||||
return itemStack.getCapability(MatteryCapability.DRIVE).isPresent
|
networkedItemView.sorter = sorting
|
||||||
|
} else {
|
||||||
|
networkedItemView.sorter = sorting.reversed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
if (tile != null) {
|
if (tile != null) {
|
||||||
powered = PoweredVirtualComponent(
|
profiledEnergy.with(tile.energy)
|
||||||
StorageStack.ITEMS,
|
powered = PoweredVirtualComponent(StorageStack.ITEMS, tile::energy)
|
||||||
tile.getCapability(MatteryCapability.ENERGY).resolve()::get
|
this.networkedItemView.component = powered
|
||||||
)
|
|
||||||
|
|
||||||
this.networkedItemView.setComponent(powered)
|
|
||||||
} else {
|
} else {
|
||||||
powered = null
|
powered = null
|
||||||
}
|
}
|
||||||
@ -126,12 +136,9 @@ class DriveViewerMenu @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun removed(p_38940_: Player) {
|
override fun removed(player: Player) {
|
||||||
super.removed(p_38940_)
|
super.removed(player)
|
||||||
|
lastDrive?.let { powered?.remove(it) }
|
||||||
if (lastDrive != null)
|
|
||||||
powered?.remove(lastDrive!!)
|
|
||||||
|
|
||||||
this.networkedItemView.removed()
|
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.Inventory
|
||||||
import net.minecraft.world.entity.player.Player
|
import net.minecraft.world.entity.player.Player
|
||||||
import net.minecraft.world.item.ItemStack
|
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.ItemMonitorBlockEntity
|
||||||
import ru.dbotthepony.mc.otm.block.entity.storage.ItemMonitorPlayerSettings
|
import ru.dbotthepony.mc.otm.block.entity.storage.ItemMonitorPlayerSettings
|
||||||
import ru.dbotthepony.mc.otm.container.get
|
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.MatteryPoweredMenu
|
||||||
import ru.dbotthepony.mc.otm.menu.MatterySlot
|
import ru.dbotthepony.mc.otm.menu.MatterySlot
|
||||||
import ru.dbotthepony.mc.otm.menu.data.INetworkedItemViewProvider
|
import ru.dbotthepony.mc.otm.menu.data.INetworkedItemViewProvider
|
||||||
import ru.dbotthepony.mc.otm.menu.data.NetworkedItemView
|
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.registry.MMenus
|
||||||
import ru.dbotthepony.mc.otm.storage.*
|
import ru.dbotthepony.mc.otm.storage.*
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -73,73 +78,85 @@ private class ResultSlot(container: Container) : MatterySlot(container, 0) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ItemMonitorMenu @JvmOverloads constructor(
|
class ItemMonitorMenu(
|
||||||
containerId: Int,
|
containerId: Int,
|
||||||
inventory: Inventory,
|
inventory: Inventory,
|
||||||
tile: ItemMonitorBlockEntity? = null
|
tile: ItemMonitorBlockEntity? = null
|
||||||
) : MatteryPoweredMenu(MMenus.ITEM_MONITOR, containerId, inventory, tile), INetworkedItemViewProvider {
|
) : MatteryPoweredMenu(MMenus.ITEM_MONITOR, containerId, inventory, tile), INetworkedItemViewProvider {
|
||||||
override val networkedItemView = NetworkedItemView(inventory.player, this, tile == null)
|
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 craftingResult: MatterySlot
|
||||||
val craftingSlots: List<MatterySlot>
|
val craftingSlots: List<MatterySlot>
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (tile != null) {
|
if (tile != null) {
|
||||||
networkedItemView.setComponent(tile.poweredView)
|
networkedItemView.component = 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())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
addSlot(craftingResult)
|
||||||
|
|
||||||
addInventorySlots()
|
addInventorySlots()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun broadcastFullState() {
|
override fun removed(player: Player) {
|
||||||
super.broadcastFullState()
|
super.removed(player)
|
||||||
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_)
|
|
||||||
networkedItemView.removed()
|
networkedItemView.removed()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun broadcastChanges() {
|
override fun broadcastChanges() {
|
||||||
super.broadcastChanges()
|
super.broadcastChanges()
|
||||||
networkedItemView.network()
|
networkedItemView.network()
|
||||||
|
}
|
||||||
|
|
||||||
if (!settingsNetworked) {
|
private fun moveCrafting(view: IStorageComponent<ItemStorageStack>, simulate: Boolean): Boolean {
|
||||||
MenuNetworkChannel.send(PacketDistributor.PLAYER.with { ply as ServerPlayer }, settings)
|
val itemStack = craftingResult.item
|
||||||
settingsNetworked = true
|
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 {
|
override fun quickMoveStack(ply: Player, slotIndex: Int): ItemStack {
|
||||||
if (playerInventorySlots.any { it.index == slotIndex }) {
|
if (playerInventorySlots.any { it.index == slotIndex }) {
|
||||||
if (tile == null) {
|
if (tile == null) return ItemStack.EMPTY
|
||||||
return ItemStack.EMPTY
|
|
||||||
}
|
|
||||||
|
|
||||||
val slot = slots[slotIndex]
|
val slot = slots[slotIndex]
|
||||||
|
if (slot.item.isEmpty) return ItemStack.EMPTY
|
||||||
|
|
||||||
if (slot.item.isEmpty) {
|
val leftover = networkedItemView.component?.insertStack(ItemStorageStack(slot.item), false)?.toItemStack() ?: slot.item
|
||||||
return ItemStack.EMPTY
|
|
||||||
}
|
|
||||||
|
|
||||||
val leftover = networkedItemView.provider?.insertStack(ItemStorageStack(slot.item), false)?.toItemStack() ?: slot.item
|
|
||||||
|
|
||||||
if (leftover.count == slot.item.count) {
|
if (leftover.count == slot.item.count) {
|
||||||
return ItemStack.EMPTY
|
return ItemStack.EMPTY
|
||||||
@ -148,77 +165,48 @@ class ItemMonitorMenu @JvmOverloads constructor(
|
|||||||
val old = slot.item.copy()
|
val old = slot.item.copy()
|
||||||
slot.item.count = leftover.count
|
slot.item.count = leftover.count
|
||||||
return old
|
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
|
// from crafting grid to inventory
|
||||||
val item = slots[slotIndex].item
|
val item = slots[slotIndex].item
|
||||||
|
if (item.isEmpty) return ItemStack.EMPTY
|
||||||
|
|
||||||
if (item.isEmpty) {
|
var remainder = item
|
||||||
return ItemStack.EMPTY
|
|
||||||
|
when (settings.ingredientPriority) {
|
||||||
|
ItemMonitorPlayerSettings.IngredientPriority.SYSTEM, ItemMonitorPlayerSettings.IngredientPriority.SYSTEM_FIRST -> {
|
||||||
|
remainder = networkedItemView.component?.insertStack(ItemStorageStack(remainder), false)?.toItemStack() ?: remainder
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {}
|
||||||
}
|
}
|
||||||
|
|
||||||
var remainder = moveItemStackToSlots(item, playerInventorySlots)
|
remainder = moveItemStackToSlots(remainder, playerInventorySlots)
|
||||||
|
|
||||||
slots[slotIndex].set(remainder)
|
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
|
return if (remainder.count != item.count) item else ItemStack.EMPTY
|
||||||
} else if (slotIndex == craftingResult.index) {
|
} else if (slotIndex == craftingResult.index) {
|
||||||
// quickcraft... god damn it
|
var item = craftingResult.item
|
||||||
if (!craftingResult.hasItem()) {
|
if (item.isEmpty) return ItemStack.EMPTY
|
||||||
return ItemStack.EMPTY
|
|
||||||
}
|
|
||||||
|
|
||||||
val item = craftingResult.item
|
|
||||||
|
|
||||||
val tile = tile as ItemMonitorBlockEntity? ?: return ItemStack.EMPTY
|
val tile = tile as ItemMonitorBlockEntity? ?: return ItemStack.EMPTY
|
||||||
|
|
||||||
if (tile.lastCraftingRecipe(ply) != null && tile.craftingRecipe != tile.lastCraftingRecipe(ply)) {
|
if (tile.lastCraftingRecipe(ply) != null && tile.craftingRecipe != tile.lastCraftingRecipe(ply)) {
|
||||||
// recipe has changed
|
// рецепт изменился
|
||||||
return ItemStack.EMPTY
|
return ItemStack.EMPTY
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val crafted = tile.howMuchPlayerCrafted(ply)
|
||||||
|
|
||||||
if (settings.craftingAmount == ItemMonitorPlayerSettings.Amount.ONE) {
|
if (settings.craftingAmount == ItemMonitorPlayerSettings.Amount.ONE) {
|
||||||
if (tile.howMuchPlayerCrafted(ply) > 0) {
|
if (crafted > 0) {
|
||||||
return ItemStack.EMPTY
|
return ItemStack.EMPTY
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var hasUnstackables = false
|
if (settings.craftingAmount == ItemMonitorPlayerSettings.Amount.STACK || tile.craftingGrid.any { !it.isStackable }) {
|
||||||
var maxStack = 64
|
if (crafted > 0 && (crafted + 1) * item.count > item.maxStackSize) {
|
||||||
|
|
||||||
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) {
|
|
||||||
return ItemStack.EMPTY
|
return ItemStack.EMPTY
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val count = tile.howMuchPlayerCrafted(ply)
|
if (crafted > 0 && crafted >= tile.craftingGrid.iterator().mapToInt { it.maxStackSize }.reduce(Int.MAX_VALUE, Int::coerceAtMost)) {
|
||||||
|
|
||||||
if (count > 0 && count >= maxStack) {
|
|
||||||
return ItemStack.EMPTY
|
return ItemStack.EMPTY
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -227,50 +215,12 @@ class ItemMonitorMenu @JvmOverloads constructor(
|
|||||||
tile.craftingResultContainer.craftingPlayer = ply as ServerPlayer
|
tile.craftingResultContainer.craftingPlayer = ply as ServerPlayer
|
||||||
|
|
||||||
try {
|
try {
|
||||||
when (settings.resultTarget) {
|
if (moveCrafting(tile.poweredView ?: return ItemStack.EMPTY, true)) {
|
||||||
ItemMonitorPlayerSettings.ResultTarget.ALL_SYSTEM -> {
|
item = item.copy()
|
||||||
val wrapper = ItemStorageStack(item)
|
moveCrafting(tile.poweredView ?: return ItemStack.EMPTY, false)
|
||||||
var remaining = tile.poweredView?.insertStack(wrapper, true)?.toItemStack() ?: return ItemStack.EMPTY
|
return item
|
||||||
|
} else {
|
||||||
if (remaining.isEmpty) {
|
return ItemStack.EMPTY
|
||||||
tile.poweredView!!.insertStack(wrapper, false)
|
|
||||||
craftingResult.remove(item.count)
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
} finally {
|
||||||
tile.craftingResultContainer.craftingPlayer = null
|
tile.craftingResultContainer.craftingPlayer = null
|
||||||
|
@ -81,8 +81,8 @@ object MenuNetworkChannel : MatteryNetworkChannel(
|
|||||||
add(SetCarriedPacket::class.java, SetCarriedPacket.Companion::read, NetworkDirection.PLAY_TO_CLIENT)
|
add(SetCarriedPacket::class.java, SetCarriedPacket.Companion::read, NetworkDirection.PLAY_TO_CLIENT)
|
||||||
add(ItemFilterSlotPacket::class.java, ItemFilterSlotPacket.Companion::read)
|
add(ItemFilterSlotPacket::class.java, ItemFilterSlotPacket.Companion::read)
|
||||||
|
|
||||||
// networked view
|
// networked item view
|
||||||
add(ClearItemViewPacket::class.java, ClearItemViewPacket::read, NetworkDirection.PLAY_TO_CLIENT)
|
add(ClearItemViewPacket::class.java, { ClearItemViewPacket }, NetworkDirection.PLAY_TO_CLIENT)
|
||||||
add(ItemViewInteractPacket::class.java, ItemViewInteractPacket.Companion::read, NetworkDirection.PLAY_TO_SERVER)
|
add(ItemViewInteractPacket::class.java, ItemViewInteractPacket.Companion::read, NetworkDirection.PLAY_TO_SERVER)
|
||||||
add(StackAddPacket::class.java, StackAddPacket.Companion::read, NetworkDirection.PLAY_TO_CLIENT)
|
add(StackAddPacket::class.java, StackAddPacket.Companion::read, NetworkDirection.PLAY_TO_CLIENT)
|
||||||
add(StackChangePacket::class.java, StackChangePacket.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
|
// Client->Server
|
||||||
add(MatteryMenu.PlayerInputPacket::class.java, MatteryMenu::PlayerInputPacket, NetworkDirection.PLAY_TO_SERVER)
|
add(MatteryMenu.PlayerInputPacket::class.java, MatteryMenu::PlayerInputPacket, NetworkDirection.PLAY_TO_SERVER)
|
||||||
|
|
||||||
// Item monitor
|
|
||||||
add(ItemMonitorPlayerSettings::class.java, ItemMonitorPlayerSettings.Companion::read)
|
|
||||||
|
|
||||||
// matter panel menu
|
// matter panel menu
|
||||||
add(CancelTaskPacket::class.java, CancelTaskPacket.Companion::read, NetworkDirection.PLAY_TO_SERVER)
|
add(CancelTaskPacket::class.java, CancelTaskPacket.Companion::read, NetworkDirection.PLAY_TO_SERVER)
|
||||||
add(PatternsChangePacket::class.java, PatternsChangePacket.Companion::read, NetworkDirection.PLAY_TO_CLIENT)
|
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.math.BigDecimal
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.function.BooleanSupplier
|
import java.util.function.BooleanSupplier
|
||||||
|
import java.util.function.Consumer
|
||||||
import java.util.function.DoubleConsumer
|
import java.util.function.DoubleConsumer
|
||||||
import java.util.function.DoubleSupplier
|
import java.util.function.DoubleSupplier
|
||||||
import java.util.function.IntConsumer
|
import java.util.function.IntConsumer
|
||||||
@ -529,6 +530,11 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
|||||||
isObserver: Boolean = false,
|
isObserver: Boolean = false,
|
||||||
) : AbstractField<V>(), IMutableField<V> {
|
) : AbstractField<V>(), IMutableField<V> {
|
||||||
private var remote: V = codec.copy(field)
|
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 {
|
init {
|
||||||
if (isObserver) {
|
if (isObserver) {
|
||||||
@ -548,6 +554,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
|||||||
}
|
}
|
||||||
|
|
||||||
this@Field.field = value
|
this@Field.field = value
|
||||||
|
subs.accept(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -577,15 +584,15 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
|||||||
|
|
||||||
if (setter != null) {
|
if (setter != null) {
|
||||||
setter.invoke(value, access, false)
|
setter.invoke(value, access, false)
|
||||||
return
|
} else {
|
||||||
}
|
if (!isDirty && !codec.compare(remote, value)) {
|
||||||
|
notifyEndpoints(this@Field)
|
||||||
|
isDirty = true
|
||||||
|
}
|
||||||
|
|
||||||
if (!isDirty && !codec.compare(remote, value)) {
|
this.field = value
|
||||||
notifyEndpoints(this@Field)
|
subs.accept(value)
|
||||||
isDirty = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.field = value
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun write(stream: DataOutputStream, endpoint: Endpoint) {
|
override fun write(stream: DataOutputStream, endpoint: Endpoint) {
|
||||||
@ -602,10 +609,10 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
|||||||
|
|
||||||
if (setter != null) {
|
if (setter != null) {
|
||||||
setter.invoke(value, access, true)
|
setter.invoke(value, access, true)
|
||||||
return
|
} else {
|
||||||
|
this.field = value
|
||||||
|
subs.accept(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.field = value
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -619,7 +626,21 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
|||||||
/**
|
/**
|
||||||
* Type specific field, storing primitive [Float] directly
|
* 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 val property = object : IMutableFloatProperty {
|
||||||
override fun getValue(thisRef: Any?, property: KProperty<*>): Float {
|
override fun getValue(thisRef: Any?, property: KProperty<*>): Float {
|
||||||
return this@FloatField.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
|
* 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 val property = object : IMutableDoubleProperty {
|
||||||
override fun getValue(thisRef: Any?, property: KProperty<*>): Double {
|
override fun getValue(thisRef: Any?, property: KProperty<*>): Double {
|
||||||
return this@DoubleField.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 {
|
final override val property = object : IMutableIntProperty {
|
||||||
override fun getValue(thisRef: Any?, property: KProperty<*>): Int {
|
override fun getValue(thisRef: Any?, property: KProperty<*>): Int {
|
||||||
return this@AbstractIntField.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
|
* 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 {
|
final override val property = object : IMutableLongProperty {
|
||||||
override fun getValue(thisRef: Any?, property: KProperty<*>): Long {
|
override fun getValue(thisRef: Any?, property: KProperty<*>): Long {
|
||||||
return this@AbstractLongField.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
|
* 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 val property = object : IMutableBooleanProperty {
|
||||||
override fun getValue(thisRef: Any?, property: KProperty<*>): Boolean {
|
override fun getValue(thisRef: Any?, property: KProperty<*>): Boolean {
|
||||||
return this@BooleanField.boolean
|
return this@BooleanField.boolean
|
||||||
@ -1014,6 +1091,11 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
|||||||
) : AbstractField<V>(), IField<V> {
|
) : AbstractField<V>(), IField<V> {
|
||||||
private var remote: Any? = Mark
|
private var remote: Any? = Mark
|
||||||
private var clientValue: 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 {
|
init {
|
||||||
observers.add(this)
|
observers.add(this)
|
||||||
@ -1055,6 +1137,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
|||||||
val newValue = codec.read(stream)
|
val newValue = codec.read(stream)
|
||||||
clientValue = newValue
|
clientValue = newValue
|
||||||
observer.invoke(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
|
* 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 remote: Float = 0f
|
||||||
private var isRemoteSet = false
|
private var isRemoteSet = false
|
||||||
private var clientValue: Float = 0f
|
private var clientValue: Float = 0f
|
||||||
private var isClientValue = false
|
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 val property = object : IFloatProperty {
|
||||||
override fun getValue(thisRef: Any?, property: KProperty<*>): Float {
|
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()
|
val newValue = stream.readFloat()
|
||||||
clientValue = newValue
|
clientValue = newValue
|
||||||
isClientValue = true
|
isClientValue = true
|
||||||
observer.accept(newValue)
|
subs.accept(newValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated("Use type specific property", replaceWith = ReplaceWith("this.property"))
|
@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
|
* 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 remote: Double = 0.0
|
||||||
private var isRemoteSet = false
|
private var isRemoteSet = false
|
||||||
private var clientValue: Double = 0.0
|
private var clientValue: Double = 0.0
|
||||||
private var isClientValue = false
|
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 val property = object : IDoubleProperty {
|
||||||
override fun getValue(thisRef: Any?, property: KProperty<*>): Double {
|
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()
|
val newValue = stream.readDouble()
|
||||||
clientValue = newValue
|
clientValue = newValue
|
||||||
isClientValue = true
|
isClientValue = true
|
||||||
observer.accept(newValue)
|
subs.accept(newValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated("Use type specific property", replaceWith = ReplaceWith("this.property"))
|
@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
|
* 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 remote: Int = 0
|
||||||
private var isRemoteSet = false
|
private var isRemoteSet = false
|
||||||
protected var clientValue: Int = 0
|
protected var clientValue: Int = 0
|
||||||
|
set(value) {
|
||||||
|
if (field != value) {
|
||||||
|
field = value
|
||||||
|
subs.accept(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected var isClientValue = false
|
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 {
|
final override val property = object : IIntProperty {
|
||||||
override fun getValue(thisRef: Any?, property: KProperty<*>): Int {
|
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) {
|
override fun read(stream: DataInputStream) {
|
||||||
check(!isRemoved) { "Field was removed" }
|
check(!isRemoved) { "Field was removed" }
|
||||||
val newValue = stream.readVarIntLE()
|
clientValue = stream.readVarIntLE()
|
||||||
clientValue = newValue
|
|
||||||
isClientValue = true
|
|
||||||
observer.accept(newValue)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1280,10 +1400,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
|||||||
|
|
||||||
override fun read(stream: DataInputStream) {
|
override fun read(stream: DataInputStream) {
|
||||||
check(!isRemoved) { "Field was removed" }
|
check(!isRemoved) { "Field was removed" }
|
||||||
val newValue = stream.readInt()
|
clientValue = stream.readInt()
|
||||||
clientValue = newValue
|
|
||||||
isClientValue = true
|
|
||||||
observer.accept(newValue)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1292,11 +1409,31 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
|||||||
*
|
*
|
||||||
* This class has concrete implementation for [Long] primitive
|
* 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 remote: Long = 0L
|
||||||
private var isRemoteSet = false
|
private var isRemoteSet = false
|
||||||
protected var clientValue: Long = 0L
|
protected var clientValue: Long = 0L
|
||||||
|
set(value) {
|
||||||
|
isClientValue = true
|
||||||
|
|
||||||
|
if (field != value) {
|
||||||
|
field = value
|
||||||
|
subs.accept(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected var isClientValue = false
|
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 {
|
final override val property = object : ILongProperty {
|
||||||
override fun getValue(thisRef: Any?, property: KProperty<*>): Long {
|
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) {
|
override fun read(stream: DataInputStream) {
|
||||||
check(!isRemoved) { "Field was removed" }
|
check(!isRemoved) { "Field was removed" }
|
||||||
val newValue = stream.readVarLongLE()
|
clientValue = stream.readVarLongLE()
|
||||||
clientValue = newValue
|
|
||||||
isClientValue = true
|
|
||||||
observer.accept(newValue)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1373,10 +1507,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
|||||||
|
|
||||||
override fun read(stream: DataInputStream) {
|
override fun read(stream: DataInputStream) {
|
||||||
check(!isRemoved) { "Field was removed" }
|
check(!isRemoved) { "Field was removed" }
|
||||||
val newValue = stream.readLong()
|
clientValue = stream.readLong()
|
||||||
clientValue = newValue
|
|
||||||
isClientValue = true
|
|
||||||
observer.accept(newValue)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1385,11 +1516,22 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
|||||||
*
|
*
|
||||||
* This class has concrete implementation for [Boolean] primitive
|
* 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 remote: Boolean = false
|
||||||
private var isRemoteSet = false
|
private var isRemoteSet = false
|
||||||
private var clientValue: Boolean = false
|
private var clientValue: Boolean = false
|
||||||
private var isClientValue = 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 val property = object : IBooleanProperty {
|
||||||
override fun getValue(thisRef: Any?, property: KProperty<*>): Boolean {
|
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()
|
val newValue = stream.readBoolean()
|
||||||
clientValue = newValue
|
clientValue = newValue
|
||||||
isClientValue = true
|
isClientValue = true
|
||||||
observer.accept(newValue)
|
subs.accept(newValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated("Use type specific property", replaceWith = ReplaceWith("this.property"))
|
@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 getter: () -> V
|
||||||
private val setter: (V) -> Unit
|
private val setter: (V) -> Unit
|
||||||
private var remote: V
|
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
|
override var value: V
|
||||||
get() = getter.invoke()
|
get() = getter.invoke()
|
||||||
@ -1499,6 +1646,7 @@ class FieldSynchronizer(private val callback: Runnable, private val alwaysCallCa
|
|||||||
override fun read(stream: DataInputStream) {
|
override fun read(stream: DataInputStream) {
|
||||||
check(!isRemoved) { "Field was removed" }
|
check(!isRemoved) { "Field was removed" }
|
||||||
this.value = codec.read(stream)
|
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 codec: IStreamCodec<E>,
|
||||||
private val backingSet: MutableSet<E>,
|
private val backingSet: MutableSet<E>,
|
||||||
private val callback: ((changes: Collection<SetChangeset<E>>) -> Unit)? = null,
|
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 var isRemote = false
|
||||||
|
|
||||||
private fun pushBacklog(element: E, action: (DataOutputStream) -> Unit) {
|
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 valueCodec: IStreamCodec<V>,
|
||||||
private val backingMap: MutableMap<K, V>,
|
private val backingMap: MutableMap<K, V>,
|
||||||
private val callback: ((changes: Collection<MapChangeset<K, V>>) -> Unit)? = null,
|
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 sentAllValues = false
|
||||||
private var isRemote = false
|
private var isRemote = false
|
||||||
|
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
package ru.dbotthepony.mc.otm.network.synchronizer
|
package ru.dbotthepony.mc.otm.network.synchronizer
|
||||||
|
|
||||||
import ru.dbotthepony.mc.otm.core.FloatSupplier
|
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.DataInputStream
|
||||||
import java.io.DataOutputStream
|
import java.io.DataOutputStream
|
||||||
import java.util.function.BooleanSupplier
|
import java.util.function.BooleanSupplier
|
||||||
@ -11,7 +17,7 @@ import java.util.function.Supplier
|
|||||||
import kotlin.properties.ReadOnlyProperty
|
import kotlin.properties.ReadOnlyProperty
|
||||||
import kotlin.reflect.KProperty
|
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 observe(): Boolean
|
||||||
fun markDirty()
|
fun markDirty()
|
||||||
fun markDirty(endpoint: FieldSynchronizer.Endpoint)
|
fun markDirty(endpoint: FieldSynchronizer.Endpoint)
|
||||||
@ -40,7 +46,7 @@ interface IFloatProperty {
|
|||||||
operator fun getValue(thisRef: Any?, property: KProperty<*>): Float
|
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 float: Float
|
||||||
val property: IFloatProperty
|
val property: IFloatProperty
|
||||||
|
|
||||||
@ -66,7 +72,7 @@ interface IDoubleProperty {
|
|||||||
operator fun getValue(thisRef: Any?, property: KProperty<*>): Double
|
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 double: Double
|
||||||
val property: IDoubleProperty
|
val property: IDoubleProperty
|
||||||
|
|
||||||
@ -92,7 +98,7 @@ interface IIntProperty {
|
|||||||
operator fun getValue(thisRef: Any?, property: KProperty<*>): Int
|
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 int: Int
|
||||||
val property: IIntProperty
|
val property: IIntProperty
|
||||||
|
|
||||||
@ -118,7 +124,7 @@ interface ILongProperty {
|
|||||||
operator fun getValue(thisRef: Any?, property: KProperty<*>): Long
|
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 long: Long
|
||||||
val property: ILongProperty
|
val property: ILongProperty
|
||||||
|
|
||||||
@ -144,7 +150,7 @@ interface IBooleanProperty {
|
|||||||
operator fun getValue(thisRef: Any?, property: KProperty<*>): Boolean
|
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 boolean: Boolean
|
||||||
val property: IBooleanProperty
|
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.booleans.BooleanConsumer
|
||||||
import it.unimi.dsi.fastutil.floats.FloatConsumer
|
import it.unimi.dsi.fastutil.floats.FloatConsumer
|
||||||
import ru.dbotthepony.mc.otm.core.GetterSetter
|
import ru.dbotthepony.mc.otm.core.GetterSetter
|
||||||
|
import ru.dbotthepony.mc.otm.core.SentientGetterSetter
|
||||||
import java.util.function.DoubleConsumer
|
import java.util.function.DoubleConsumer
|
||||||
import java.util.function.IntConsumer
|
import java.util.function.IntConsumer
|
||||||
import java.util.function.LongConsumer
|
import java.util.function.LongConsumer
|
||||||
import kotlin.reflect.KProperty
|
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 {
|
override fun getValue(thisRef: Any?, property: KProperty<*>): V {
|
||||||
return this.value
|
return this.value
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ import net.minecraftforge.registries.ForgeRegistries
|
|||||||
import ru.dbotthepony.mc.otm.OverdriveThatMatters
|
import ru.dbotthepony.mc.otm.OverdriveThatMatters
|
||||||
import ru.dbotthepony.mc.otm.capability.matter.matter
|
import ru.dbotthepony.mc.otm.capability.matter.matter
|
||||||
import ru.dbotthepony.mc.otm.capability.matteryEnergy
|
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.TranslatableComponent
|
||||||
import ru.dbotthepony.mc.otm.core.ifPresentK
|
import ru.dbotthepony.mc.otm.core.ifPresentK
|
||||||
import ru.dbotthepony.mc.otm.core.registryName
|
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_CREATIVE)
|
||||||
accept(MItems.PATTERN_DRIVE_CREATIVE2)
|
accept(MItems.PATTERN_DRIVE_CREATIVE2)
|
||||||
|
|
||||||
|
accept(MItems.PORTABLE_CONDENSATION_DRIVE)
|
||||||
|
accept(MItems.PORTABLE_DENSE_CONDENSATION_DRIVE)
|
||||||
|
|
||||||
fluids(MItems.FLUID_CAPSULE)
|
fluids(MItems.FLUID_CAPSULE)
|
||||||
fluids(MItems.FLUID_TANK)
|
fluids(MItems.FLUID_TANK)
|
||||||
|
|
||||||
@ -285,7 +288,7 @@ object MCreativeTabs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun register(event: BuildCreativeModeTabContentsEvent) {
|
fun register(event: BuildCreativeModeTabContentsEvent) {
|
||||||
CreativeMenuComparator.invalidate()
|
CreativeMenuItemComparator.invalidate()
|
||||||
|
|
||||||
when (event.tab) {
|
when (event.tab) {
|
||||||
MAIN -> addMainCreativeTabItems(event)
|
MAIN -> addMainCreativeTabItems(event)
|
||||||
|
@ -156,8 +156,8 @@ object MItems {
|
|||||||
::MATTER_SCANNER, ::MATTER_PANEL, ::MATTER_REPLICATOR, ::MATTER_BOTTLER, ::ENERGY_COUNTER, ::CHEMICAL_GENERATOR,
|
::MATTER_SCANNER, ::MATTER_PANEL, ::MATTER_REPLICATOR, ::MATTER_BOTTLER, ::ENERGY_COUNTER, ::CHEMICAL_GENERATOR,
|
||||||
::MATTER_RECYCLER, ::PLATE_PRESS, ::TWIN_PLATE_PRESS, ::POWERED_FURNACE, ::POWERED_BLAST_FURNACE,
|
::MATTER_RECYCLER, ::PLATE_PRESS, ::TWIN_PLATE_PRESS, ::POWERED_FURNACE, ::POWERED_BLAST_FURNACE,
|
||||||
::POWERED_SMOKER,
|
::POWERED_SMOKER,
|
||||||
// ::STORAGE_BUS, ::STORAGE_IMPORTER, ::STORAGE_EXPORTER, ::DRIVE_VIEWER,
|
::STORAGE_BUS, ::STORAGE_IMPORTER, ::STORAGE_EXPORTER, ::DRIVE_VIEWER,
|
||||||
// ::DRIVE_RACK, ::ITEM_MONITOR, ::STORAGE_CABLE, ::STORAGE_POWER_SUPPLIER,
|
::DRIVE_RACK, ::ITEM_MONITOR, ::STORAGE_CABLE, ::STORAGE_POWER_SUPPLIER,
|
||||||
::ENERGY_SERVO,
|
::ENERGY_SERVO,
|
||||||
::PHANTOM_ATTRACTOR, ::GRAVITATION_STABILIZER, ::COBBLESTONE_GENERATOR, ::INFINITE_WATER_SOURCE,
|
::PHANTOM_ATTRACTOR, ::GRAVITATION_STABILIZER, ::COBBLESTONE_GENERATOR, ::INFINITE_WATER_SOURCE,
|
||||||
::ESSENCE_STORAGE, ::MATTER_RECONSTRUCTOR
|
::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
|
* @return copy of object, with amount of units actually extracted
|
||||||
*/
|
*/
|
||||||
fun extractStack(id: UUID, amount: BigInteger, simulate: Boolean): T
|
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
|
package ru.dbotthepony.mc.otm.storage
|
||||||
|
|
||||||
|
import net.minecraft.network.chat.Component
|
||||||
import net.minecraft.world.item.Item
|
import net.minecraft.world.item.Item
|
||||||
import net.minecraft.world.item.ItemStack
|
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 ru.dbotthepony.mc.otm.core.math.toIntSafe
|
||||||
import java.math.BigInteger
|
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 isEmpty: Boolean = stack.isEmpty || super.isEmpty
|
||||||
override val maxStackSize: BigInteger = BigInteger.valueOf(stack.maxStackSize.toLong())
|
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 {
|
fun toItemStack(count: Int = this.count.toIntSafe()): ItemStack {
|
||||||
return stack.copyWithCount(count)
|
return stack.copyWithCount(count)
|
||||||
}
|
}
|
||||||
|
@ -63,9 +63,19 @@ abstract class StorageStack<S : StorageStack<S>>(val count: BigInteger) {
|
|||||||
fun read(buff: FriendlyByteBuf): T
|
fun read(buff: FriendlyByteBuf): T
|
||||||
fun write(buff: FriendlyByteBuf, value: 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)
|
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)
|
fun energyPerExtract(stack: T): Decimal = energyPerOperation(stack)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If there is not enough energy for operation, it is completely cancelled
|
||||||
|
*/
|
||||||
fun energyPerOperation(stack: T): Decimal
|
fun energyPerOperation(stack: T): Decimal
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,7 +123,7 @@ abstract class StorageStack<S : StorageStack<S>>(val count: BigInteger) {
|
|||||||
ItemStorageStack.unsafe(it.readItem(), it.readBigInteger())
|
ItemStorageStack.unsafe(it.readItem(), it.readBigInteger())
|
||||||
},
|
},
|
||||||
{ buff, v ->
|
{ buff, v ->
|
||||||
buff.writeItem(v.toItemStack())
|
buff.writeItem(v.toItemStack(1))
|
||||||
buff.writeBigInteger(v.count)
|
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.Object2ObjectOpenCustomHashMap
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
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.isPositive
|
||||||
import ru.dbotthepony.mc.otm.core.math.isZero
|
import ru.dbotthepony.mc.otm.core.math.isZero
|
||||||
import java.math.BigInteger
|
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 -> Кортеж
|
// удаленный UUID -> Кортеж
|
||||||
protected val remoteTuples = Object2ObjectOpenHashMap<UUID, RemoteTuple<T>>()
|
private val remoteTuples = Object2ObjectOpenHashMap<UUID, RemoteTuple<T>>()
|
||||||
|
|
||||||
// локальный UUID -> Локальный кортеж
|
// локальный 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 для скорости работы
|
private val listeners = ObjectLinkedOpenHashSet<IStorageEventConsumer<T>>()
|
||||||
protected val listeners: MutableSet<IStorageEventConsumer<T>> = ObjectArraySet()
|
private val children = ObjectLinkedOpenHashSet<IStorage<T>>()
|
||||||
protected val children: MutableSet<IStorage<T>> = ObjectArraySet()
|
private val consumers = ObjectLinkedOpenHashSet<IStorageAcceptor<T>>()
|
||||||
protected val consumers: MutableSet<IStorageAcceptor<T>> = ObjectArraySet()
|
|
||||||
|
|
||||||
protected open fun onAdd(identity: IStorage<T>) {}
|
|
||||||
protected open fun onRemove(identity: IStorage<T>) {}
|
|
||||||
|
|
||||||
override fun add(identity: IStorage<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" }
|
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>) {
|
if (identity is IStorageAcceptor<T>) {
|
||||||
consumers.add(identity)
|
consumers.add(identity)
|
||||||
}
|
}
|
||||||
|
|
||||||
onAdd(identity)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,8 +93,6 @@ open class VirtualComponent<T : StorageStack<T>>(override val storageType: Stora
|
|||||||
if (identity is IStorageAcceptor<T>) {
|
if (identity is IStorageAcceptor<T>) {
|
||||||
consumers.remove(identity)
|
consumers.remove(identity)
|
||||||
}
|
}
|
||||||
|
|
||||||
onRemove(identity)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,4 +224,8 @@ open class VirtualComponent<T : StorageStack<T>>(override val storageType: Stora
|
|||||||
|
|
||||||
return this.storageType.empty
|
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) {
|
) : IStorageComponent<T>, IStorageProvider<T> by PoweredStorageProvider(parent, energy), IStorageAcceptor<T> by PoweredStorageAcceptor(parent, energy) {
|
||||||
override val storageType: StorageStack.Type<T>
|
override val storageType: StorageStack.Type<T>
|
||||||
get() = parent.storageType
|
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
|
import java.util.function.Supplier
|
||||||
|
|
||||||
class PoweredStorageAcceptor<T : StorageStack<T>>(val parent: IStorageAcceptor<T>, val energy: Supplier<IMatteryEnergyStorage>) : IStorageAcceptor<T> by parent {
|
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 {
|
override fun insertStack(stack: T, simulate: Boolean): T {
|
||||||
val leftover = parent.insertStack(stack, true)
|
val leftover = parent.insertStack(stack, true)
|
||||||
if (leftover == stack) return stack
|
if (leftover == stack) return stack
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
package ru.dbotthepony.mc.otm.storage.powered
|
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.capability.energy.IMatteryEnergyStorage
|
||||||
|
import ru.dbotthepony.mc.otm.storage.IStorageEventConsumer
|
||||||
import ru.dbotthepony.mc.otm.storage.IStorageProvider
|
import ru.dbotthepony.mc.otm.storage.IStorageProvider
|
||||||
import ru.dbotthepony.mc.otm.storage.StorageStack
|
import ru.dbotthepony.mc.otm.storage.StorageStack
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
@ -8,8 +12,57 @@ import java.util.*
|
|||||||
import java.util.function.Supplier
|
import java.util.function.Supplier
|
||||||
|
|
||||||
class PoweredStorageProvider<T : StorageStack<T>>(val parent: IStorageProvider<T>, val energy: Supplier<IMatteryEnergyStorage>) : IStorageProvider<T> by parent {
|
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 {
|
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
|
if (extracted.isEmpty) return extracted
|
||||||
|
|
||||||
val energy = energy.get()
|
val energy = energy.get()
|
||||||
|
@ -16,6 +16,18 @@ class PoweredVirtualComponent<T : StorageStack<T>>(
|
|||||||
) : IVirtualStorageComponent<T>, IStorageComponent<T> by PoweredComponent(parent, energy) {
|
) : IVirtualStorageComponent<T>, IStorageComponent<T> by PoweredComponent(parent, energy) {
|
||||||
constructor(type: StorageStack.Type<T>, energy: Supplier<IMatteryEnergyStorage>) : this(VirtualComponent(type), 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 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 onStackChanged(stack: T, id: UUID) = parent.onStackChanged(stack, id)
|
||||||
override fun onStackRemoved(id: UUID) = parent.onStackRemoved(id)
|
override fun onStackRemoved(id: UUID) = parent.onStackRemoved(id)
|
||||||
|
Loading…
Reference in New Issue
Block a user