Compare commits

...

9 Commits

7 changed files with 155 additions and 73 deletions

View File

@ -1,5 +1,6 @@
package ru.dbotthepony.mc.otm.block.entity
import it.unimi.dsi.fastutil.booleans.BooleanConsumer
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
import it.unimi.dsi.fastutil.longs.Long2ObjectFunction
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap
@ -46,15 +47,18 @@ import ru.dbotthepony.mc.otm.core.collect.WeakHashSet
import ru.dbotthepony.mc.otm.core.get
import ru.dbotthepony.mc.otm.core.math.BlockRotation
import ru.dbotthepony.mc.otm.core.math.RelativeSide
import ru.dbotthepony.mc.otm.core.math.plus
import ru.dbotthepony.mc.otm.core.util.IntCounter
import ru.dbotthepony.mc.otm.core.util.Savetables
import ru.dbotthepony.mc.otm.core.util.TickList
import ru.dbotthepony.mc.otm.core.util.countingLazy
import ru.dbotthepony.mc.otm.network.BlockEntitySyncPacket
import ru.dbotthepony.mc.otm.onceServer
import ru.dbotthepony.mc.otm.registry.MBlocks
import ru.dbotthepony.mc.otm.sometimeServer
import java.lang.ref.WeakReference
import java.util.*
import java.util.function.BooleanSupplier
import java.util.function.Consumer
import java.util.function.Predicate
import java.util.function.Supplier
@ -69,7 +73,7 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
private val sidelessCaps = Reference2ObjectOpenHashMap<BlockCapability<*, *>, Any>()
private val sidedCaps = Array(RelativeSide.entries.size) {
Reference2ObjectOpenHashMap<BlockCapability<*, *>, Any>()
Reference2ObjectOpenHashMap<BlockCapability<*, *>, ControllableCapability<*>>()
}
protected val tickList = TickList()
@ -119,33 +123,97 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
tickList.tick()
}
interface CapabilityControl : BooleanSupplier, BooleanConsumer {
var isEnabled: Boolean
fun doDispatchInvalidation()
fun dontDispatchInvalidation()
override fun getAsBoolean(): Boolean {
return isEnabled
}
override fun accept(t: Boolean) {
isEnabled = t
}
}
private inner class ControllableCapability<T : Any>(val capability: T) : CapabilityControl {
override var isEnabled: Boolean = true
set(value) {
if (value != field) {
field = value
if (dispatchInvalidation) level?.invalidateCapabilities(blockPos)
}
}
private var dispatchInvalidation = true
override fun doDispatchInvalidation() {
dispatchInvalidation = true
}
override fun dontDispatchInvalidation() {
dispatchInvalidation = false
}
fun getOrNull(): T? {
return if (isEnabled) capability else null
}
}
private inner class ControllableCapabilitySet(val capabilities: List<CapabilityControl>) : CapabilityControl {
override var isEnabled: Boolean
get() = capabilities.all { it.isEnabled }
set(value) {
capabilities.forEach { it.dontDispatchInvalidation() }
try {
capabilities.forEach { it.isEnabled = value }
} finally {
capabilities.forEach { it.doDispatchInvalidation() }
level?.invalidateCapabilities(blockPos)
}
}
override fun doDispatchInvalidation() {
capabilities.forEach { it.doDispatchInvalidation() }
}
override fun dontDispatchInvalidation() {
capabilities.forEach { it.dontDispatchInvalidation() }
}
}
/**
* exposes capability when no side is specified
*/
protected fun <T : Any> exposeSideless(capability: BlockCapability<T, *>, value: T) {
check(!sidelessCaps.containsKey(capability)) { "Already has globally exposed $capability!" }
MBlocks.ensureCapabilityIsKnown(capability)
sidelessCaps[capability] = value
setChanged()
level?.invalidateCapabilities(blockPos)
}
protected fun <T : Any> exposeSided(side: RelativeSide, capability: BlockCapability<T, *>, value: T) {
protected fun <T : Any> exposeSided(side: RelativeSide, capability: BlockCapability<T, *>, value: T): CapabilityControl {
val map = sidedCaps[side.ordinal]
check(!map.containsKey(capability)) { "Already has exposed $capability on $side!" }
map[capability] = value
MBlocks.ensureCapabilityIsKnown(capability)
val wrapper = ControllableCapability(value)
map[capability] = wrapper
setChanged()
level?.invalidateCapabilities(blockPos)
return wrapper
}
/**
* Exposes capability unconditionally, on all sides and sideless
*/
protected fun <T : Any> exposeGlobally(capability: BlockCapability<T, *>, value: T, predicate: Predicate<RelativeSide> = Predicate { true }) {
protected fun <T : Any> exposeGlobally(capability: BlockCapability<T, *>, value: T, predicate: Predicate<RelativeSide> = Predicate { true }): CapabilityControl {
exposeSideless(capability, value)
for (side in RelativeSide.entries)
if (predicate.test(side))
exposeSided(side, capability, value)
return ControllableCapabilitySet(RelativeSide.entries.stream().filter(predicate).map { exposeSided(it, capability, value) }.toList())
}
protected fun exposeEnergySideless(value: IMatteryEnergyStorage) {
@ -153,14 +221,22 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
exposeSideless(MatteryCapability.BLOCK_ENERGY, value)
}
protected fun exposeEnergyGlobally(value: IMatteryEnergyStorage) {
exposeGlobally(Capabilities.EnergyStorage.BLOCK, value)
exposeGlobally(MatteryCapability.BLOCK_ENERGY, value)
protected fun exposeEnergyGlobally(value: IMatteryEnergyStorage): CapabilityControl {
return ControllableCapabilitySet(
listOf(
exposeGlobally(Capabilities.EnergyStorage.BLOCK, value),
exposeGlobally(MatteryCapability.BLOCK_ENERGY, value)
)
)
}
protected fun exposeEnergySided(side: RelativeSide, value: IMatteryEnergyStorage) {
exposeSided(side, Capabilities.EnergyStorage.BLOCK, value)
exposeSided(side, MatteryCapability.BLOCK_ENERGY, value)
protected fun exposeEnergySided(side: RelativeSide, value: IMatteryEnergyStorage): CapabilityControl {
return ControllableCapabilitySet(
listOf(
exposeSided(side, Capabilities.EnergyStorage.BLOCK, value),
exposeSided(side, MatteryCapability.BLOCK_ENERGY, value)
)
)
}
protected fun waitForServerLevel(lambda: () -> Unit) {
@ -173,7 +249,7 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
fun <T : Any> getCapability(cap: BlockCapability<T, *>, side: Direction?): T? {
if (side != null) {
return sidedCaps[blockRotation.dir2Side(side).ordinal][cap] as T?
return sidedCaps[blockRotation.dir2Side(side).ordinal][cap]?.getOrNull() as T?
}
return sidelessCaps[cap] as T?
@ -277,9 +353,10 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
val creationVersion = ++currentVersion
val direction = blockRotation.side2Dir(side)
cache = BlockCapabilityCache.create(
capability, level, blockPos,
blockRotation.side2Dir(side),
capability, level, blockPos + direction.normal, direction.opposite,
{ !isRemoved || creationVersion != currentVersion },
// IllegalStateException("Do not call getCapability on an invalid cache or from the invalidation listener!")
// what a shame.

View File

@ -147,10 +147,7 @@ abstract class MatteryDeviceBlockEntity(blockEntityType: BlockEntityType<*>, blo
inner class Piece(val side: RelativeSide) : IFluidHandler, ITickable {
private val ticker = tickList.Ticker(this)
private val neighbour = CapabilityCache(side, Capabilities.FluidHandler.BLOCK)
init {
exposeSided(side, Capabilities.FluidHandler.BLOCK, this)
}
private val control = exposeSided(side, Capabilities.FluidHandler.BLOCK, this)
private fun updateTickerState() {
ticker.isEnabled =
@ -174,7 +171,7 @@ abstract class MatteryDeviceBlockEntity(blockEntityType: BlockEntityType<*>, blo
access.accept(value)
markDirtyFast()
updateTickerState()
level?.invalidateCapabilities(blockPos)
control.isEnabled = value != FlowDirection.NONE
}
})
@ -273,6 +270,12 @@ abstract class MatteryDeviceBlockEntity(blockEntityType: BlockEntityType<*>, blo
return FluidStack.EMPTY
}
}
init {
if (flow == FlowDirection.NONE) {
control.isEnabled = false
}
}
}
}
@ -350,10 +353,7 @@ abstract class MatteryDeviceBlockEntity(blockEntityType: BlockEntityType<*>, blo
inner class Piece(val side: RelativeSide, val possibleModes: FlowDirection) : IMatteryEnergyStorage, ITickable {
private val neighbour = CapabilityCache(side, Capabilities.EnergyStorage.BLOCK)
init {
exposeEnergySided(side, this)
}
private val control = exposeEnergySided(side, this)
override var batteryLevel: Decimal by energy::batteryLevel
override val maxBatteryLevel: Decimal by energy::maxBatteryLevel
@ -453,9 +453,15 @@ abstract class MatteryDeviceBlockEntity(blockEntityType: BlockEntityType<*>, blo
access.accept(value)
markDirtyFast()
updateTickerState()
level?.invalidateCapabilities(blockPos)
control.isEnabled = value != FlowDirection.NONE
}
}).delegate
init {
if (energyFlow == FlowDirection.NONE) {
control.isEnabled = false
}
}
}
}
@ -566,10 +572,7 @@ abstract class MatteryDeviceBlockEntity(blockEntityType: BlockEntityType<*>, blo
private val neighbour = CapabilityCache(side, Capabilities.ItemHandler.BLOCK)
private val ticker = tickList.Ticker(this)
init {
exposeSided(side, Capabilities.ItemHandler.BLOCK, this)
}
private val control = exposeSided(side, Capabilities.ItemHandler.BLOCK, this)
private var innerSlotPull = 0
private var outerSlotPull = 0
@ -601,7 +604,7 @@ abstract class MatteryDeviceBlockEntity(blockEntityType: BlockEntityType<*>, blo
ItemHandlerMode.BATTERY -> battery!!
}
level?.invalidateCapabilities(blockPos)
control.isEnabled = value != ItemHandlerMode.DISABLED
}
}).delegate
@ -705,6 +708,12 @@ abstract class MatteryDeviceBlockEntity(blockEntityType: BlockEntityType<*>, blo
override fun isItemValid(slot: Int, stack: ItemStack): Boolean {
return currentHandler.isItemValid(slot, stack)
}
init {
if (mode == ItemHandlerMode.DISABLED) {
control.isEnabled = false
}
}
}
}

View File

@ -3,6 +3,7 @@ package ru.dbotthepony.mc.otm.client.screen
import com.mojang.blaze3d.systems.RenderSystem
import it.unimi.dsi.fastutil.ints.Int2ObjectFunction
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
import it.unimi.dsi.fastutil.objects.ObjectArrayList
import net.minecraft.ChatFormatting
import net.minecraft.client.gui.Font
import net.minecraft.client.gui.GuiGraphics
@ -54,6 +55,7 @@ import ru.dbotthepony.mc.otm.menu.widget.LevelGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
import java.util.*
import kotlin.collections.ArrayDeque
import kotlin.collections.ArrayList
import kotlin.collections.List
import kotlin.collections.MutableSet
import kotlin.collections.isNotEmpty
@ -471,7 +473,7 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
override fun onClose() {
super.onClose()
panels.forEach { it.markRemoved() }
ObjectArrayList(panels).forEach { it.markRemoved() }
}
public override fun recalculateQuickCraftRemaining() {
@ -506,7 +508,7 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
var click = false
var focusKilled = false
for (panel in panels) {
for (panel in ObjectArrayList(panels)) {
if (click || !panel.mouseClickedChecked(x, y, button)) {
focusKilled = panel.killFocus() || focusKilled
} else {
@ -525,7 +527,7 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
private var lastDragSlot: Slot? = null
override fun mouseDragged(x: Double, y: Double, button: Int, xDelta: Double, yDelta: Double): Boolean {
for (panel in panels) {
for (panel in ObjectArrayList(panels)) {
if (panel.mouseDraggedChecked(x, y, button, xDelta, yDelta)) {
if (returnSlot != null) {
super.mouseDragged(x, y, button, xDelta, yDelta)
@ -553,7 +555,7 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
}
}
for (panel in panels) {
for (panel in ObjectArrayList(panels)) {
if (panel.mouseReleasedChecked(p_97812_, p_97813_, p_97814_)) {
if (returnSlot != null) {
super.mouseReleased(p_97812_, p_97813_, p_97814_)
@ -569,7 +571,7 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
}
override fun mouseScrolled(mouseX: Double, mouseY: Double, scrollX: Double, scrollY: Double): Boolean {
for (panel in panels) {
for (panel in ObjectArrayList(panels)) {
if (panel.mouseScrolledChecked(mouseX, mouseY, scrollY)) {
return true
}
@ -579,7 +581,7 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
}
override fun keyReleased(p_94715_: Int, p_94716_: Int, p_94717_: Int): Boolean {
for (panel in panels) {
for (panel in ObjectArrayList(panels)) {
if (panel.keyReleased(p_94715_, p_94716_, p_94717_)) {
return true
}
@ -589,7 +591,7 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
}
override fun charTyped(p_94683_: Char, p_94684_: Int): Boolean {
for (panel in panels) {
for (panel in ObjectArrayList(panels)) {
if (panel.charTyped(p_94683_, p_94684_)) {
return true
}
@ -599,7 +601,7 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
}
override fun keyPressed(key: Int, scancode: Int, mods: Int): Boolean {
for (panel in panels) {
for (panel in ObjectArrayList(panels)) {
if (panel.keyPressed(key, scancode, mods)) {
if (returnSlot != null) {
super.keyPressed(key, scancode, mods)

View File

@ -32,6 +32,7 @@ import ru.dbotthepony.mc.otm.core.collect.count
import ru.dbotthepony.mc.otm.core.collect.emptyIterator
import ru.dbotthepony.mc.otm.core.collect.filter
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.nbt.set
import ru.dbotthepony.mc.otm.core.registryName
@ -286,29 +287,13 @@ open class MatteryContainer(var listener: ContainerListener, private val size: I
}
override fun serializeNBT(registries: HolderLookup.Provider): CompoundTag {
return CompoundTag().also {
it["items"] = ListTag().also {
for ((i, item) in slots.withIndex()) {
if (!item.isEmpty) {
it.add(item.save(registries).also {
it as CompoundTag
it["slotIndex"] = i
})
}
}
}
val state = SerializedState(
slotIterator(true).map { SerializedItem(it.item, it.slot) }.toList(size),
filters.withIndex().iterator().filter { it.value != null }.map { SerializedFilter(it.value!!, it.index) }.toList()
)
it["filters"] = ListTag().also {
for ((i, filter) in filters.withIndex()) {
if (filter != null) {
it.add(CompoundTag().also {
it["filter"] = filter.registryName!!.toString()
it["slotIndex"] = i
})
}
}
}
}
return SerializedState.CODEC.encodeStart(registries.createSerializationContext(NbtOps.INSTANCE), state)
.resultOrPartial { throw RuntimeException("Failed to encode container contents: $it") }.get() as CompoundTag
}
final override fun isEmpty(): Boolean {

View File

@ -1,7 +0,0 @@
package ru.dbotthepony.mc.otm.item.armor
import net.minecraft.world.item.ArmorItem
import ru.dbotthepony.mc.otm.registry.MArmorMaterials
class SimpleTritaniumArmorItem(slot: Type) : ArmorItem(MArmorMaterials.SIMPLE_TRITANIUM, slot, Properties().stacksTo(1)) {
}

View File

@ -1,5 +1,6 @@
package ru.dbotthepony.mc.otm.registry
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet
import net.minecraft.core.Direction
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.util.valueproviders.UniformInt
@ -19,6 +20,7 @@ import net.minecraft.world.level.material.MapColor
import net.minecraft.world.level.material.PushReaction
import net.neoforged.bus.api.IEventBus
import net.neoforged.neoforge.capabilities.BlockCapability
import net.neoforged.neoforge.capabilities.Capabilities
import net.neoforged.neoforge.capabilities.IBlockCapabilityProvider
import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent
import ru.dbotthepony.mc.otm.block.BlackHoleBlock
@ -77,6 +79,7 @@ import ru.dbotthepony.mc.otm.block.tech.PlatePressBlock
import ru.dbotthepony.mc.otm.block.tech.PoweredBlastFurnaceBlock
import ru.dbotthepony.mc.otm.block.tech.PoweredFurnaceBlock
import ru.dbotthepony.mc.otm.block.tech.PoweredSmokerBlock
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.config.CablesConfig
import ru.dbotthepony.mc.otm.core.collect.SupplierList
import ru.dbotthepony.mc.otm.core.collect.SupplierMap
@ -85,9 +88,23 @@ import java.util.function.Supplier
object MBlocks {
private val registry = MDeferredRegister(BuiltInRegistries.BLOCK)
private val knownCaps = ObjectOpenHashSet<BlockCapability<*, *>>()
fun ensureCapabilityIsKnown(cap: BlockCapability<*, *>) {
check(cap in knownCaps) { "Tried to expose capability which wasn't known on mod startup. This is unfortunately limitation with new capability system and can not be fixed, unless NeoForge team decides to rethink their decision to remove per block(entity) dynamic capabilities." }
}
private fun registerCapabilities(event: RegisterCapabilitiesEvent) {
// static initializers
MatteryCapability.BLOCK_ENERGY
Capabilities.FluidHandler.BLOCK
Capabilities.EnergyStorage.BLOCK
Capabilities.ItemHandler.BLOCK
// ugly
for (cap in BlockCapability.getAll()) {
knownCaps.add(cap)
val provider = IBlockCapabilityProvider<Any?, Direction?> { level, pos, state, be, context: Direction? ->
if (be is MatteryBlockEntity) {
return@IBlockCapabilityProvider be.getCapability(cap as BlockCapability<Any, Direction?>, context)

View File

@ -55,7 +55,6 @@ import ru.dbotthepony.mc.otm.item.SimpleUpgrade
import ru.dbotthepony.mc.otm.item.ZPMItem
import ru.dbotthepony.mc.otm.item.addSimpleDescription
import ru.dbotthepony.mc.otm.item.armor.PortableGravitationStabilizerItem
import ru.dbotthepony.mc.otm.item.armor.SimpleTritaniumArmorItem
import ru.dbotthepony.mc.otm.item.armor.TritaniumArmorItem
import ru.dbotthepony.mc.otm.item.exopack.ExopackProbeItem
import ru.dbotthepony.mc.otm.item.exopack.ExopackSlotUpgradeItem
@ -363,10 +362,10 @@ object MItems {
val TRITANIUM_PANTS: TritaniumArmorItem by registry.register(MNames.TRITANIUM_PANTS) { TritaniumArmorItem(ArmorItem.Type.LEGGINGS) }
val TRITANIUM_BOOTS: TritaniumArmorItem by registry.register(MNames.TRITANIUM_BOOTS) { TritaniumArmorItem(ArmorItem.Type.BOOTS) }
val SIMPLE_TRITANIUM_HELMET: SimpleTritaniumArmorItem by registry.register(MNames.SIMPLE_TRITANIUM_HELMET) { SimpleTritaniumArmorItem(ArmorItem.Type.HELMET) }
val SIMPLE_TRITANIUM_CHESTPLATE: SimpleTritaniumArmorItem by registry.register(MNames.SIMPLE_TRITANIUM_CHESTPLATE) { SimpleTritaniumArmorItem(ArmorItem.Type.CHESTPLATE) }
val SIMPLE_TRITANIUM_PANTS: SimpleTritaniumArmorItem by registry.register(MNames.SIMPLE_TRITANIUM_PANTS) { SimpleTritaniumArmorItem(ArmorItem.Type.LEGGINGS) }
val SIMPLE_TRITANIUM_BOOTS: SimpleTritaniumArmorItem by registry.register(MNames.SIMPLE_TRITANIUM_BOOTS) { SimpleTritaniumArmorItem(ArmorItem.Type.BOOTS) }
val SIMPLE_TRITANIUM_HELMET: ArmorItem by registry.register(MNames.SIMPLE_TRITANIUM_HELMET) { ArmorItem(MArmorMaterials.SIMPLE_TRITANIUM, ArmorItem.Type.HELMET, Properties().stacksTo(1)) }
val SIMPLE_TRITANIUM_CHESTPLATE: ArmorItem by registry.register(MNames.SIMPLE_TRITANIUM_CHESTPLATE) { ArmorItem(MArmorMaterials.SIMPLE_TRITANIUM, ArmorItem.Type.CHESTPLATE, Properties().stacksTo(1)) }
val SIMPLE_TRITANIUM_PANTS: ArmorItem by registry.register(MNames.SIMPLE_TRITANIUM_PANTS) { ArmorItem(MArmorMaterials.SIMPLE_TRITANIUM, ArmorItem.Type.LEGGINGS, Properties().stacksTo(1)) }
val SIMPLE_TRITANIUM_BOOTS: ArmorItem by registry.register(MNames.SIMPLE_TRITANIUM_BOOTS) { ArmorItem(MArmorMaterials.SIMPLE_TRITANIUM, ArmorItem.Type.BOOTS, Properties().stacksTo(1)) }
val TRITANIUM_ARMOR = SupplierList(
::TRITANIUM_HELMET,