Black hole generator test (for real this time)
This commit is contained in:
parent
b832873203
commit
58af515f9a
@ -510,6 +510,10 @@ private fun blocks(provider: MatteryLanguageProvider) {
|
|||||||
add(MBlocks.ITEM_OUTPUT_HATCH, "Item Output Hatch")
|
add(MBlocks.ITEM_OUTPUT_HATCH, "Item Output Hatch")
|
||||||
add(MBlocks.MATTER_OUTPUT_HATCH, "Matter Output Hatch")
|
add(MBlocks.MATTER_OUTPUT_HATCH, "Matter Output Hatch")
|
||||||
|
|
||||||
|
add(MBlocks.MATTER_INJECTOR, "Matter Injector")
|
||||||
|
add(MBlocks.ANTIMATTER_INJECTOR, "Antimatter Injector")
|
||||||
|
add(MBlocks.HIGH_ENERGY_PARTICLE_COLLECTOR, "High Energy Particle Collector")
|
||||||
|
|
||||||
addBlock(MBlocks.COBBLESTONE_GENERATOR.values, "Cobblestone Generator")
|
addBlock(MBlocks.COBBLESTONE_GENERATOR.values, "Cobblestone Generator")
|
||||||
add(MBlocks.INFINITE_WATER_SOURCE, "Infinite Water Source")
|
add(MBlocks.INFINITE_WATER_SOURCE, "Infinite Water Source")
|
||||||
add(MBlocks.INFINITE_WATER_SOURCE, "desc", "Pushes water into all neighbour blocks")
|
add(MBlocks.INFINITE_WATER_SOURCE, "desc", "Pushes water into all neighbour blocks")
|
||||||
|
@ -520,6 +520,10 @@ private fun blocks(provider: MatteryLanguageProvider) {
|
|||||||
add(MBlocks.ITEM_OUTPUT_HATCH, "Выходной предметный клапан")
|
add(MBlocks.ITEM_OUTPUT_HATCH, "Выходной предметный клапан")
|
||||||
add(MBlocks.MATTER_OUTPUT_HATCH, "Выходной клапан материи")
|
add(MBlocks.MATTER_OUTPUT_HATCH, "Выходной клапан материи")
|
||||||
|
|
||||||
|
add(MBlocks.MATTER_INJECTOR, "Инжектор материи")
|
||||||
|
add(MBlocks.ANTIMATTER_INJECTOR, "Инжектор анти-материи")
|
||||||
|
add(MBlocks.HIGH_ENERGY_PARTICLE_COLLECTOR, "Коллектор высокоэнергичных частиц")
|
||||||
|
|
||||||
add(MBlocks.DEV_CHEST, "Сундук разработчика")
|
add(MBlocks.DEV_CHEST, "Сундук разработчика")
|
||||||
add(MBlocks.DEV_CHEST, "desc", "Хранит все предметы, которые есть в игре")
|
add(MBlocks.DEV_CHEST, "desc", "Хранит все предметы, которые есть в игре")
|
||||||
add(MBlocks.PAINTER, "Стол маляра")
|
add(MBlocks.PAINTER, "Стол маляра")
|
||||||
|
@ -41,6 +41,18 @@ fun addTags(tagsProvider: TagsProvider) {
|
|||||||
tagsProvider.storageBlocksAsBlock.add("tritanium", MBlocks.TRITANIUM_INGOT_BLOCK)
|
tagsProvider.storageBlocksAsBlock.add("tritanium", MBlocks.TRITANIUM_INGOT_BLOCK)
|
||||||
tagsProvider.blocks.Appender(BlockTags.BEACON_BASE_BLOCKS).add(MBlocks.TRITANIUM_INGOT_BLOCK)
|
tagsProvider.blocks.Appender(BlockTags.BEACON_BASE_BLOCKS).add(MBlocks.TRITANIUM_INGOT_BLOCK)
|
||||||
|
|
||||||
|
tagsProvider.blocks.Appender(MBlockTags.MULTIBLOCK_STRUCTURE)
|
||||||
|
.add(MBlockTags.MULTIBLOCK_HARD_STRUCTURE, MBlockTags.MULTIBLOCK_SOFT_STRUCTURE)
|
||||||
|
|
||||||
|
tagsProvider.blocks.Appender(MBlockTags.MULTIBLOCK_SOFT_STRUCTURE)
|
||||||
|
.add(MBlockTags.HARDENED_GLASS)
|
||||||
|
|
||||||
|
tagsProvider.blocks.Appender(MBlockTags.MULTIBLOCK_HARD_STRUCTURE)
|
||||||
|
.add(MRegistry.VENT.allBlocks.values)
|
||||||
|
.add(MRegistry.VENT_ALTERNATIVE.allBlocks.values)
|
||||||
|
.add(MRegistry.TRITANIUM_BLOCK.allBlocks.values)
|
||||||
|
.add(MRegistry.TRITANIUM_STRIPED_BLOCK.blocks.values.stream().flatMap { it.values.stream() })
|
||||||
|
|
||||||
tagsProvider.stoneOre("tritanium", MBlocks.TRITANIUM_ORE)
|
tagsProvider.stoneOre("tritanium", MBlocks.TRITANIUM_ORE)
|
||||||
tagsProvider.deepslateOre("tritanium", MBlocks.DEEPSLATE_TRITANIUM_ORE)
|
tagsProvider.deepslateOre("tritanium", MBlocks.DEEPSLATE_TRITANIUM_ORE)
|
||||||
tagsProvider.singleDropOre(
|
tagsProvider.singleDropOre(
|
||||||
|
@ -5,7 +5,7 @@ import org.spongepowered.asm.mixin.Mixin;
|
|||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
import ru.dbotthepony.mc.otm.core.MultiblockKt;
|
import ru.dbotthepony.mc.otm.core.multiblock.GlobalBlockEntityRemovalListener;
|
||||||
|
|
||||||
// because i know
|
// because i know
|
||||||
// someone
|
// someone
|
||||||
@ -20,6 +20,6 @@ public abstract class BlockEntityMixin {
|
|||||||
remap = false
|
remap = false
|
||||||
)
|
)
|
||||||
public void setRemovedListener(CallbackInfo ci) {
|
public void setRemovedListener(CallbackInfo ci) {
|
||||||
MultiblockKt.onBlockEntityInvalidated((BlockEntity) (Object) this);
|
GlobalBlockEntityRemovalListener.Companion.onBlockEntityInvalidated((BlockEntity) (Object) this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import net.minecraft.core.HolderLookup
|
|||||||
import net.minecraft.nbt.CompoundTag
|
import net.minecraft.nbt.CompoundTag
|
||||||
import net.neoforged.neoforge.common.util.INBTSerializable
|
import net.neoforged.neoforge.common.util.INBTSerializable
|
||||||
import net.neoforged.neoforge.event.entity.living.LivingIncomingDamageEvent
|
import net.neoforged.neoforge.event.entity.living.LivingIncomingDamageEvent
|
||||||
import ru.dbotthepony.mc.otm.network.syncher.Syncher
|
import ru.dbotthepony.mc.otm.network.syncher.SynchableGroup
|
||||||
import ru.dbotthepony.kommons.util.getValue
|
import ru.dbotthepony.kommons.util.getValue
|
||||||
import ru.dbotthepony.kommons.util.setValue
|
import ru.dbotthepony.kommons.util.setValue
|
||||||
import ru.dbotthepony.mc.otm.capability.MatteryPlayer
|
import ru.dbotthepony.mc.otm.capability.MatteryPlayer
|
||||||
@ -12,7 +12,7 @@ import ru.dbotthepony.mc.otm.core.nbt.set
|
|||||||
|
|
||||||
abstract class AndroidFeature(val type: AndroidFeatureType<*>, val android: MatteryPlayer) : INBTSerializable<CompoundTag> {
|
abstract class AndroidFeature(val type: AndroidFeatureType<*>, val android: MatteryPlayer) : INBTSerializable<CompoundTag> {
|
||||||
val ply get() = android.ply
|
val ply get() = android.ply
|
||||||
val syncher = Syncher()
|
val syncher = SynchableGroup()
|
||||||
val syncherRemote = syncher.Remote()
|
val syncherRemote = syncher.Remote()
|
||||||
|
|
||||||
open var level by syncher.int(setter = setter@{ field, value ->
|
open var level by syncher.int(setter = setter@{ field, value ->
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package ru.dbotthepony.mc.otm.android
|
package ru.dbotthepony.mc.otm.android
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream
|
|
||||||
import net.minecraft.ChatFormatting
|
import net.minecraft.ChatFormatting
|
||||||
import net.minecraft.core.HolderLookup
|
import net.minecraft.core.HolderLookup
|
||||||
import net.minecraft.nbt.CompoundTag
|
import net.minecraft.nbt.CompoundTag
|
||||||
@ -10,7 +9,7 @@ import net.minecraft.world.entity.player.Player
|
|||||||
import net.neoforged.bus.api.Event
|
import net.neoforged.bus.api.Event
|
||||||
import net.neoforged.neoforge.common.NeoForge
|
import net.neoforged.neoforge.common.NeoForge
|
||||||
import net.neoforged.neoforge.common.util.INBTSerializable
|
import net.neoforged.neoforge.common.util.INBTSerializable
|
||||||
import ru.dbotthepony.mc.otm.network.syncher.Syncher
|
import ru.dbotthepony.mc.otm.network.syncher.SynchableGroup
|
||||||
import ru.dbotthepony.kommons.util.getValue
|
import ru.dbotthepony.kommons.util.getValue
|
||||||
import ru.dbotthepony.kommons.util.setValue
|
import ru.dbotthepony.kommons.util.setValue
|
||||||
import ru.dbotthepony.mc.otm.OverdriveThatMatters
|
import ru.dbotthepony.mc.otm.OverdriveThatMatters
|
||||||
@ -22,7 +21,6 @@ import ru.dbotthepony.mc.otm.core.registryName
|
|||||||
import ru.dbotthepony.mc.otm.core.nbt.set
|
import ru.dbotthepony.mc.otm.core.nbt.set
|
||||||
import ru.dbotthepony.mc.otm.milliTime
|
import ru.dbotthepony.mc.otm.milliTime
|
||||||
import ru.dbotthepony.mc.otm.triggers.AndroidResearchTrigger
|
import ru.dbotthepony.mc.otm.triggers.AndroidResearchTrigger
|
||||||
import java.io.InputStream
|
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
class AndroidResearch(val type: AndroidResearchType, val capability: MatteryPlayer) : INBTSerializable<CompoundTag> {
|
class AndroidResearch(val type: AndroidResearchType, val capability: MatteryPlayer) : INBTSerializable<CompoundTag> {
|
||||||
@ -48,7 +46,7 @@ class AndroidResearch(val type: AndroidResearchType, val capability: MatteryPlay
|
|||||||
|
|
||||||
val ply: Player get() = capability.ply
|
val ply: Player get() = capability.ply
|
||||||
|
|
||||||
val syncher = Syncher()
|
val syncher = SynchableGroup()
|
||||||
val syncherRemote = syncher.Remote()
|
val syncherRemote = syncher.Remote()
|
||||||
|
|
||||||
var isResearched by syncher.boolean()
|
var isResearched by syncher.boolean()
|
||||||
|
@ -53,7 +53,7 @@ import ru.dbotthepony.mc.otm.core.util.Savetables
|
|||||||
import ru.dbotthepony.mc.otm.core.util.TickList
|
import ru.dbotthepony.mc.otm.core.util.TickList
|
||||||
import ru.dbotthepony.mc.otm.core.util.countingLazy
|
import ru.dbotthepony.mc.otm.core.util.countingLazy
|
||||||
import ru.dbotthepony.mc.otm.network.BlockEntitySyncPacket
|
import ru.dbotthepony.mc.otm.network.BlockEntitySyncPacket
|
||||||
import ru.dbotthepony.mc.otm.network.syncher.Syncher
|
import ru.dbotthepony.mc.otm.network.syncher.SynchableGroup
|
||||||
import ru.dbotthepony.mc.otm.onceServer
|
import ru.dbotthepony.mc.otm.onceServer
|
||||||
import ru.dbotthepony.mc.otm.registry.MBlocks
|
import ru.dbotthepony.mc.otm.registry.MBlocks
|
||||||
import ru.dbotthepony.mc.otm.sometimeServer
|
import ru.dbotthepony.mc.otm.sometimeServer
|
||||||
@ -369,8 +369,8 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val syncher = Syncher()
|
val syncher = SynchableGroup()
|
||||||
private val synchers = Object2ObjectArrayMap<ServerPlayer, Syncher.Remote>()
|
private val synchers = Object2ObjectArrayMap<ServerPlayer, SynchableGroup.Remote>()
|
||||||
|
|
||||||
override fun setLevel(level: Level) {
|
override fun setLevel(level: Level) {
|
||||||
val old = this.level
|
val old = this.level
|
||||||
|
@ -3,12 +3,11 @@ package ru.dbotthepony.mc.otm.block.entity
|
|||||||
import net.minecraft.core.BlockPos
|
import net.minecraft.core.BlockPos
|
||||||
import net.minecraft.tags.BlockTags
|
import net.minecraft.tags.BlockTags
|
||||||
import net.minecraft.world.level.block.Blocks
|
import net.minecraft.world.level.block.Blocks
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity
|
|
||||||
import net.minecraft.world.level.block.entity.FurnaceBlockEntity
|
import net.minecraft.world.level.block.entity.FurnaceBlockEntity
|
||||||
import net.minecraft.world.level.block.entity.HopperBlockEntity
|
import net.minecraft.world.level.block.entity.HopperBlockEntity
|
||||||
import net.minecraft.world.level.block.state.BlockState
|
import net.minecraft.world.level.block.state.BlockState
|
||||||
import ru.dbotthepony.mc.otm.core.multiblockConfiguration
|
import ru.dbotthepony.mc.otm.core.multiblock.multiblockEntity
|
||||||
import ru.dbotthepony.mc.otm.core.multiblockEntity
|
import ru.dbotthepony.mc.otm.core.multiblock.shapedMultiblock
|
||||||
import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
||||||
import ru.dbotthepony.mc.otm.registry.MBlocks
|
import ru.dbotthepony.mc.otm.registry.MBlocks
|
||||||
|
|
||||||
@ -28,7 +27,7 @@ class MultiblockTestBlockEntity(blockPos: BlockPos, blockState: BlockState) : Ma
|
|||||||
private val HOPPERS = multiblockEntity<HopperBlockEntity>()
|
private val HOPPERS = multiblockEntity<HopperBlockEntity>()
|
||||||
private val FURNACES = multiblockEntity<FurnaceBlockEntity>()
|
private val FURNACES = multiblockEntity<FurnaceBlockEntity>()
|
||||||
|
|
||||||
val CONFIG = multiblockConfiguration {
|
val CONFIG = shapedMultiblock {
|
||||||
block(MBlocks.MULTIBLOCK_TEST)
|
block(MBlocks.MULTIBLOCK_TEST)
|
||||||
|
|
||||||
and {
|
and {
|
||||||
|
@ -3,7 +3,7 @@ package ru.dbotthepony.mc.otm.block.entity
|
|||||||
import net.minecraft.core.HolderLookup
|
import net.minecraft.core.HolderLookup
|
||||||
import net.minecraft.nbt.CompoundTag
|
import net.minecraft.nbt.CompoundTag
|
||||||
import net.neoforged.neoforge.common.util.INBTSerializable
|
import net.neoforged.neoforge.common.util.INBTSerializable
|
||||||
import ru.dbotthepony.mc.otm.network.syncher.Syncher
|
import ru.dbotthepony.mc.otm.network.syncher.SynchableGroup
|
||||||
import ru.dbotthepony.kommons.util.Listenable
|
import ru.dbotthepony.kommons.util.Listenable
|
||||||
import ru.dbotthepony.kommons.util.getValue
|
import ru.dbotthepony.kommons.util.getValue
|
||||||
import ru.dbotthepony.kommons.util.setValue
|
import ru.dbotthepony.kommons.util.setValue
|
||||||
@ -79,7 +79,7 @@ class RedstoneControl(private val valueChanges: (new: Boolean, old: Boolean) ->
|
|||||||
}
|
}
|
||||||
|
|
||||||
class SynchronizedRedstoneControl(
|
class SynchronizedRedstoneControl(
|
||||||
synchronizer: Syncher,
|
synchronizer: SynchableGroup,
|
||||||
private val valueChanges: (new: Boolean, old: Boolean) -> Unit,
|
private val valueChanges: (new: Boolean, old: Boolean) -> Unit,
|
||||||
) : AbstractRedstoneControl() {
|
) : AbstractRedstoneControl() {
|
||||||
override var redstoneSetting: RedstoneSetting by synchronizer.enum(RedstoneSetting.LOW, setter = { access, value ->
|
override var redstoneSetting: RedstoneSetting by synchronizer.enum(RedstoneSetting.LOW, setter = { access, value ->
|
||||||
|
@ -4,6 +4,7 @@ import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
|||||||
import net.minecraft.client.Minecraft
|
import net.minecraft.client.Minecraft
|
||||||
import net.minecraft.core.BlockPos
|
import net.minecraft.core.BlockPos
|
||||||
import net.minecraft.core.HolderLookup
|
import net.minecraft.core.HolderLookup
|
||||||
|
import net.minecraft.core.SectionPos
|
||||||
import net.minecraft.nbt.CompoundTag
|
import net.minecraft.nbt.CompoundTag
|
||||||
import net.minecraft.server.level.ServerLevel
|
import net.minecraft.server.level.ServerLevel
|
||||||
import net.minecraft.server.level.ServerPlayer
|
import net.minecraft.server.level.ServerPlayer
|
||||||
@ -26,6 +27,8 @@ import ru.dbotthepony.mc.otm.block.BlackHoleBlock
|
|||||||
import ru.dbotthepony.mc.otm.block.entity.tech.GravitationStabilizerBlockEntity
|
import ru.dbotthepony.mc.otm.block.entity.tech.GravitationStabilizerBlockEntity
|
||||||
import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity
|
import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity
|
||||||
import ru.dbotthepony.mc.otm.config.ServerConfig
|
import ru.dbotthepony.mc.otm.config.ServerConfig
|
||||||
|
import ru.dbotthepony.mc.otm.core.addAll
|
||||||
|
import ru.dbotthepony.mc.otm.core.collect.map
|
||||||
import ru.dbotthepony.mc.otm.core.damageType
|
import ru.dbotthepony.mc.otm.core.damageType
|
||||||
import ru.dbotthepony.mc.otm.core.getExplosionResistance
|
import ru.dbotthepony.mc.otm.core.getExplosionResistance
|
||||||
import ru.dbotthepony.mc.otm.core.gracefulBlockBreak
|
import ru.dbotthepony.mc.otm.core.gracefulBlockBreak
|
||||||
@ -182,6 +185,9 @@ class BlackHoleBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mattery
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val sphericalPositionsCache = ArrayList<BlockPos>()
|
||||||
|
private var lastRadii = -1
|
||||||
|
|
||||||
override fun tick() {
|
override fun tick() {
|
||||||
super.tick()
|
super.tick()
|
||||||
if (--sleepTicks > 0) return
|
if (--sleepTicks > 0) return
|
||||||
@ -245,7 +251,30 @@ class BlackHoleBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mattery
|
|||||||
var sphereIterator = sphereIterator
|
var sphereIterator = sphereIterator
|
||||||
|
|
||||||
if (sphereIterator == null || !sphereIterator.hasNext()) {
|
if (sphereIterator == null || !sphereIterator.hasNext()) {
|
||||||
sphereIterator = getSphericalBlockPositions((gravitationStrength * 18.0).roundToInt())
|
val radii = (gravitationStrength * 18.0).roundToInt()
|
||||||
|
|
||||||
|
if (lastRadii != radii) {
|
||||||
|
sphericalPositionsCache.clear()
|
||||||
|
lastRadii = radii
|
||||||
|
|
||||||
|
if (radii <= 24) {
|
||||||
|
sphericalPositionsCache.addAll(getSphericalBlockPositions(radii).map { it + blockPos })
|
||||||
|
|
||||||
|
// Sort to maximize chunk cache
|
||||||
|
sphericalPositionsCache.sortWith { o1, o2 ->
|
||||||
|
val cmp = SectionPos.blockToSectionCoord(o1.x).compareTo(SectionPos.blockToSectionCoord(o2.x))
|
||||||
|
if (cmp != 0) return@sortWith cmp
|
||||||
|
return@sortWith SectionPos.blockToSectionCoord(o1.z).compareTo(SectionPos.blockToSectionCoord(o2.z))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sphericalPositionsCache.isNotEmpty()) {
|
||||||
|
sphereIterator = sphericalPositionsCache.iterator()
|
||||||
|
} else {
|
||||||
|
sphereIterator = getSphericalBlockPositions(radii).map { it + blockPos }
|
||||||
|
}
|
||||||
|
|
||||||
this.sphereIterator = sphereIterator
|
this.sphereIterator = sphereIterator
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,7 +283,7 @@ class BlackHoleBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mattery
|
|||||||
while (iterations < ITERATIONS && sphereIterator.hasNext()) {
|
while (iterations < ITERATIONS && sphereIterator.hasNext()) {
|
||||||
iterations++
|
iterations++
|
||||||
|
|
||||||
val pos = sphereIterator.next() + blockPos
|
val pos = sphereIterator.next()
|
||||||
val getBlock = level.getBlockState(pos)
|
val getBlock = level.getBlockState(pos)
|
||||||
|
|
||||||
if (!getBlock.isAir && getBlock.block !is BlackHoleBlock) {
|
if (!getBlock.isAir && getBlock.block !is BlackHoleBlock) {
|
||||||
|
@ -2,29 +2,83 @@ package ru.dbotthepony.mc.otm.block.entity.blackhole
|
|||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectIterators
|
||||||
import net.minecraft.core.BlockPos
|
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.level.block.state.BlockState
|
import net.minecraft.world.level.block.state.BlockState
|
||||||
|
import net.neoforged.neoforge.capabilities.Capabilities
|
||||||
|
import ru.dbotthepony.kommons.util.DelegateSetter
|
||||||
|
import ru.dbotthepony.kommons.util.getValue
|
||||||
|
import ru.dbotthepony.kommons.util.setValue
|
||||||
|
import ru.dbotthepony.kommons.util.value
|
||||||
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
|
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
|
||||||
import ru.dbotthepony.mc.otm.block.entity.tech.EnergyHatchBlockEntity
|
import ru.dbotthepony.mc.otm.block.entity.tech.EnergyHatchBlockEntity
|
||||||
import ru.dbotthepony.mc.otm.block.entity.tech.MatterHatchBlockEntity
|
import ru.dbotthepony.mc.otm.block.entity.tech.MatterHatchBlockEntity
|
||||||
|
import ru.dbotthepony.mc.otm.capability.FlowDirection
|
||||||
|
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
||||||
|
import ru.dbotthepony.mc.otm.capability.energy.CombinedProfiledEnergyStorage
|
||||||
|
import ru.dbotthepony.mc.otm.capability.energy.receiveEnergy
|
||||||
|
import ru.dbotthepony.mc.otm.capability.matter.CombinedProfiledMatterStorage
|
||||||
import ru.dbotthepony.mc.otm.config.MachinesConfig
|
import ru.dbotthepony.mc.otm.config.MachinesConfig
|
||||||
import ru.dbotthepony.mc.otm.core.Multiblock
|
import ru.dbotthepony.mc.otm.core.RandomSource2Generator
|
||||||
import ru.dbotthepony.mc.otm.core.MultiblockBuilder
|
import ru.dbotthepony.mc.otm.core.collect.map
|
||||||
|
import ru.dbotthepony.mc.otm.core.multiblock.ShapedMultiblock
|
||||||
import ru.dbotthepony.mc.otm.core.getBlockStateNow
|
import ru.dbotthepony.mc.otm.core.getBlockStateNow
|
||||||
import ru.dbotthepony.mc.otm.core.immutableList
|
import ru.dbotthepony.mc.otm.core.immutableList
|
||||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
import ru.dbotthepony.mc.otm.core.math.plus
|
import ru.dbotthepony.mc.otm.core.math.plus
|
||||||
import ru.dbotthepony.mc.otm.core.math.times
|
import ru.dbotthepony.mc.otm.core.math.times
|
||||||
import ru.dbotthepony.mc.otm.core.multiblockConfiguration
|
import ru.dbotthepony.mc.otm.core.multiblock.BlockEntityTag
|
||||||
|
import ru.dbotthepony.mc.otm.core.multiblock.Strategy
|
||||||
|
import ru.dbotthepony.mc.otm.core.multiblock.shapedMultiblock
|
||||||
|
import ru.dbotthepony.mc.otm.menu.tech.BlackHoleGeneratorMenu
|
||||||
import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
||||||
|
import ru.dbotthepony.mc.otm.registry.MBlockTags
|
||||||
import ru.dbotthepony.mc.otm.registry.MBlocks
|
import ru.dbotthepony.mc.otm.registry.MBlocks
|
||||||
|
import java.io.Closeable
|
||||||
|
|
||||||
class BlackHoleGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryDeviceBlockEntity(MBlockEntities.BLACK_HOLE_GENERATOR, blockPos, blockState) {
|
class BlackHoleGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryDeviceBlockEntity(MBlockEntities.BLACK_HOLE_GENERATOR, blockPos, blockState) {
|
||||||
private var multiblock: Multiblock? = null
|
var multiblock: ShapedMultiblock? = null
|
||||||
private var lastRange = -1
|
private set
|
||||||
|
private var multiblockSyncher: Closeable? = null
|
||||||
|
|
||||||
|
var lastRange by syncher.int(-1, setter = DelegateSetter { field, value ->
|
||||||
|
if (value == -1) {
|
||||||
|
multiblockSyncher?.close()
|
||||||
|
multiblockSyncher = null
|
||||||
|
multiblock?.close()
|
||||||
|
multiblock = null
|
||||||
|
field.accept(value)
|
||||||
|
} else if (value != field.value) {
|
||||||
|
multiblockSyncher?.close()
|
||||||
|
multiblock = CONFIGURATIONS[value - 5].value.create(blockPos)
|
||||||
|
multiblockSyncher = syncher.add0(multiblock!!)
|
||||||
|
field.accept(value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
private set
|
||||||
|
|
||||||
|
var drawBuildingGuide by syncher.boolean()
|
||||||
|
|
||||||
|
val energy = CombinedProfiledEnergyStorage(
|
||||||
|
FlowDirection.NONE,
|
||||||
|
{ multiblock?.blockEntities(EnergyHatchBlockEntity.OUTPUT_TAG)?.iterator()?.map { it.energy } ?: ObjectIterators.emptyIterator() },
|
||||||
|
{ level?.random?.let { RandomSource2Generator(it) } }
|
||||||
|
)
|
||||||
|
|
||||||
|
val matter = CombinedProfiledMatterStorage(
|
||||||
|
FlowDirection.NONE,
|
||||||
|
{ multiblock?.blockEntities(MatterHatchBlockEntity.INPUT_TAG)?.iterator()?.map { it.matter } ?: ObjectIterators.emptyIterator() },
|
||||||
|
{ level?.random?.let { RandomSource2Generator(it) } }
|
||||||
|
)
|
||||||
|
|
||||||
|
init {
|
||||||
|
exposeSideless(MatteryCapability.MATTER_BLOCK, matter)
|
||||||
|
exposeSideless(MatteryCapability.BLOCK_ENERGY, energy)
|
||||||
|
exposeSideless(Capabilities.EnergyStorage.BLOCK, energy)
|
||||||
|
}
|
||||||
|
|
||||||
private fun findBlackHoleRange(): Int {
|
private fun findBlackHoleRange(): Int {
|
||||||
val normal = blockRotation.normal
|
val normal = blockRotation.normal
|
||||||
@ -52,69 +106,29 @@ class BlackHoleGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState)
|
|||||||
override fun tick() {
|
override fun tick() {
|
||||||
super.tick()
|
super.tick()
|
||||||
|
|
||||||
|
energy.tick()
|
||||||
|
matter.tick()
|
||||||
|
|
||||||
val level = level!!
|
val level = level!!
|
||||||
|
|
||||||
if (lastRange == -1) {
|
if (lastRange == -1) {
|
||||||
val found = findBlackHoleRange()
|
this.lastRange = findBlackHoleRange()
|
||||||
|
|
||||||
if (found != -1) {
|
|
||||||
lastRange = found
|
|
||||||
multiblock = CONFIGURATIONS[found - 5].value.create(blockPos)
|
|
||||||
} else {
|
|
||||||
multiblock = null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val multiblock = multiblock
|
val multiblock = multiblock
|
||||||
|
|
||||||
if (multiblock != null) {
|
if (multiblock != null) {
|
||||||
if (!multiblock.update(level, blockRotation.front)) {
|
if (!multiblock.update(level, blockRotation.front)) {
|
||||||
val found = findBlackHoleRange()
|
this.lastRange = findBlackHoleRange()
|
||||||
|
|
||||||
if (found == -1) {
|
|
||||||
this.multiblock = null
|
|
||||||
} else if (found != lastRange) {
|
|
||||||
lastRange = found
|
|
||||||
this.multiblock = CONFIGURATIONS[found - 5].value.create(blockPos)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
val blackHole = multiblock.blockEntities(BLACK_HOLE).first()
|
val blackHole = multiblock.blockEntities(BLACK_HOLE).first()
|
||||||
val matterHatches = multiblock.blockEntities(MatterHatchBlockEntity.INPUT_TAG)
|
|
||||||
val energyHatches = multiblock.blockEntities(EnergyHatchBlockEntity.OUTPUT_TAG)
|
|
||||||
|
|
||||||
if (matterHatches.isEmpty() || energyHatches.isEmpty()) return
|
if (matter.extractMatter(MachinesConfig.BlackHoleGenerator.MATTER_RATE, true) != MachinesConfig.BlackHoleGenerator.MATTER_RATE) return
|
||||||
|
val energyToStore = blackHole.mass / MachinesConfig.BlackHoleGenerator.MASS_DIVISOR
|
||||||
|
if (energy.receiveEnergy(energyToStore, true) != energyToStore) return
|
||||||
|
|
||||||
var required = MachinesConfig.BlackHoleGenerator.MATTER_RATE
|
matter.extractMatter(MachinesConfig.BlackHoleGenerator.MATTER_RATE, false)
|
||||||
|
energy.receiveEnergy(energyToStore, false)
|
||||||
for (hatch in matterHatches) {
|
|
||||||
required -= hatch.matter.extractMatter(required, true)
|
|
||||||
if (required <= Decimal.ZERO) break
|
|
||||||
}
|
|
||||||
|
|
||||||
if (required > Decimal.ZERO) return
|
|
||||||
|
|
||||||
var energyLeftover = blackHole.mass / MachinesConfig.BlackHoleGenerator.MASS_DIVISOR
|
|
||||||
|
|
||||||
for (hatch in energyHatches) {
|
|
||||||
energyLeftover -= hatch.energy.receiveEnergy(energyLeftover, true)
|
|
||||||
if (energyLeftover <= Decimal.ZERO) break
|
|
||||||
}
|
|
||||||
|
|
||||||
if (energyLeftover > Decimal.ZERO) return
|
|
||||||
|
|
||||||
required = MachinesConfig.BlackHoleGenerator.MATTER_RATE
|
|
||||||
|
|
||||||
for (hatch in matterHatches) {
|
|
||||||
required -= hatch.matter.extractMatter(required, false)
|
|
||||||
if (required <= Decimal.ZERO) break
|
|
||||||
}
|
|
||||||
|
|
||||||
energyLeftover = blackHole.mass / MachinesConfig.BlackHoleGenerator.MASS_DIVISOR
|
|
||||||
|
|
||||||
for (hatch in energyHatches) {
|
|
||||||
energyLeftover -= hatch.energy.receiveEnergy(energyLeftover, false)
|
|
||||||
if (energyLeftover <= Decimal.ZERO) break
|
|
||||||
}
|
|
||||||
|
|
||||||
blackHole.mass += MachinesConfig.BlackHoleGenerator.MASS_FEEDING_RATIO * MachinesConfig.BlackHoleGenerator.MATTER_RATE
|
blackHole.mass += MachinesConfig.BlackHoleGenerator.MASS_FEEDING_RATIO * MachinesConfig.BlackHoleGenerator.MATTER_RATE
|
||||||
}
|
}
|
||||||
@ -122,11 +136,11 @@ class BlackHoleGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState)
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu? {
|
override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu? {
|
||||||
return null
|
return BlackHoleGeneratorMenu(containerID, inventory, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val BLACK_HOLE = MultiblockBuilder.EntityTag(BlackHoleBlockEntity::class)
|
val BLACK_HOLE = BlockEntityTag(BlackHoleBlockEntity::class)
|
||||||
|
|
||||||
private fun points(x: Int, z: Int, result: ObjectArraySet<BlockPos>) {
|
private fun points(x: Int, z: Int, result: ObjectArraySet<BlockPos>) {
|
||||||
if (x == 0) {
|
if (x == 0) {
|
||||||
@ -187,7 +201,7 @@ class BlackHoleGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState)
|
|||||||
val i = i2 + 5
|
val i = i2 + 5
|
||||||
|
|
||||||
accept(lazy {
|
accept(lazy {
|
||||||
multiblockConfiguration {
|
shapedMultiblock {
|
||||||
builder.customCheck {
|
builder.customCheck {
|
||||||
val blocks = it.blocks()
|
val blocks = it.blocks()
|
||||||
|
|
||||||
@ -226,9 +240,9 @@ class BlackHoleGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState)
|
|||||||
tag(MatterHatchBlockEntity.INPUT_TAG)
|
tag(MatterHatchBlockEntity.INPUT_TAG)
|
||||||
}
|
}
|
||||||
|
|
||||||
node.block(MBlocks.MULTIBLOCK_STRUCTURE)
|
node.block(MBlockTags.MULTIBLOCK_STRUCTURE)
|
||||||
} else {
|
} else {
|
||||||
node.block(MBlocks.MULTIBLOCK_STRUCTURE)
|
node.block(MBlockTags.MULTIBLOCK_HARD_STRUCTURE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
|
|||||||
import ru.dbotthepony.mc.otm.capability.*
|
import ru.dbotthepony.mc.otm.capability.*
|
||||||
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
|
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
|
||||||
import ru.dbotthepony.mc.otm.core.*
|
import ru.dbotthepony.mc.otm.core.*
|
||||||
|
import ru.dbotthepony.mc.otm.core.chart.DecimalHistoryChart
|
||||||
import ru.dbotthepony.mc.otm.core.math.BlockRotation
|
import ru.dbotthepony.mc.otm.core.math.BlockRotation
|
||||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
import ru.dbotthepony.mc.otm.core.math.RelativeSide
|
import ru.dbotthepony.mc.otm.core.math.RelativeSide
|
||||||
|
@ -17,7 +17,7 @@ import ru.dbotthepony.mc.otm.config.EnergyBalanceValues
|
|||||||
import ru.dbotthepony.mc.otm.config.MachinesConfig
|
import ru.dbotthepony.mc.otm.config.MachinesConfig
|
||||||
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.core.MultiblockBuilder
|
import ru.dbotthepony.mc.otm.core.multiblock.BlockEntityTag
|
||||||
import ru.dbotthepony.mc.otm.menu.tech.EnergyHatchMenu
|
import ru.dbotthepony.mc.otm.menu.tech.EnergyHatchMenu
|
||||||
import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
||||||
|
|
||||||
@ -70,8 +70,8 @@ class EnergyHatchBlockEntity(
|
|||||||
companion object {
|
companion object {
|
||||||
const val CAPACITY = 1
|
const val CAPACITY = 1
|
||||||
|
|
||||||
val INPUT_TAG = MultiblockBuilder.EntityTag(EnergyHatchBlockEntity::class) { it.isInput }
|
val INPUT_TAG = BlockEntityTag(EnergyHatchBlockEntity::class) { it.isInput }
|
||||||
val OUTPUT_TAG = MultiblockBuilder.EntityTag(EnergyHatchBlockEntity::class) { !it.isInput }
|
val OUTPUT_TAG = BlockEntityTag(EnergyHatchBlockEntity::class) { !it.isInput }
|
||||||
|
|
||||||
fun input(blockPos: BlockPos, blockState: BlockState): EnergyHatchBlockEntity {
|
fun input(blockPos: BlockPos, blockState: BlockState): EnergyHatchBlockEntity {
|
||||||
return EnergyHatchBlockEntity(true, MachinesConfig.ENERGY_HATCH, MBlockEntities.ENERGY_INPUT_HATCH, blockPos, blockState)
|
return EnergyHatchBlockEntity(true, MachinesConfig.ENERGY_HATCH, MBlockEntities.ENERGY_INPUT_HATCH, blockPos, blockState)
|
||||||
|
@ -11,7 +11,7 @@ import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
|
|||||||
import ru.dbotthepony.mc.otm.block.entity.decorative.CargoCrateBlockEntity
|
import ru.dbotthepony.mc.otm.block.entity.decorative.CargoCrateBlockEntity
|
||||||
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.core.MultiblockBuilder
|
import ru.dbotthepony.mc.otm.core.multiblock.BlockEntityTag
|
||||||
import ru.dbotthepony.mc.otm.menu.tech.ItemHatchMenu
|
import ru.dbotthepony.mc.otm.menu.tech.ItemHatchMenu
|
||||||
import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
||||||
|
|
||||||
@ -36,8 +36,8 @@ class ItemHatchBlockEntity(
|
|||||||
companion object {
|
companion object {
|
||||||
const val CAPACITY = CargoCrateBlockEntity.CAPACITY
|
const val CAPACITY = CargoCrateBlockEntity.CAPACITY
|
||||||
|
|
||||||
val INPUT_TAG = MultiblockBuilder.EntityTag(ItemHatchBlockEntity::class) { it.isInput }
|
val INPUT_TAG = BlockEntityTag(ItemHatchBlockEntity::class) { it.isInput }
|
||||||
val OUTPUT_TAG = MultiblockBuilder.EntityTag(ItemHatchBlockEntity::class) { !it.isInput }
|
val OUTPUT_TAG = BlockEntityTag(ItemHatchBlockEntity::class) { !it.isInput }
|
||||||
|
|
||||||
fun input(blockPos: BlockPos, blockState: BlockState): ItemHatchBlockEntity {
|
fun input(blockPos: BlockPos, blockState: BlockState): ItemHatchBlockEntity {
|
||||||
return ItemHatchBlockEntity(true, MBlockEntities.ITEM_INPUT_HATCH, blockPos, blockState)
|
return ItemHatchBlockEntity(true, MBlockEntities.ITEM_INPUT_HATCH, blockPos, blockState)
|
||||||
|
@ -17,8 +17,8 @@ import ru.dbotthepony.mc.otm.capability.moveMatter
|
|||||||
import ru.dbotthepony.mc.otm.config.MachinesConfig
|
import ru.dbotthepony.mc.otm.config.MachinesConfig
|
||||||
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.core.MultiblockBuilder
|
|
||||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
|
import ru.dbotthepony.mc.otm.core.multiblock.BlockEntityTag
|
||||||
import ru.dbotthepony.mc.otm.menu.tech.MatterHatchMenu
|
import ru.dbotthepony.mc.otm.menu.tech.MatterHatchMenu
|
||||||
import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
||||||
|
|
||||||
@ -74,8 +74,8 @@ class MatterHatchBlockEntity(
|
|||||||
companion object {
|
companion object {
|
||||||
const val CAPACITY = 1
|
const val CAPACITY = 1
|
||||||
|
|
||||||
val INPUT_TAG = MultiblockBuilder.EntityTag(MatterHatchBlockEntity::class) { it.isInput }
|
val INPUT_TAG = BlockEntityTag(MatterHatchBlockEntity::class) { it.isInput }
|
||||||
val OUTPUT_TAG = MultiblockBuilder.EntityTag(MatterHatchBlockEntity::class) { !it.isInput }
|
val OUTPUT_TAG = BlockEntityTag(MatterHatchBlockEntity::class) { !it.isInput }
|
||||||
|
|
||||||
fun input(blockPos: BlockPos, blockState: BlockState): MatterHatchBlockEntity {
|
fun input(blockPos: BlockPos, blockState: BlockState): MatterHatchBlockEntity {
|
||||||
return MatterHatchBlockEntity(true, MBlockEntities.MATTER_INPUT_HATCH, blockPos, blockState)
|
return MatterHatchBlockEntity(true, MBlockEntities.MATTER_INPUT_HATCH, blockPos, blockState)
|
||||||
|
@ -4,18 +4,18 @@ import it.unimi.dsi.fastutil.objects.ObjectArrayList
|
|||||||
import net.minecraft.core.HolderLookup
|
import net.minecraft.core.HolderLookup
|
||||||
import net.minecraft.nbt.CompoundTag
|
import net.minecraft.nbt.CompoundTag
|
||||||
import net.neoforged.neoforge.common.util.INBTSerializable
|
import net.neoforged.neoforge.common.util.INBTSerializable
|
||||||
import ru.dbotthepony.mc.otm.core.DecimalHistoryChart
|
import ru.dbotthepony.mc.otm.core.chart.DecimalHistoryChart
|
||||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
import ru.dbotthepony.mc.otm.core.nbt.map
|
import ru.dbotthepony.mc.otm.core.nbt.map
|
||||||
import ru.dbotthepony.mc.otm.core.nbt.set
|
import ru.dbotthepony.mc.otm.core.nbt.set
|
||||||
|
|
||||||
abstract class AbstractProfiledStorage<out P>(val parent: P) : INBTSerializable<CompoundTag?> {
|
abstract class AbstractProfiledStorage<out P>(val parent: P) : IProfiledStorage, INBTSerializable<CompoundTag?> {
|
||||||
val received = DecimalHistoryChart(ticks = HISTORY_SIZE)
|
final override val received = DecimalHistoryChart(ticks = HISTORY_SIZE)
|
||||||
val transferred = DecimalHistoryChart(ticks = HISTORY_SIZE)
|
final override val transferred = DecimalHistoryChart(ticks = HISTORY_SIZE)
|
||||||
|
|
||||||
var receivedThisTick = Decimal.ZERO
|
final override var receivedThisTick = Decimal.ZERO
|
||||||
private set
|
private set
|
||||||
var transferredThisTick = Decimal.ZERO
|
final override var transferredThisTick = Decimal.ZERO
|
||||||
private set
|
private set
|
||||||
|
|
||||||
private var isTicking = false
|
private var isTicking = false
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package ru.dbotthepony.mc.otm.capability
|
package ru.dbotthepony.mc.otm.capability
|
||||||
|
|
||||||
import com.google.common.collect.Streams
|
import com.google.common.collect.Streams
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList
|
||||||
import net.minecraft.ChatFormatting
|
import net.minecraft.ChatFormatting
|
||||||
import net.minecraft.network.chat.Component
|
import net.minecraft.network.chat.Component
|
||||||
import net.minecraft.world.entity.LivingEntity
|
import net.minecraft.world.entity.LivingEntity
|
||||||
@ -33,7 +34,10 @@ import ru.dbotthepony.mc.otm.core.collect.filter
|
|||||||
import ru.dbotthepony.mc.otm.core.collect.map
|
import ru.dbotthepony.mc.otm.core.collect.map
|
||||||
import ru.dbotthepony.mc.otm.core.isNotEmpty
|
import ru.dbotthepony.mc.otm.core.isNotEmpty
|
||||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
|
import ru.dbotthepony.mc.otm.core.shuffle
|
||||||
import ru.dbotthepony.mc.otm.core.util.formatFluidLevel
|
import ru.dbotthepony.mc.otm.core.util.formatFluidLevel
|
||||||
|
import java.util.function.Supplier
|
||||||
|
import java.util.random.RandomGenerator
|
||||||
import java.util.stream.Stream
|
import java.util.stream.Stream
|
||||||
|
|
||||||
private val LOGGER = LogManager.getLogger()
|
private val LOGGER = LogManager.getLogger()
|
||||||
@ -41,6 +45,37 @@ private val LOGGER = LogManager.getLogger()
|
|||||||
val Player.matteryPlayer: MatteryPlayer get() = (this as IMatteryPlayer).otmPlayer
|
val Player.matteryPlayer: MatteryPlayer get() = (this as IMatteryPlayer).otmPlayer
|
||||||
val LivingEntity.matteryPlayer: MatteryPlayer? get() = (this as? IMatteryPlayer)?.otmPlayer
|
val LivingEntity.matteryPlayer: MatteryPlayer? get() = (this as? IMatteryPlayer)?.otmPlayer
|
||||||
|
|
||||||
|
fun <T> Supplier<out Iterator<T>>.iterateProviders(random: RandomGenerator?, value: Decimal, simulate: Boolean, walker: T.(Decimal, Boolean) -> Decimal): Decimal {
|
||||||
|
val providers = get()
|
||||||
|
val iteratorProvider: () -> Iterator<T>
|
||||||
|
|
||||||
|
if (random == null) {
|
||||||
|
iteratorProvider = providers::iterator
|
||||||
|
} else {
|
||||||
|
val providerList = ObjectArrayList(providers)
|
||||||
|
providerList.shuffle(random)
|
||||||
|
iteratorProvider = providerList::iterator
|
||||||
|
}
|
||||||
|
|
||||||
|
var leftover = value
|
||||||
|
|
||||||
|
for (provider in iteratorProvider()) {
|
||||||
|
leftover -= walker(provider, leftover, true)
|
||||||
|
if (leftover.signum() <= 0) break
|
||||||
|
}
|
||||||
|
|
||||||
|
if (simulate) return value - leftover
|
||||||
|
|
||||||
|
leftover = value
|
||||||
|
|
||||||
|
for (provider in iteratorProvider()) {
|
||||||
|
leftover -= walker(provider, leftover, false)
|
||||||
|
if (leftover.signum() <= 0) break
|
||||||
|
}
|
||||||
|
|
||||||
|
return value - leftover
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Does a checked energy receive, calls [IMatteryEnergyStorage.receiveEnergyChecked] if possible
|
* Does a checked energy receive, calls [IMatteryEnergyStorage.receiveEnergyChecked] if possible
|
||||||
*/
|
*/
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.capability
|
||||||
|
|
||||||
|
import ru.dbotthepony.mc.otm.core.chart.CombinedDecimalHistoryChart
|
||||||
|
import ru.dbotthepony.mc.otm.core.chart.IHistoryChart
|
||||||
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
|
|
||||||
|
interface IProfiledStorage {
|
||||||
|
val received: IHistoryChart<Decimal>
|
||||||
|
val transferred: IHistoryChart<Decimal>
|
||||||
|
|
||||||
|
val receivedThisTick: Decimal
|
||||||
|
val transferredThisTick: Decimal
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Just a middleware interface for [CombinedDecimalHistoryChart] carriers
|
||||||
|
*/
|
||||||
|
interface Combined : IProfiledStorage {
|
||||||
|
override val received: CombinedDecimalHistoryChart
|
||||||
|
override val transferred: CombinedDecimalHistoryChart
|
||||||
|
}
|
||||||
|
}
|
@ -13,10 +13,8 @@ import net.minecraft.commands.arguments.EntityArgument
|
|||||||
import net.minecraft.core.HolderLookup
|
import net.minecraft.core.HolderLookup
|
||||||
import net.minecraft.core.registries.BuiltInRegistries
|
import net.minecraft.core.registries.BuiltInRegistries
|
||||||
import net.minecraft.nbt.CompoundTag
|
import net.minecraft.nbt.CompoundTag
|
||||||
import net.minecraft.nbt.IntTag
|
|
||||||
import net.minecraft.nbt.ListTag
|
import net.minecraft.nbt.ListTag
|
||||||
import net.minecraft.nbt.StringTag
|
import net.minecraft.nbt.StringTag
|
||||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
|
||||||
import net.minecraft.network.chat.Component
|
import net.minecraft.network.chat.Component
|
||||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload
|
import net.minecraft.network.protocol.common.custom.CustomPacketPayload
|
||||||
import net.minecraft.resources.ResourceLocation
|
import net.minecraft.resources.ResourceLocation
|
||||||
@ -59,7 +57,7 @@ import org.apache.logging.log4j.LogManager
|
|||||||
import org.joml.Vector4f
|
import org.joml.Vector4f
|
||||||
import ru.dbotthepony.kommons.collect.ListenableMap
|
import ru.dbotthepony.kommons.collect.ListenableMap
|
||||||
import ru.dbotthepony.kommons.collect.ListenableSet
|
import ru.dbotthepony.kommons.collect.ListenableSet
|
||||||
import ru.dbotthepony.mc.otm.network.syncher.Syncher
|
import ru.dbotthepony.mc.otm.network.syncher.SynchableGroup
|
||||||
import ru.dbotthepony.kommons.util.ListenableDelegate
|
import ru.dbotthepony.kommons.util.ListenableDelegate
|
||||||
import ru.dbotthepony.kommons.util.getValue
|
import ru.dbotthepony.kommons.util.getValue
|
||||||
import ru.dbotthepony.kommons.util.setValue
|
import ru.dbotthepony.kommons.util.setValue
|
||||||
@ -80,7 +78,6 @@ import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
|
|||||||
import ru.dbotthepony.mc.otm.capability.energy.extractEnergyExact
|
import ru.dbotthepony.mc.otm.capability.energy.extractEnergyExact
|
||||||
import ru.dbotthepony.mc.otm.capability.energy.receiveEnergyExact
|
import ru.dbotthepony.mc.otm.capability.energy.receiveEnergyExact
|
||||||
import ru.dbotthepony.mc.otm.client.minecraft
|
import ru.dbotthepony.mc.otm.client.minecraft
|
||||||
import ru.dbotthepony.mc.otm.client.onceClient
|
|
||||||
import ru.dbotthepony.mc.otm.config.AndroidConfig
|
import ru.dbotthepony.mc.otm.config.AndroidConfig
|
||||||
import ru.dbotthepony.mc.otm.config.ExopackConfig
|
import ru.dbotthepony.mc.otm.config.ExopackConfig
|
||||||
import ru.dbotthepony.mc.otm.container.CombinedContainer
|
import ru.dbotthepony.mc.otm.container.CombinedContainer
|
||||||
@ -184,7 +181,7 @@ class MatteryPlayer(val ply: Player) {
|
|||||||
* any differences in field order/types/etc between client and server
|
* any differences in field order/types/etc between client and server
|
||||||
* will break *everything*
|
* will break *everything*
|
||||||
*/
|
*/
|
||||||
val syncher = Syncher()
|
val syncher = SynchableGroup()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remote for "this" player
|
* Remote for "this" player
|
||||||
@ -198,10 +195,10 @@ class MatteryPlayer(val ply: Player) {
|
|||||||
* any differences in field order/types/etc between client and server
|
* any differences in field order/types/etc between client and server
|
||||||
* will break *everything*
|
* will break *everything*
|
||||||
*/
|
*/
|
||||||
val publicSyncher = Syncher()
|
val publicSyncher = SynchableGroup()
|
||||||
|
|
||||||
val publicSyncherRemote = publicSyncher.Remote()
|
val publicSyncherRemote = publicSyncher.Remote()
|
||||||
val remoteSynchers = Object2ObjectArrayMap<ServerPlayer, Syncher.Remote>()
|
val remoteSynchers = Object2ObjectArrayMap<ServerPlayer, SynchableGroup.Remote>()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For data to be stored to and loaded from NBT automatically
|
* For data to be stored to and loaded from NBT automatically
|
||||||
|
@ -8,7 +8,7 @@ import net.minecraft.world.item.ItemStack
|
|||||||
import net.minecraft.world.ticks.ContainerSingleItem
|
import net.minecraft.world.ticks.ContainerSingleItem
|
||||||
import net.neoforged.neoforge.capabilities.Capabilities
|
import net.neoforged.neoforge.capabilities.Capabilities
|
||||||
import net.neoforged.neoforge.common.util.INBTSerializable
|
import net.neoforged.neoforge.common.util.INBTSerializable
|
||||||
import ru.dbotthepony.mc.otm.network.syncher.Syncher
|
import ru.dbotthepony.mc.otm.network.syncher.SynchableGroup
|
||||||
import ru.dbotthepony.kommons.util.getValue
|
import ru.dbotthepony.kommons.util.getValue
|
||||||
import ru.dbotthepony.kommons.util.setValue
|
import ru.dbotthepony.kommons.util.setValue
|
||||||
import ru.dbotthepony.mc.otm.capability.FlowDirection
|
import ru.dbotthepony.mc.otm.capability.FlowDirection
|
||||||
@ -23,7 +23,7 @@ import ru.dbotthepony.mc.otm.triggers.ExopackBatterySlotTrigger
|
|||||||
|
|
||||||
class BatteryBackedEnergyStorage(
|
class BatteryBackedEnergyStorage(
|
||||||
private val ply: Player,
|
private val ply: Player,
|
||||||
synchronizer: Syncher,
|
synchronizer: SynchableGroup,
|
||||||
initialCharge: Decimal,
|
initialCharge: Decimal,
|
||||||
maxCharge: Decimal,
|
maxCharge: Decimal,
|
||||||
val isAndroid: Boolean,
|
val isAndroid: Boolean,
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.capability.energy
|
||||||
|
|
||||||
|
import ru.dbotthepony.mc.otm.capability.FlowDirection
|
||||||
|
import ru.dbotthepony.mc.otm.capability.iterateProviders
|
||||||
|
import ru.dbotthepony.mc.otm.core.collect.map
|
||||||
|
import ru.dbotthepony.mc.otm.core.collect.reduce
|
||||||
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
|
import java.util.function.Supplier
|
||||||
|
import java.util.random.RandomGenerator
|
||||||
|
|
||||||
|
open class CombinedEnergyStorage(
|
||||||
|
final override val energyFlow: FlowDirection,
|
||||||
|
val provider: Supplier<out Iterator<IMatteryEnergyStorage>>,
|
||||||
|
val random: Supplier<RandomGenerator?> = Supplier { null }
|
||||||
|
) : IMatteryEnergyStorage {
|
||||||
|
final override fun extractEnergy(howMuch: Decimal, simulate: Boolean): Decimal {
|
||||||
|
return provider.iterateProviders(random.get(), howMuch, simulate, IMatteryEnergyStorage::extractEnergy)
|
||||||
|
}
|
||||||
|
|
||||||
|
final override fun receiveEnergy(howMuch: Decimal, simulate: Boolean): Decimal {
|
||||||
|
return provider.iterateProviders(random.get(), howMuch, simulate, IMatteryEnergyStorage::receiveEnergy)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
final override fun extractEnergyChecked(howMuch: Decimal, simulate: Boolean): Decimal {
|
||||||
|
return provider.iterateProviders(random.get(), howMuch, simulate, IMatteryEnergyStorage::extractEnergyChecked)
|
||||||
|
}
|
||||||
|
|
||||||
|
final override fun receiveEnergyChecked(howMuch: Decimal, simulate: Boolean): Decimal {
|
||||||
|
return provider.iterateProviders(random.get(), howMuch, simulate, IMatteryEnergyStorage::receiveEnergyChecked)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
final override var batteryLevel: Decimal
|
||||||
|
get() = provider.get().iterator().map { it.batteryLevel }.reduce(Decimal.ZERO, Decimal::plus)
|
||||||
|
set(value) { throw UnsupportedOperationException() }
|
||||||
|
final override val maxBatteryLevel: Decimal
|
||||||
|
get() = provider.get().iterator().map { it.maxBatteryLevel }.reduce(Decimal.ZERO, Decimal::plus)
|
||||||
|
|
||||||
|
final override val canSetBatteryLevel: Boolean
|
||||||
|
get() = false
|
||||||
|
final override val missingPower: Decimal
|
||||||
|
get() = provider.get().iterator().map { it.missingPower }.reduce(Decimal.ZERO, Decimal::plus)
|
||||||
|
|
||||||
|
final override fun drainBattery(): Boolean {
|
||||||
|
var any = false
|
||||||
|
provider.get().forEach { any = it.drainBattery() || any }
|
||||||
|
return any
|
||||||
|
}
|
||||||
|
|
||||||
|
final override fun fillBattery(): Boolean {
|
||||||
|
var any = false
|
||||||
|
provider.get().forEach { any = it.fillBattery() || any }
|
||||||
|
return any
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.capability.energy
|
||||||
|
|
||||||
|
import ru.dbotthepony.mc.otm.capability.AbstractProfiledStorage
|
||||||
|
import ru.dbotthepony.mc.otm.capability.FlowDirection
|
||||||
|
import ru.dbotthepony.mc.otm.capability.IProfiledStorage
|
||||||
|
import ru.dbotthepony.mc.otm.core.chart.CombinedDecimalHistoryChart
|
||||||
|
import ru.dbotthepony.mc.otm.core.collect.map
|
||||||
|
import ru.dbotthepony.mc.otm.core.collect.reduce
|
||||||
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
|
import ru.dbotthepony.mc.otm.core.util.ITickable
|
||||||
|
import java.util.function.Supplier
|
||||||
|
import java.util.random.RandomGenerator
|
||||||
|
|
||||||
|
class CombinedProfiledEnergyStorage(
|
||||||
|
energyFlow: FlowDirection,
|
||||||
|
provider: Supplier<out Iterator<ProfiledEnergyStorage<*>>>,
|
||||||
|
random: Supplier<RandomGenerator?> = Supplier { null }
|
||||||
|
) : CombinedEnergyStorage(energyFlow, provider, random), IProfiledMatteryEnergyStorage, IProfiledStorage.Combined, ITickable {
|
||||||
|
override val received = CombinedDecimalHistoryChart(Supplier { this.provider.get().map { (it as ProfiledEnergyStorage<*>).received } }, ticks = AbstractProfiledStorage.HISTORY_SIZE)
|
||||||
|
override val transferred = CombinedDecimalHistoryChart(Supplier { this.provider.get().map { (it as ProfiledEnergyStorage<*>).transferred } }, ticks = AbstractProfiledStorage.HISTORY_SIZE)
|
||||||
|
override val receivedThisTick: Decimal
|
||||||
|
get() = provider.get().map { (it as ProfiledEnergyStorage<*>).receivedThisTick }.reduce(Decimal.ZERO, Decimal::plus)
|
||||||
|
override val transferredThisTick: Decimal
|
||||||
|
get() = provider.get().map { (it as ProfiledEnergyStorage<*>).transferredThisTick }.reduce(Decimal.ZERO, Decimal::plus)
|
||||||
|
|
||||||
|
override fun tick() {
|
||||||
|
received.tick()
|
||||||
|
transferred.tick()
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@ import net.neoforged.neoforge.energy.IEnergyStorage
|
|||||||
import ru.dbotthepony.mc.otm.capability.FlowDirection
|
import ru.dbotthepony.mc.otm.capability.FlowDirection
|
||||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
import ru.dbotthepony.kommons.math.RGBAColor
|
import ru.dbotthepony.kommons.math.RGBAColor
|
||||||
|
import ru.dbotthepony.mc.otm.capability.IProfiledStorage
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
@ -72,7 +73,7 @@ interface IMatteryEnergyStorage : IEnergyStorage {
|
|||||||
* * Returned value CAN NOT be bigger than requested [howMuch], implementations failing to obey this contract will cause undefined behavior in upstream code.
|
* * Returned value CAN NOT be bigger than requested [howMuch], implementations failing to obey this contract will cause undefined behavior in upstream code.
|
||||||
* Upstream code SHOULD NOT check for this contract.
|
* Upstream code SHOULD NOT check for this contract.
|
||||||
*
|
*
|
||||||
* @return energy extracted
|
* @return energy received
|
||||||
*/
|
*/
|
||||||
fun receiveEnergy(howMuch: Decimal, simulate: Boolean): Decimal
|
fun receiveEnergy(howMuch: Decimal, simulate: Boolean): Decimal
|
||||||
|
|
||||||
@ -81,7 +82,7 @@ interface IMatteryEnergyStorage : IEnergyStorage {
|
|||||||
*
|
*
|
||||||
* Interface implementations generally do not need to override this.
|
* Interface implementations generally do not need to override this.
|
||||||
*
|
*
|
||||||
* @return energy extracted
|
* @return energy received
|
||||||
*/
|
*/
|
||||||
fun receiveEnergyChecked(howMuch: Decimal, simulate: Boolean): Decimal {
|
fun receiveEnergyChecked(howMuch: Decimal, simulate: Boolean): Decimal {
|
||||||
if (howMuch.isNegative || !energyFlow.input)
|
if (howMuch.isNegative || !energyFlow.input)
|
||||||
@ -122,12 +123,16 @@ interface IMatteryEnergyStorage : IEnergyStorage {
|
|||||||
* Empties power of this energy storage
|
* Empties power of this energy storage
|
||||||
*
|
*
|
||||||
* @see batteryLevel
|
* @see batteryLevel
|
||||||
* @return whenever operation was successful
|
* @return whenever operation was successful, including partial completion
|
||||||
*/
|
*/
|
||||||
fun drainBattery(): Boolean {
|
fun drainBattery(): Boolean {
|
||||||
try {
|
try {
|
||||||
batteryLevel = Decimal.ZERO
|
if (canSetBatteryLevel)
|
||||||
|
batteryLevel = Decimal.ZERO
|
||||||
|
else
|
||||||
|
return false
|
||||||
} catch(err: UnsupportedOperationException) {
|
} catch(err: UnsupportedOperationException) {
|
||||||
|
// anti-stupid
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,12 +143,16 @@ interface IMatteryEnergyStorage : IEnergyStorage {
|
|||||||
* Fully fills power in this energy storage
|
* Fully fills power in this energy storage
|
||||||
*
|
*
|
||||||
* @see batteryLevel
|
* @see batteryLevel
|
||||||
* @return whenever operation was successful
|
* @return whenever operation was successful, including partial completion
|
||||||
*/
|
*/
|
||||||
fun fillBattery(): Boolean {
|
fun fillBattery(): Boolean {
|
||||||
try {
|
try {
|
||||||
batteryLevel = maxBatteryLevel
|
if (canSetBatteryLevel)
|
||||||
|
batteryLevel = maxBatteryLevel
|
||||||
|
else
|
||||||
|
return false
|
||||||
} catch(err: UnsupportedOperationException) {
|
} catch(err: UnsupportedOperationException) {
|
||||||
|
// anti-stupid
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -374,3 +383,5 @@ fun IMatteryEnergyStorage.getBarColor(): Int {
|
|||||||
|
|
||||||
return RGBAColor.LOW_POWER.linearInterpolation((batteryLevel / maxBatteryLevel).toFloat(), RGBAColor.FULL_POWER).toBGR()
|
return RGBAColor.LOW_POWER.linearInterpolation((batteryLevel / maxBatteryLevel).toFloat(), RGBAColor.FULL_POWER).toBGR()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface IProfiledMatteryEnergyStorage : IProfiledStorage, IMatteryEnergyStorage
|
||||||
|
@ -4,7 +4,7 @@ import ru.dbotthepony.mc.otm.capability.AbstractProfiledStorage
|
|||||||
import ru.dbotthepony.mc.otm.capability.FlowDirection
|
import ru.dbotthepony.mc.otm.capability.FlowDirection
|
||||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
|
|
||||||
class ProfiledEnergyStorage<out E : IMatteryEnergyStorage>(parent: E) : AbstractProfiledStorage<E>(parent), IMatteryEnergyStorage {
|
class ProfiledEnergyStorage<out E : IMatteryEnergyStorage>(parent: E) : AbstractProfiledStorage<E>(parent), IProfiledMatteryEnergyStorage {
|
||||||
override fun extractEnergy(howMuch: Decimal, simulate: Boolean): Decimal {
|
override fun extractEnergy(howMuch: Decimal, simulate: Boolean): Decimal {
|
||||||
return recordTransfer(parent.extractEnergy(howMuch, simulate), simulate)
|
return recordTransfer(parent.extractEnergy(howMuch, simulate), simulate)
|
||||||
}
|
}
|
||||||
|
@ -40,4 +40,28 @@ class ProxiedEnergyStorage<T : IMatteryEnergyStorage>(var parent: T? = null) : I
|
|||||||
override fun fillBattery(): Boolean {
|
override fun fillBattery(): Boolean {
|
||||||
return parent?.fillBattery() ?: false
|
return parent?.fillBattery() ?: false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun receiveEnergy(maxReceive: Int, simulate: Boolean): Int {
|
||||||
|
return parent?.receiveEnergy(maxReceive, simulate) ?: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun extractEnergy(maxReceive: Int, simulate: Boolean): Int {
|
||||||
|
return parent?.extractEnergy(maxReceive, simulate) ?: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getEnergyStored(): Int {
|
||||||
|
return parent?.getEnergyStored() ?: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getMaxEnergyStored(): Int {
|
||||||
|
return parent?.getMaxEnergyStored() ?: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun canExtract(): Boolean {
|
||||||
|
return parent?.canExtract() == true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun canReceive(): Boolean {
|
||||||
|
return parent?.canReceive() == true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,59 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.capability.matter
|
||||||
|
|
||||||
|
import ru.dbotthepony.mc.otm.capability.FlowDirection
|
||||||
|
import ru.dbotthepony.mc.otm.capability.iterateProviders
|
||||||
|
import ru.dbotthepony.mc.otm.core.collect.map
|
||||||
|
import ru.dbotthepony.mc.otm.core.collect.reduce
|
||||||
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
|
import java.util.function.Supplier
|
||||||
|
import java.util.random.RandomGenerator
|
||||||
|
|
||||||
|
open class CombinedMatterStorage(
|
||||||
|
final override val matterFlow: FlowDirection,
|
||||||
|
val provider: Supplier<out Iterator<IMatterStorage>>,
|
||||||
|
val random: Supplier<RandomGenerator?> = Supplier { null }
|
||||||
|
) : IMatterStorage {
|
||||||
|
final override var storedMatter: Decimal
|
||||||
|
get() = provider.get().map { it.storedMatter }.reduce(Decimal.ZERO, Decimal::plus)
|
||||||
|
set(value) {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
final override val maxStoredMatter: Decimal
|
||||||
|
get() = provider.get().map { it.maxStoredMatter }.reduce(Decimal.ZERO, Decimal::plus)
|
||||||
|
|
||||||
|
final override fun receiveMatter(howMuch: Decimal, simulate: Boolean): Decimal {
|
||||||
|
return provider.iterateProviders(random.get(), howMuch, simulate, IMatterStorage::receiveMatter)
|
||||||
|
}
|
||||||
|
|
||||||
|
final override fun extractMatter(howMuch: Decimal, simulate: Boolean): Decimal {
|
||||||
|
return provider.iterateProviders(random.get(), howMuch, simulate, IMatterStorage::extractMatter)
|
||||||
|
}
|
||||||
|
|
||||||
|
final override val canSetMatterLevel: Boolean
|
||||||
|
get() = false
|
||||||
|
|
||||||
|
final override fun drainMatter(): Boolean {
|
||||||
|
var any = false
|
||||||
|
provider.get().forEach { any = it.drainMatter() || any }
|
||||||
|
return any
|
||||||
|
}
|
||||||
|
|
||||||
|
final override fun fillMatter(): Boolean {
|
||||||
|
var any = false
|
||||||
|
provider.get().forEach { any = it.fillMatter() || any }
|
||||||
|
return any
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
final override fun receiveMatterChecked(howMuch: Decimal, simulate: Boolean): Decimal {
|
||||||
|
return provider.iterateProviders(random.get(), howMuch, simulate, IMatterStorage::receiveMatterChecked)
|
||||||
|
}
|
||||||
|
|
||||||
|
final override fun extractMatterChecked(howMuch: Decimal, simulate: Boolean): Decimal {
|
||||||
|
return provider.iterateProviders(random.get(), howMuch, simulate, IMatterStorage::extractMatterChecked)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
final override val missingMatter: Decimal
|
||||||
|
get() = provider.get().iterator().map { it.missingMatter }.reduce(Decimal.ZERO, Decimal::plus)
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.capability.matter
|
||||||
|
|
||||||
|
import ru.dbotthepony.mc.otm.capability.AbstractProfiledStorage
|
||||||
|
import ru.dbotthepony.mc.otm.capability.FlowDirection
|
||||||
|
import ru.dbotthepony.mc.otm.capability.IProfiledStorage
|
||||||
|
import ru.dbotthepony.mc.otm.core.chart.CombinedDecimalHistoryChart
|
||||||
|
import ru.dbotthepony.mc.otm.core.collect.map
|
||||||
|
import ru.dbotthepony.mc.otm.core.collect.reduce
|
||||||
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
|
import ru.dbotthepony.mc.otm.core.util.ITickable
|
||||||
|
import java.util.function.Supplier
|
||||||
|
import java.util.random.RandomGenerator
|
||||||
|
|
||||||
|
class CombinedProfiledMatterStorage(
|
||||||
|
matterFlow: FlowDirection,
|
||||||
|
provider: Supplier<out Iterator<ProfiledMatterStorage<*>>>,
|
||||||
|
random: Supplier<RandomGenerator?> = Supplier { null }
|
||||||
|
) : CombinedMatterStorage(matterFlow, provider, random), IProfiledMatterStorage, IProfiledStorage.Combined, ITickable {
|
||||||
|
override val received = CombinedDecimalHistoryChart(Supplier { this.provider.get().map { (it as ProfiledMatterStorage<*>).received } }, ticks = AbstractProfiledStorage.HISTORY_SIZE)
|
||||||
|
override val transferred = CombinedDecimalHistoryChart(Supplier { this.provider.get().map { (it as ProfiledMatterStorage<*>).transferred } }, ticks = AbstractProfiledStorage.HISTORY_SIZE)
|
||||||
|
override val receivedThisTick: Decimal
|
||||||
|
get() = provider.get().map { (it as ProfiledMatterStorage<*>).receivedThisTick }.reduce(Decimal.ZERO, Decimal::plus)
|
||||||
|
override val transferredThisTick: Decimal
|
||||||
|
get() = provider.get().map { (it as ProfiledMatterStorage<*>).transferredThisTick }.reduce(Decimal.ZERO, Decimal::plus)
|
||||||
|
|
||||||
|
override fun tick() {
|
||||||
|
received.tick()
|
||||||
|
transferred.tick()
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@ package ru.dbotthepony.mc.otm.capability.matter
|
|||||||
import ru.dbotthepony.mc.otm.capability.FlowDirection
|
import ru.dbotthepony.mc.otm.capability.FlowDirection
|
||||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
import ru.dbotthepony.kommons.math.RGBAColor
|
import ru.dbotthepony.kommons.math.RGBAColor
|
||||||
|
import ru.dbotthepony.mc.otm.capability.IProfiledStorage
|
||||||
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
|
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
@ -39,21 +40,41 @@ interface IMatterStorage {
|
|||||||
/**
|
/**
|
||||||
* Empties matter stored of this matter storage, if possible
|
* Empties matter stored of this matter storage, if possible
|
||||||
*
|
*
|
||||||
* @throws [UnsupportedOperationException]
|
|
||||||
* @see storedMatter
|
* @see storedMatter
|
||||||
|
* @return whenever operation was successful, including partial completion
|
||||||
*/
|
*/
|
||||||
fun drainMatter() {
|
fun drainMatter(): Boolean {
|
||||||
storedMatter = Decimal.ZERO
|
try {
|
||||||
|
if (canSetMatterLevel)
|
||||||
|
storedMatter = Decimal.ZERO
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
} catch (err: UnsupportedOperationException) {
|
||||||
|
// anti-stupid
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fully fills matter stored, if possible
|
* Fully fills matter stored, if possible
|
||||||
*
|
*
|
||||||
* @throws [UnsupportedOperationException]
|
|
||||||
* @see storedMatter
|
* @see storedMatter
|
||||||
|
* @return whenever operation was successful, including partial completion
|
||||||
*/
|
*/
|
||||||
fun fillMatter() {
|
fun fillMatter(): Boolean {
|
||||||
storedMatter = maxStoredMatter
|
try {
|
||||||
|
if (canSetMatterLevel)
|
||||||
|
storedMatter = maxStoredMatter
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
} catch (err: UnsupportedOperationException) {
|
||||||
|
// anti-stupid
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -122,3 +143,5 @@ fun IMatterStorage.getBarWidth(): Int {
|
|||||||
fun IMatterStorage.getBarColor(): Int {
|
fun IMatterStorage.getBarColor(): Int {
|
||||||
return RGBAColor.LOW_MATTER.linearInterpolation((storedMatter / maxStoredMatter).toFloat(), RGBAColor.FULL_MATTER).toBGR()
|
return RGBAColor.LOW_MATTER.linearInterpolation((storedMatter / maxStoredMatter).toFloat(), RGBAColor.FULL_MATTER).toBGR()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface IProfiledMatterStorage : IMatterStorage, IProfiledStorage
|
||||||
|
@ -32,12 +32,12 @@ class ProfiledMatterStorage<out M : IMatterStorage>(parent: M) : AbstractProfile
|
|||||||
override val canSetMatterLevel: Boolean
|
override val canSetMatterLevel: Boolean
|
||||||
get() = parent.canSetMatterLevel
|
get() = parent.canSetMatterLevel
|
||||||
|
|
||||||
override fun drainMatter() {
|
override fun drainMatter(): Boolean {
|
||||||
parent.drainMatter()
|
return parent.drainMatter()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun fillMatter() {
|
override fun fillMatter(): Boolean {
|
||||||
parent.fillMatter()
|
return parent.fillMatter()
|
||||||
}
|
}
|
||||||
|
|
||||||
override val missingMatter: Decimal
|
override val missingMatter: Decimal
|
||||||
|
@ -13,6 +13,7 @@ import net.minecraft.client.renderer.Sheets
|
|||||||
import net.minecraft.client.resources.model.ModelBakery
|
import net.minecraft.client.resources.model.ModelBakery
|
||||||
import net.neoforged.fml.ModLoader
|
import net.neoforged.fml.ModLoader
|
||||||
import net.neoforged.neoforge.client.event.RegisterRenderBuffersEvent
|
import net.neoforged.neoforge.client.event.RegisterRenderBuffersEvent
|
||||||
|
import ru.dbotthepony.mc.otm.client.render.blockentity.BlackHoleGeneratorRenderer
|
||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
|
|
||||||
private fun equals(existing: ImmutableList<RenderType>?, types: Array<out RenderType>): Boolean {
|
private fun equals(existing: ImmutableList<RenderType>?, types: Array<out RenderType>): Boolean {
|
||||||
@ -179,6 +180,7 @@ class DynamicBufferSource(
|
|||||||
next = State(RenderType.entityGlintDirect(), ImmutableList.of(next.type), immutableAfter = true, chained = false)
|
next = State(RenderType.entityGlintDirect(), ImmutableList.of(next.type), immutableAfter = true, chained = false)
|
||||||
next = State(RenderType.waterMask(), ImmutableList.of(next.type), immutableAfter = true, chained = false)
|
next = State(RenderType.waterMask(), ImmutableList.of(next.type), immutableAfter = true, chained = false)
|
||||||
|
|
||||||
|
next = State(MULTIBLOCK_STATUS_RENDER_TYPE, ImmutableList.of(next.type), immutableAfter = true, chained = true)
|
||||||
next = State(rectRenderType(true, true, true), ImmutableList.of(next.type), immutableAfter = true, chained = true)
|
next = State(rectRenderType(true, true, true), ImmutableList.of(next.type), immutableAfter = true, chained = true)
|
||||||
next = State(rectRenderType(true, true, false), ImmutableList.of(next.type), immutableAfter = true, chained = true)
|
next = State(rectRenderType(true, true, false), ImmutableList.of(next.type), immutableAfter = true, chained = true)
|
||||||
|
|
||||||
|
@ -0,0 +1,113 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.client.render
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.systems.RenderSystem
|
||||||
|
import com.mojang.blaze3d.vertex.DefaultVertexFormat
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack
|
||||||
|
import com.mojang.blaze3d.vertex.VertexFormat
|
||||||
|
import net.minecraft.client.renderer.GameRenderer
|
||||||
|
import net.minecraft.client.renderer.MultiBufferSource
|
||||||
|
import net.minecraft.client.renderer.RenderStateShard
|
||||||
|
import net.minecraft.client.renderer.RenderType
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
import org.lwjgl.opengl.GL11.GL_LESS
|
||||||
|
import ru.dbotthepony.kommons.math.RGBAColor
|
||||||
|
import ru.dbotthepony.mc.otm.core.math.component1
|
||||||
|
import ru.dbotthepony.mc.otm.core.math.component2
|
||||||
|
import ru.dbotthepony.mc.otm.core.math.component3
|
||||||
|
import ru.dbotthepony.mc.otm.core.math.minus
|
||||||
|
import ru.dbotthepony.mc.otm.core.multiblock.IMultiblockAccess
|
||||||
|
import ru.dbotthepony.mc.otm.core.multiblock.NodeStatus
|
||||||
|
|
||||||
|
private val UNKNOWN = RGBAColor.YELLOW.copy(alpha = 0.5f)
|
||||||
|
private val VALID = RGBAColor.DARK_GREEN.copy(alpha = 0.5f)
|
||||||
|
private val INVALID = RGBAColor.DARK_RED.copy(alpha = 0.5f)
|
||||||
|
|
||||||
|
val MULTIBLOCK_STATUS_RENDER_TYPE = run {
|
||||||
|
val builder = RenderType.CompositeState.builder()
|
||||||
|
|
||||||
|
builder.setShaderState(RenderStateShard.ShaderStateShard(GameRenderer::getPositionColorShader))
|
||||||
|
builder.setDepthTestState(RenderStateShard.DepthTestStateShard("less", GL_LESS))
|
||||||
|
|
||||||
|
builder.setTransparencyState(RenderStateShard.TransparencyStateShard("normal_blend", {
|
||||||
|
RenderSystem.enableBlend()
|
||||||
|
RenderSystem.defaultBlendFunc()
|
||||||
|
}, { RenderSystem.disableBlend() }))
|
||||||
|
|
||||||
|
@Suppress("INACCESSIBLE_TYPE")
|
||||||
|
RenderType.create(
|
||||||
|
"otm_multiblock_status",
|
||||||
|
DefaultVertexFormat.POSITION_COLOR,
|
||||||
|
VertexFormat.Mode.QUADS,
|
||||||
|
256_000,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
builder.createCompositeState(false)
|
||||||
|
) as RenderType
|
||||||
|
}
|
||||||
|
|
||||||
|
fun IMultiblockAccess.render(
|
||||||
|
poseStack: PoseStack,
|
||||||
|
buffers: MultiBufferSource,
|
||||||
|
relativePos: BlockPos,
|
||||||
|
) {
|
||||||
|
val buffer = buffers.getBuffer(MULTIBLOCK_STATUS_RENDER_TYPE)
|
||||||
|
val nodes = currentNodes
|
||||||
|
|
||||||
|
for (node in nodes.values) {
|
||||||
|
val color = when (node.status) {
|
||||||
|
NodeStatus.UNKNOWN -> UNKNOWN
|
||||||
|
NodeStatus.VALID -> VALID
|
||||||
|
NodeStatus.INVALID -> INVALID
|
||||||
|
}
|
||||||
|
|
||||||
|
val (x, y, z) = node.pos - relativePos
|
||||||
|
|
||||||
|
if (nodes[node.pos.south()]?.status != node.status) {
|
||||||
|
// south
|
||||||
|
buffer.vertex(poseStack, x.toFloat(), y.toFloat(), z.toFloat() + 1f).color(color)
|
||||||
|
buffer.vertex(poseStack, x.toFloat() + 1f, y.toFloat(), z.toFloat() + 1f).color(color)
|
||||||
|
buffer.vertex(poseStack, x.toFloat() + 1f, y.toFloat() + 1f, z.toFloat() + 1f).color(color)
|
||||||
|
buffer.vertex(poseStack, x.toFloat(), y.toFloat() + 1f, z.toFloat() + 1f).color(color)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nodes[node.pos.north()]?.status != node.status) {
|
||||||
|
// north
|
||||||
|
buffer.vertex(poseStack, x.toFloat(), y.toFloat(), z.toFloat()).color(color)
|
||||||
|
buffer.vertex(poseStack, x.toFloat(), y.toFloat() + 1f, z.toFloat()).color(color)
|
||||||
|
buffer.vertex(poseStack, x.toFloat() + 1f, y.toFloat() + 1f, z.toFloat()).color(color)
|
||||||
|
buffer.vertex(poseStack, x.toFloat() + 1f, y.toFloat(), z.toFloat()).color(color)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nodes[node.pos.above()]?.status != node.status) {
|
||||||
|
// up
|
||||||
|
buffer.vertex(poseStack, x.toFloat(), y.toFloat() + 1f, z.toFloat()).color(color)
|
||||||
|
buffer.vertex(poseStack, x.toFloat(), y.toFloat() + 1f, z.toFloat() + 1f).color(color)
|
||||||
|
buffer.vertex(poseStack, x.toFloat() + 1f, y.toFloat() + 1f, z.toFloat() + 1f).color(color)
|
||||||
|
buffer.vertex(poseStack, x.toFloat() + 1f, y.toFloat() + 1f, z.toFloat()).color(color)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nodes[node.pos.below()]?.status != node.status) {
|
||||||
|
// down
|
||||||
|
buffer.vertex(poseStack, x.toFloat() + 1f, y.toFloat(), z.toFloat() + 1f).color(color)
|
||||||
|
buffer.vertex(poseStack, x.toFloat(), y.toFloat(), z.toFloat() + 1f).color(color)
|
||||||
|
buffer.vertex(poseStack, x.toFloat(), y.toFloat(), z.toFloat()).color(color)
|
||||||
|
buffer.vertex(poseStack, x.toFloat() + 1f, y.toFloat(), z.toFloat()).color(color)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nodes[node.pos.east()]?.status != node.status) {
|
||||||
|
// east
|
||||||
|
buffer.vertex(poseStack, x.toFloat() + 1f, y.toFloat(), z.toFloat()).color(color)
|
||||||
|
buffer.vertex(poseStack, x.toFloat() + 1f, y.toFloat() + 1f, z.toFloat()).color(color)
|
||||||
|
buffer.vertex(poseStack, x.toFloat() + 1f, y.toFloat() + 1f, z.toFloat() + 1f).color(color)
|
||||||
|
buffer.vertex(poseStack, x.toFloat() + 1f, y.toFloat(), z.toFloat() + 1f).color(color)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nodes[node.pos.west()]?.status != node.status) {
|
||||||
|
// west
|
||||||
|
buffer.vertex(poseStack, x.toFloat(), y.toFloat() + 1f, z.toFloat() + 1f).color(color)
|
||||||
|
buffer.vertex(poseStack, x.toFloat(), y.toFloat() + 1f, z.toFloat()).color(color)
|
||||||
|
buffer.vertex(poseStack, x.toFloat(), y.toFloat(), z.toFloat()).color(color)
|
||||||
|
buffer.vertex(poseStack, x.toFloat(), y.toFloat(), z.toFloat() + 1f).color(color)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.client.render.blockentity
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack
|
||||||
|
import net.minecraft.client.renderer.MultiBufferSource
|
||||||
|
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer
|
||||||
|
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider
|
||||||
|
import ru.dbotthepony.mc.otm.block.entity.blackhole.BlackHoleGeneratorBlockEntity
|
||||||
|
import ru.dbotthepony.mc.otm.client.render.DynamicBufferSource
|
||||||
|
import ru.dbotthepony.mc.otm.client.render.render
|
||||||
|
|
||||||
|
class BlackHoleGeneratorRenderer(private val context: BlockEntityRendererProvider.Context) : BlockEntityRenderer<BlackHoleGeneratorBlockEntity> {
|
||||||
|
override fun render(
|
||||||
|
tile: BlackHoleGeneratorBlockEntity,
|
||||||
|
partialtick: Float,
|
||||||
|
poseStack: PoseStack,
|
||||||
|
buffers: MultiBufferSource,
|
||||||
|
packedLight: Int,
|
||||||
|
packedOverlay: Int
|
||||||
|
) {
|
||||||
|
//if (!tile.drawBuildingGuide) return
|
||||||
|
val multiblock = tile.multiblock ?: return
|
||||||
|
multiblock.render(poseStack, DynamicBufferSource.WORLD, tile.blockPos)
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
package ru.dbotthepony.mc.otm.client.screen.panels
|
package ru.dbotthepony.mc.otm.client.screen.panels
|
||||||
|
|
||||||
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
|
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
|
||||||
import ru.dbotthepony.mc.otm.core.AbstractHistoryChart
|
import ru.dbotthepony.mc.otm.core.chart.AbstractHistoryChart
|
||||||
|
|
||||||
abstract class AbstractHistoryGraphPanel<out S : MatteryScreen<*>, G : AbstractHistoryChart<V>, V : Any>(
|
abstract class AbstractHistoryGraphPanel<out S : MatteryScreen<*>, G : AbstractHistoryChart<V>, V : Any>(
|
||||||
screen: S,
|
screen: S,
|
||||||
|
@ -8,7 +8,7 @@ import ru.dbotthepony.mc.otm.client.render.ChartMouseLabels
|
|||||||
import ru.dbotthepony.mc.otm.client.render.MGUIGraphics
|
import ru.dbotthepony.mc.otm.client.render.MGUIGraphics
|
||||||
import ru.dbotthepony.mc.otm.client.render.renderChart
|
import ru.dbotthepony.mc.otm.client.render.renderChart
|
||||||
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
|
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
|
||||||
import ru.dbotthepony.mc.otm.core.DecimalHistoryChart
|
import ru.dbotthepony.mc.otm.core.chart.DecimalHistoryChart
|
||||||
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.Decimal
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
|
@ -494,7 +494,7 @@ private enum class PreviewScrollers(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
class AndroidStationScreen constructor(p_97741_: AndroidStationMenu, p_97742_: Inventory, p_97743_: Component) :
|
class AndroidStationScreen(p_97741_: AndroidStationMenu, p_97742_: Inventory, p_97743_: Component) :
|
||||||
MatteryScreen<AndroidStationMenu>(p_97741_, p_97742_, p_97743_) {
|
MatteryScreen<AndroidStationMenu>(p_97741_, p_97742_, p_97743_) {
|
||||||
|
|
||||||
private fun makeCanvas(isPreview: Boolean): DraggableCanvasPanel<AndroidStationScreen> {
|
private fun makeCanvas(isPreview: Boolean): DraggableCanvasPanel<AndroidStationScreen> {
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.client.screen.tech
|
||||||
|
|
||||||
|
import net.minecraft.network.chat.Component
|
||||||
|
import net.minecraft.world.entity.player.Inventory
|
||||||
|
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
|
||||||
|
import ru.dbotthepony.mc.otm.client.screen.panels.Dock
|
||||||
|
import ru.dbotthepony.mc.otm.client.screen.panels.DockResizeMode
|
||||||
|
import ru.dbotthepony.mc.otm.client.screen.panels.FramePanel
|
||||||
|
import ru.dbotthepony.mc.otm.client.screen.widget.ProfiledMatterGaugePanel
|
||||||
|
import ru.dbotthepony.mc.otm.client.screen.widget.TallHorizontalProfiledPowerGaugePanel
|
||||||
|
import ru.dbotthepony.mc.otm.client.screen.widget.WideProfiledPowerGaugePanel
|
||||||
|
import ru.dbotthepony.mc.otm.menu.tech.BlackHoleGeneratorMenu
|
||||||
|
import ru.dbotthepony.mc.otm.menu.tech.CobblerMenu
|
||||||
|
|
||||||
|
class BlackHoleGeneratorScreen(menu: BlackHoleGeneratorMenu, inventory: Inventory, title: Component) : MatteryScreen<BlackHoleGeneratorMenu>(menu, inventory, title) {
|
||||||
|
override fun makeMainFrame(): FramePanel<MatteryScreen<*>> {
|
||||||
|
val frame = super.makeMainFrame()!!
|
||||||
|
|
||||||
|
val energy = TallHorizontalProfiledPowerGaugePanel(this, frame, menu.energy)
|
||||||
|
//val matter = ProfiledMatterGaugePanel
|
||||||
|
|
||||||
|
energy.dock = Dock.TOP
|
||||||
|
energy.dockTop = 4f
|
||||||
|
energy.dockResize = DockResizeMode.NONE
|
||||||
|
|
||||||
|
return frame
|
||||||
|
}
|
||||||
|
}
|
@ -10,27 +10,26 @@ import ru.dbotthepony.mc.otm.client.screen.panels.FramePanel
|
|||||||
import ru.dbotthepony.mc.otm.client.screen.panels.SpritePanel
|
import ru.dbotthepony.mc.otm.client.screen.panels.SpritePanel
|
||||||
import ru.dbotthepony.mc.otm.client.screen.panels.button.DeviceControls
|
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.MatterCapacitorSlotPanel
|
import ru.dbotthepony.mc.otm.client.screen.panels.slot.BatterySlotPanel
|
||||||
import ru.dbotthepony.mc.otm.client.screen.widget.HorizontalProfiledPowerGaugePanel
|
import ru.dbotthepony.mc.otm.client.screen.widget.HorizontalProfiledPowerGaugePanel
|
||||||
import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel
|
import ru.dbotthepony.mc.otm.client.screen.widget.ProgressGaugePanel
|
||||||
import ru.dbotthepony.mc.otm.menu.tech.EnergyHatchMenu
|
import ru.dbotthepony.mc.otm.menu.tech.EnergyHatchMenu
|
||||||
import ru.dbotthepony.mc.otm.menu.tech.MatterHatchMenu
|
|
||||||
|
|
||||||
class EnergyHatchScreen(menu: EnergyHatchMenu, inventory: Inventory, title: Component) : MatteryScreen<EnergyHatchMenu>(menu, inventory, title) {
|
class EnergyHatchScreen(menu: EnergyHatchMenu, inventory: Inventory, title: Component) : MatteryScreen<EnergyHatchMenu>(menu, inventory, title) {
|
||||||
override fun makeMainFrame(): FramePanel<MatteryScreen<*>> {
|
override fun makeMainFrame(): FramePanel<MatteryScreen<*>> {
|
||||||
val frame = FramePanel(this, null, 0f, 0f, INVENTORY_FRAME_WIDTH, AbstractSlotPanel.SIZE, getTitle())
|
val frame = FramePanel.padded(this, null, INVENTORY_FRAME_WIDTH, AbstractSlotPanel.SIZE, getTitle())
|
||||||
|
|
||||||
frame.makeCloseButton()
|
frame.makeCloseButton()
|
||||||
frame.onClose { onClose() }
|
frame.onClose { onClose() }
|
||||||
|
|
||||||
for (slot in menu.inputSlots) {
|
for (slot in menu.inputSlots) {
|
||||||
val panel = MatterCapacitorSlotPanel(this, frame, slot)
|
val panel = BatterySlotPanel(this, frame, slot)
|
||||||
panel.dock = Dock.LEFT
|
panel.dock = Dock.LEFT
|
||||||
panel.dockResize = DockResizeMode.NONE
|
panel.dockResize = DockResizeMode.NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
val arrow = SpritePanel(this, frame, ProgressGaugePanel.GAUGE_BACKGROUND)
|
val arrow = SpritePanel(this, frame, ProgressGaugePanel.GAUGE_BACKGROUND)
|
||||||
arrow.dockLeft = 2f
|
arrow.dockLeft = 20f
|
||||||
arrow.dockRight = 2f
|
arrow.dockRight = 2f
|
||||||
arrow.dock = Dock.LEFT
|
arrow.dock = Dock.LEFT
|
||||||
arrow.dockResize = DockResizeMode.NONE
|
arrow.dockResize = DockResizeMode.NONE
|
||||||
|
@ -16,7 +16,7 @@ import ru.dbotthepony.mc.otm.menu.tech.MatterHatchMenu
|
|||||||
|
|
||||||
class MatterHatchScreen(menu: MatterHatchMenu, inventory: Inventory, title: Component) : MatteryScreen<MatterHatchMenu>(menu, inventory, title) {
|
class MatterHatchScreen(menu: MatterHatchMenu, inventory: Inventory, title: Component) : MatteryScreen<MatterHatchMenu>(menu, inventory, title) {
|
||||||
override fun makeMainFrame(): FramePanel<MatteryScreen<*>> {
|
override fun makeMainFrame(): FramePanel<MatteryScreen<*>> {
|
||||||
val frame = FramePanel(this, null, 0f, 0f, INVENTORY_FRAME_WIDTH, AbstractSlotPanel.SIZE, getTitle())
|
val frame = FramePanel.padded(this, null, INVENTORY_FRAME_WIDTH, AbstractSlotPanel.SIZE, getTitle())
|
||||||
|
|
||||||
frame.makeCloseButton()
|
frame.makeCloseButton()
|
||||||
frame.onClose { onClose() }
|
frame.onClose { onClose() }
|
||||||
@ -28,7 +28,7 @@ class MatterHatchScreen(menu: MatterHatchMenu, inventory: Inventory, title: Comp
|
|||||||
}
|
}
|
||||||
|
|
||||||
val arrow = SpritePanel(this, frame, ProgressGaugePanel.GAUGE_BACKGROUND)
|
val arrow = SpritePanel(this, frame, ProgressGaugePanel.GAUGE_BACKGROUND)
|
||||||
arrow.dockLeft = 2f
|
arrow.dockLeft = 20f
|
||||||
arrow.dockRight = 2f
|
arrow.dockRight = 2f
|
||||||
arrow.dock = Dock.LEFT
|
arrow.dock = Dock.LEFT
|
||||||
arrow.dockResize = DockResizeMode.NONE
|
arrow.dockResize = DockResizeMode.NONE
|
||||||
|
@ -5,6 +5,7 @@ import ru.dbotthepony.mc.otm.client.render.MGUIGraphics
|
|||||||
import ru.dbotthepony.mc.otm.client.render.UVWindingOrder
|
import ru.dbotthepony.mc.otm.client.render.UVWindingOrder
|
||||||
import ru.dbotthepony.mc.otm.client.render.WidgetLocation
|
import ru.dbotthepony.mc.otm.client.render.WidgetLocation
|
||||||
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
|
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
|
||||||
|
import ru.dbotthepony.mc.otm.menu.widget.IProfiledLevelGaugeWidget
|
||||||
import ru.dbotthepony.mc.otm.menu.widget.LevelGaugeWidget
|
import ru.dbotthepony.mc.otm.menu.widget.LevelGaugeWidget
|
||||||
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
|
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
|
||||||
|
|
||||||
@ -73,7 +74,7 @@ fun <S : Screen> TallHorizontalPowerGaugePanel(
|
|||||||
open class HorizontalProfiledPowerGaugePanel<out S : Screen>(
|
open class HorizontalProfiledPowerGaugePanel<out S : Screen>(
|
||||||
screen: S,
|
screen: S,
|
||||||
parent: EditablePanel<*>? = null,
|
parent: EditablePanel<*>? = null,
|
||||||
widget: ProfiledLevelGaugeWidget<*>,
|
widget: IProfiledLevelGaugeWidget,
|
||||||
x: Float = 0f,
|
x: Float = 0f,
|
||||||
y: Float = 0f,
|
y: Float = 0f,
|
||||||
width: Float = HorizontalPowerGaugePanel.GAUGE_BACKGROUND.width,
|
width: Float = HorizontalPowerGaugePanel.GAUGE_BACKGROUND.width,
|
||||||
@ -93,7 +94,7 @@ open class HorizontalProfiledPowerGaugePanel<out S : Screen>(
|
|||||||
fun <S : Screen> TallHorizontalProfiledPowerGaugePanel(
|
fun <S : Screen> TallHorizontalProfiledPowerGaugePanel(
|
||||||
screen: S,
|
screen: S,
|
||||||
parent: EditablePanel<*>? = null,
|
parent: EditablePanel<*>? = null,
|
||||||
widget: ProfiledLevelGaugeWidget<*>,
|
widget: IProfiledLevelGaugeWidget,
|
||||||
x: Float = 0f,
|
x: Float = 0f,
|
||||||
y: Float = 0f,
|
y: Float = 0f,
|
||||||
width: Float = HorizontalPowerGaugePanel.GAUGE_BACKGROUND_TALL.width,
|
width: Float = HorizontalPowerGaugePanel.GAUGE_BACKGROUND_TALL.width,
|
||||||
|
@ -5,30 +5,20 @@ import com.mojang.blaze3d.vertex.BufferUploader
|
|||||||
import com.mojang.blaze3d.vertex.DefaultVertexFormat
|
import com.mojang.blaze3d.vertex.DefaultVertexFormat
|
||||||
import com.mojang.blaze3d.vertex.VertexFormat
|
import com.mojang.blaze3d.vertex.VertexFormat
|
||||||
import com.mojang.datafixers.util.Either
|
import com.mojang.datafixers.util.Either
|
||||||
import it.unimi.dsi.fastutil.ints.IntArrayList
|
|
||||||
import net.minecraft.ChatFormatting
|
|
||||||
import net.minecraft.client.gui.screens.Screen
|
import net.minecraft.client.gui.screens.Screen
|
||||||
import net.minecraft.client.renderer.GameRenderer
|
import net.minecraft.client.renderer.GameRenderer
|
||||||
import net.minecraft.network.chat.Component
|
|
||||||
import net.minecraft.network.chat.FormattedText
|
import net.minecraft.network.chat.FormattedText
|
||||||
import net.minecraft.world.inventory.tooltip.TooltipComponent
|
import net.minecraft.world.inventory.tooltip.TooltipComponent
|
||||||
import org.lwjgl.opengl.GL11
|
import org.lwjgl.opengl.GL11
|
||||||
import ru.dbotthepony.kommons.util.value
|
|
||||||
import ru.dbotthepony.mc.otm.capability.AbstractProfiledStorage
|
|
||||||
import ru.dbotthepony.mc.otm.client.render.MGUIGraphics
|
|
||||||
import ru.dbotthepony.mc.otm.client.ShiftPressedCond
|
import ru.dbotthepony.mc.otm.client.ShiftPressedCond
|
||||||
import ru.dbotthepony.mc.otm.client.isShiftDown
|
import ru.dbotthepony.mc.otm.client.render.MGUIGraphics
|
||||||
import ru.dbotthepony.mc.otm.client.minecraft
|
|
||||||
import ru.dbotthepony.mc.otm.client.render.WidgetLocation
|
import ru.dbotthepony.mc.otm.client.render.WidgetLocation
|
||||||
import ru.dbotthepony.mc.otm.client.render.tesselator
|
import ru.dbotthepony.mc.otm.client.render.tesselator
|
||||||
import ru.dbotthepony.mc.otm.client.render.uv
|
import ru.dbotthepony.mc.otm.client.render.uv
|
||||||
import ru.dbotthepony.mc.otm.client.render.vertex
|
import ru.dbotthepony.mc.otm.client.render.vertex
|
||||||
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
|
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
|
||||||
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.Decimal
|
|
||||||
import ru.dbotthepony.mc.otm.core.util.formatHistory
|
import ru.dbotthepony.mc.otm.core.util.formatHistory
|
||||||
import ru.dbotthepony.mc.otm.core.util.formatMatter
|
|
||||||
import ru.dbotthepony.mc.otm.core.util.formatMatterLevel
|
import ru.dbotthepony.mc.otm.core.util.formatMatterLevel
|
||||||
import ru.dbotthepony.mc.otm.menu.widget.LevelGaugeWidget
|
import ru.dbotthepony.mc.otm.menu.widget.LevelGaugeWidget
|
||||||
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
|
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
|
||||||
|
@ -12,6 +12,7 @@ import ru.dbotthepony.mc.otm.client.render.*
|
|||||||
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
|
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
|
||||||
import ru.dbotthepony.mc.otm.core.util.formatHistory
|
import ru.dbotthepony.mc.otm.core.util.formatHistory
|
||||||
import ru.dbotthepony.mc.otm.core.util.formatPowerLevel
|
import ru.dbotthepony.mc.otm.core.util.formatPowerLevel
|
||||||
|
import ru.dbotthepony.mc.otm.menu.widget.IProfiledLevelGaugeWidget
|
||||||
import ru.dbotthepony.mc.otm.menu.widget.LevelGaugeWidget
|
import ru.dbotthepony.mc.otm.menu.widget.LevelGaugeWidget
|
||||||
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
|
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
|
||||||
|
|
||||||
@ -92,7 +93,7 @@ fun <S : Screen> WidePowerGaugePanel(
|
|||||||
open class ProfiledPowerGaugePanel<out S : Screen>(
|
open class ProfiledPowerGaugePanel<out S : Screen>(
|
||||||
screen: S,
|
screen: S,
|
||||||
parent: EditablePanel<*>? = null,
|
parent: EditablePanel<*>? = null,
|
||||||
val profiledWidget: ProfiledLevelGaugeWidget<*>,
|
val profiledWidget: IProfiledLevelGaugeWidget,
|
||||||
x: Float = 0f,
|
x: Float = 0f,
|
||||||
y: Float = 0f,
|
y: Float = 0f,
|
||||||
width: Float = GAUGE_BACKGROUND.width,
|
width: Float = GAUGE_BACKGROUND.width,
|
||||||
@ -117,7 +118,7 @@ open class ProfiledPowerGaugePanel<out S : Screen>(
|
|||||||
fun <S : Screen> WideProfiledPowerGaugePanel(
|
fun <S : Screen> WideProfiledPowerGaugePanel(
|
||||||
screen: S,
|
screen: S,
|
||||||
parent: EditablePanel<*>? = null,
|
parent: EditablePanel<*>? = null,
|
||||||
widget: ProfiledLevelGaugeWidget<*>,
|
widget: IProfiledLevelGaugeWidget,
|
||||||
x: Float = 0f,
|
x: Float = 0f,
|
||||||
y: Float = 0f,
|
y: Float = 0f,
|
||||||
width: Float = 18f,
|
width: Float = 18f,
|
||||||
|
@ -4,18 +4,15 @@ import net.minecraft.ChatFormatting
|
|||||||
import net.minecraft.nbt.CompoundTag
|
import net.minecraft.nbt.CompoundTag
|
||||||
import net.minecraft.network.chat.Component
|
import net.minecraft.network.chat.Component
|
||||||
import net.minecraft.resources.ResourceLocation
|
import net.minecraft.resources.ResourceLocation
|
||||||
import net.neoforged.neoforge.capabilities.BlockCapability
|
import ru.dbotthepony.kommons.math.RGBAColor
|
||||||
import ru.dbotthepony.mc.otm.capability.AbstractProfiledStorage
|
import ru.dbotthepony.mc.otm.capability.IProfiledStorage
|
||||||
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
||||||
import ru.dbotthepony.mc.otm.compat.jade.JadeColors
|
import ru.dbotthepony.mc.otm.compat.jade.JadeColors
|
||||||
import ru.dbotthepony.mc.otm.compat.jade.JadeTagKeys
|
import ru.dbotthepony.mc.otm.compat.jade.JadeTagKeys
|
||||||
import ru.dbotthepony.mc.otm.compat.jade.JadeUids
|
import ru.dbotthepony.mc.otm.compat.jade.JadeUids
|
||||||
import ru.dbotthepony.mc.otm.core.TranslatableComponent
|
import ru.dbotthepony.mc.otm.core.TranslatableComponent
|
||||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
|
||||||
import ru.dbotthepony.kommons.math.RGBAColor
|
|
||||||
import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity
|
|
||||||
import ru.dbotthepony.mc.otm.capability.matter.IMatterStorage
|
|
||||||
import ru.dbotthepony.mc.otm.core.getCapability
|
import ru.dbotthepony.mc.otm.core.getCapability
|
||||||
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
import ru.dbotthepony.mc.otm.core.math.getDecimal
|
import ru.dbotthepony.mc.otm.core.math.getDecimal
|
||||||
import ru.dbotthepony.mc.otm.core.math.putDecimal
|
import ru.dbotthepony.mc.otm.core.math.putDecimal
|
||||||
import ru.dbotthepony.mc.otm.core.util.formatMatter
|
import ru.dbotthepony.mc.otm.core.util.formatMatter
|
||||||
@ -37,14 +34,14 @@ object MatterStorageProvider : IBlockComponentProvider, IServerDataProvider<Bloc
|
|||||||
matterData.putDecimal("storedMatter", it.storedMatter)
|
matterData.putDecimal("storedMatter", it.storedMatter)
|
||||||
matterData.putDecimal("maxStoredMatter", it.maxStoredMatter)
|
matterData.putDecimal("maxStoredMatter", it.maxStoredMatter)
|
||||||
|
|
||||||
if (it is AbstractProfiledStorage<*>) {
|
if (it is IProfiledStorage) {
|
||||||
val profiledData = CompoundTag()
|
val profiledData = CompoundTag()
|
||||||
|
|
||||||
// profiledData.putDecimal("lastTickReceive", it.lastTickReceive)
|
// profiledData.putDecimal("lastTickReceive", it.lastTickReceive)
|
||||||
// profiledData.putDecimal("lastTickTransfer", it.lastTickTransfer)
|
// profiledData.putDecimal("lastTickTransfer", it.lastTickTransfer)
|
||||||
|
|
||||||
profiledData.putDecimal("weightedReceive", it.received.calcWeightedAverage())
|
profiledData.putDecimal("weightedReceive", it.received.calculateAverage())
|
||||||
profiledData.putDecimal("weightedTransfer", it.transferred.calcWeightedAverage())
|
profiledData.putDecimal("weightedTransfer", it.transferred.calculateAverage())
|
||||||
|
|
||||||
matterData.put("profiledData", profiledData)
|
matterData.put("profiledData", profiledData)
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import net.minecraft.ChatFormatting
|
|||||||
import net.minecraft.nbt.CompoundTag
|
import net.minecraft.nbt.CompoundTag
|
||||||
import net.minecraft.network.chat.Component
|
import net.minecraft.network.chat.Component
|
||||||
import net.minecraft.resources.ResourceLocation
|
import net.minecraft.resources.ResourceLocation
|
||||||
import ru.dbotthepony.mc.otm.capability.AbstractProfiledStorage
|
|
||||||
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
import ru.dbotthepony.mc.otm.capability.MatteryCapability
|
||||||
import ru.dbotthepony.mc.otm.compat.jade.JadeColors
|
import ru.dbotthepony.mc.otm.compat.jade.JadeColors
|
||||||
import ru.dbotthepony.mc.otm.compat.jade.JadeTagKeys
|
import ru.dbotthepony.mc.otm.compat.jade.JadeTagKeys
|
||||||
@ -12,6 +11,7 @@ import ru.dbotthepony.mc.otm.compat.jade.JadeUids
|
|||||||
import ru.dbotthepony.mc.otm.core.TranslatableComponent
|
import ru.dbotthepony.mc.otm.core.TranslatableComponent
|
||||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
import ru.dbotthepony.kommons.math.RGBAColor
|
import ru.dbotthepony.kommons.math.RGBAColor
|
||||||
|
import ru.dbotthepony.mc.otm.capability.IProfiledStorage
|
||||||
import ru.dbotthepony.mc.otm.core.getCapability
|
import ru.dbotthepony.mc.otm.core.getCapability
|
||||||
import ru.dbotthepony.mc.otm.core.math.getDecimal
|
import ru.dbotthepony.mc.otm.core.math.getDecimal
|
||||||
import ru.dbotthepony.mc.otm.core.math.putDecimal
|
import ru.dbotthepony.mc.otm.core.math.putDecimal
|
||||||
@ -31,14 +31,14 @@ object MatteryEnergyProvider : IBlockComponentProvider, IServerDataProvider<Bloc
|
|||||||
energyData.putDecimal("batteryLevel", it.batteryLevel)
|
energyData.putDecimal("batteryLevel", it.batteryLevel)
|
||||||
energyData.putDecimal("maxBatteryLevel", it.maxBatteryLevel)
|
energyData.putDecimal("maxBatteryLevel", it.maxBatteryLevel)
|
||||||
|
|
||||||
if (it is AbstractProfiledStorage<*>) {
|
if (it is IProfiledStorage) {
|
||||||
val profiledData = CompoundTag()
|
val profiledData = CompoundTag()
|
||||||
|
|
||||||
// profiledData.putDecimal("lastTickReceive", it.lastTickReceive)
|
// profiledData.putDecimal("lastTickReceive", it.lastTickReceive)
|
||||||
// profiledData.putDecimal("lastTickTransfer", it.lastTickTransfer)
|
// profiledData.putDecimal("lastTickTransfer", it.lastTickTransfer)
|
||||||
|
|
||||||
profiledData.putDecimal("weightedReceive", it.received.calcWeightedAverage())
|
profiledData.putDecimal("weightedReceive", it.received.calculateAverage())
|
||||||
profiledData.putDecimal("weightedTransfer", it.transferred.calcWeightedAverage())
|
profiledData.putDecimal("weightedTransfer", it.transferred.calculateAverage())
|
||||||
|
|
||||||
energyData.put("profiledData", profiledData)
|
energyData.put("profiledData", profiledData)
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package ru.dbotthepony.mc.otm.container
|
|||||||
|
|
||||||
import com.mojang.serialization.Codec
|
import com.mojang.serialization.Codec
|
||||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
|
|
||||||
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.ints.IntArrayList
|
||||||
import it.unimi.dsi.fastutil.ints.IntComparators
|
import it.unimi.dsi.fastutil.ints.IntComparators
|
||||||
@ -12,10 +11,8 @@ import net.minecraft.core.HolderLookup
|
|||||||
import net.minecraft.core.registries.BuiltInRegistries
|
import net.minecraft.core.registries.BuiltInRegistries
|
||||||
import net.minecraft.world.item.ItemStack
|
import net.minecraft.world.item.ItemStack
|
||||||
import net.minecraft.nbt.CompoundTag
|
import net.minecraft.nbt.CompoundTag
|
||||||
import net.minecraft.nbt.ListTag
|
|
||||||
import net.minecraft.nbt.NbtOps
|
import net.minecraft.nbt.NbtOps
|
||||||
import net.minecraft.nbt.Tag
|
import net.minecraft.nbt.Tag
|
||||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
|
||||||
import net.minecraft.world.Container
|
import net.minecraft.world.Container
|
||||||
import net.minecraft.world.entity.player.Player
|
import net.minecraft.world.entity.player.Player
|
||||||
import net.minecraft.world.entity.player.StackedContents
|
import net.minecraft.world.entity.player.StackedContents
|
||||||
@ -24,9 +21,7 @@ import net.minecraft.world.item.Item
|
|||||||
import net.minecraft.world.item.Items
|
import net.minecraft.world.item.Items
|
||||||
import net.neoforged.neoforge.common.util.INBTSerializable
|
import net.neoforged.neoforge.common.util.INBTSerializable
|
||||||
import org.apache.logging.log4j.LogManager
|
import org.apache.logging.log4j.LogManager
|
||||||
import ru.dbotthepony.kommons.collect.ListenableMap
|
|
||||||
import ru.dbotthepony.kommons.util.Delegate
|
import ru.dbotthepony.kommons.util.Delegate
|
||||||
import ru.dbotthepony.mc.otm.network.syncher.Syncher
|
|
||||||
import ru.dbotthepony.mc.otm.container.util.IContainerSlot
|
import ru.dbotthepony.mc.otm.container.util.IContainerSlot
|
||||||
import ru.dbotthepony.mc.otm.core.addSorted
|
import ru.dbotthepony.mc.otm.core.addSorted
|
||||||
import ru.dbotthepony.mc.otm.core.collect.any
|
import ru.dbotthepony.mc.otm.core.collect.any
|
||||||
@ -37,11 +32,8 @@ import ru.dbotthepony.mc.otm.core.collect.map
|
|||||||
import ru.dbotthepony.mc.otm.core.collect.toList
|
import ru.dbotthepony.mc.otm.core.collect.toList
|
||||||
import ru.dbotthepony.mc.otm.core.immutableList
|
import ru.dbotthepony.mc.otm.core.immutableList
|
||||||
import ru.dbotthepony.mc.otm.core.isNotEmpty
|
import ru.dbotthepony.mc.otm.core.isNotEmpty
|
||||||
import ru.dbotthepony.mc.otm.core.nbt.set
|
|
||||||
import ru.dbotthepony.mc.otm.core.registryName
|
|
||||||
import ru.dbotthepony.mc.otm.data.minRange
|
import ru.dbotthepony.mc.otm.data.minRange
|
||||||
import ru.dbotthepony.mc.otm.network.StreamCodecs
|
import ru.dbotthepony.mc.otm.network.StreamCodecs
|
||||||
import ru.dbotthepony.mc.otm.network.syncher.IRemoteState
|
|
||||||
import ru.dbotthepony.mc.otm.network.syncher.ISynchable
|
import ru.dbotthepony.mc.otm.network.syncher.ISynchable
|
||||||
import ru.dbotthepony.mc.otm.network.syncher.SynchableObservedDelegate
|
import ru.dbotthepony.mc.otm.network.syncher.SynchableObservedDelegate
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
@ -59,6 +59,7 @@ import java.util.concurrent.Callable
|
|||||||
import java.util.concurrent.Future
|
import java.util.concurrent.Future
|
||||||
import java.util.function.Consumer
|
import java.util.function.Consumer
|
||||||
import java.util.function.Supplier
|
import java.util.function.Supplier
|
||||||
|
import java.util.random.RandomGenerator
|
||||||
import java.util.stream.Stream
|
import java.util.stream.Stream
|
||||||
import java.util.stream.StreamSupport
|
import java.util.stream.StreamSupport
|
||||||
import kotlin.jvm.optionals.getOrNull
|
import kotlin.jvm.optionals.getOrNull
|
||||||
@ -154,6 +155,24 @@ fun <T : Enum<T>> T.prev(values: Array<out T>): T {
|
|||||||
return values[next]
|
return values[next]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun IntArray.shuffle(random: RandomGenerator) {
|
||||||
|
for (i in lastIndex downTo 1) {
|
||||||
|
val j = random.nextInt(i + 1)
|
||||||
|
val copy = this[i]
|
||||||
|
this[i] = this[j]
|
||||||
|
this[j] = copy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> MutableList<T>.shuffle(random: RandomGenerator) {
|
||||||
|
for (i in lastIndex downTo 1) {
|
||||||
|
val j = random.nextInt(i + 1)
|
||||||
|
val copy = this[i]
|
||||||
|
this[i] = this[j]
|
||||||
|
this[j] = copy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inline fun <T : Any> immutableList(size: Int, initializer: (index: Int) -> T): ImmutableList<T> {
|
inline fun <T : Any> immutableList(size: Int, initializer: (index: Int) -> T): ImmutableList<T> {
|
||||||
require(size >= 0) { "Invalid list size $size" }
|
require(size >= 0) { "Invalid list size $size" }
|
||||||
|
|
||||||
|
37
src/main/kotlin/ru/dbotthepony/mc/otm/core/IDAllocator.kt
Normal file
37
src/main/kotlin/ru/dbotthepony/mc/otm/core/IDAllocator.kt
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.core
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntAVLTreeSet
|
||||||
|
|
||||||
|
class IDAllocator {
|
||||||
|
private var highestID = 0
|
||||||
|
private val gaps = IntAVLTreeSet()
|
||||||
|
|
||||||
|
fun allocate(): Int {
|
||||||
|
if (gaps.isEmpty()) {
|
||||||
|
return highestID++
|
||||||
|
} else {
|
||||||
|
return gaps.removeFirst()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun release(id: Int) {
|
||||||
|
if (id >= 0) {
|
||||||
|
throw IllegalArgumentException("Invalid ID: $id")
|
||||||
|
} else if (id >= highestID) {
|
||||||
|
throw IllegalArgumentException("Not tracking ID: $id (highest known ID is ${highestID - 1})")
|
||||||
|
} else if (id in gaps) {
|
||||||
|
throw IllegalArgumentException("ID is already free: $id")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id + 1 == highestID) {
|
||||||
|
highestID--
|
||||||
|
} else {
|
||||||
|
gaps.add(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun reset() {
|
||||||
|
highestID = 0
|
||||||
|
gaps.clear()
|
||||||
|
}
|
||||||
|
}
|
@ -1,988 +0,0 @@
|
|||||||
package ru.dbotthepony.mc.otm.core
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList
|
|
||||||
import com.google.common.collect.ImmutableSet
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
|
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectSets
|
|
||||||
import it.unimi.dsi.fastutil.objects.Reference2IntMap
|
|
||||||
import it.unimi.dsi.fastutil.objects.Reference2IntMaps
|
|
||||||
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap
|
|
||||||
import net.minecraft.core.BlockPos
|
|
||||||
import net.minecraft.core.Direction
|
|
||||||
import net.minecraft.core.SectionPos
|
|
||||||
import net.minecraft.core.Vec3i
|
|
||||||
import net.minecraft.tags.TagKey
|
|
||||||
import net.minecraft.world.level.Level
|
|
||||||
import net.minecraft.world.level.LevelAccessor
|
|
||||||
import net.minecraft.world.level.block.Block
|
|
||||||
import net.minecraft.world.level.block.Rotation
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity
|
|
||||||
import net.minecraft.world.level.block.state.BlockState
|
|
||||||
import net.minecraft.world.level.chunk.status.ChunkStatus
|
|
||||||
import ru.dbotthepony.mc.otm.core.collect.SupplierList
|
|
||||||
import ru.dbotthepony.mc.otm.core.collect.WeakHashSet
|
|
||||||
import ru.dbotthepony.mc.otm.core.collect.collect
|
|
||||||
import ru.dbotthepony.mc.otm.core.collect.map
|
|
||||||
import ru.dbotthepony.mc.otm.core.math.RelativeSide
|
|
||||||
import ru.dbotthepony.mc.otm.core.math.plus
|
|
||||||
import java.util.*
|
|
||||||
import java.util.function.Predicate
|
|
||||||
import kotlin.collections.HashMap
|
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
private val blockEntityListeners = WeakHashMap<Level, WeakHashMap<BlockEntity, WeakHashSet<Multiblock>>>()
|
|
||||||
|
|
||||||
fun onBlockEntityInvalidated(blockEntity: BlockEntity) {
|
|
||||||
blockEntityListeners[blockEntity.level]?.get(blockEntity)?.forEach { it.blockEntityRemoved(blockEntity) }
|
|
||||||
blockEntityListeners[blockEntity.level]?.remove(blockEntity)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun interface BlockPredicate {
|
|
||||||
fun test(pos: BlockPos, access: LevelAccessor): Boolean
|
|
||||||
|
|
||||||
fun rotate(rotation: Rotation): BlockPredicate {
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
fun and(other: BlockPredicate): BlockPredicate {
|
|
||||||
return BlockPredicate { pos, access -> test(pos, access) && other.test(pos, access) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun or(other: BlockPredicate): BlockPredicate {
|
|
||||||
return BlockPredicate { pos, access -> test(pos, access) || other.test(pos, access) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun and(other: BlockPredicate, vararg others: BlockPredicate): BlockPredicate {
|
|
||||||
return And(ImmutableSet.copyOf(listOf(other, *others)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun or(other: BlockPredicate, vararg others: BlockPredicate): BlockPredicate {
|
|
||||||
return Or(ImmutableSet.copyOf(listOf(other, *others)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun offset(offset: BlockPos): Positioned {
|
|
||||||
return Positioned(offset, this)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun offset(offset: Vec3i): Positioned {
|
|
||||||
return offset(BlockPos(offset))
|
|
||||||
}
|
|
||||||
|
|
||||||
data class Positioned(val offset: BlockPos, val parent: BlockPredicate) : BlockPredicate {
|
|
||||||
override fun test(pos: BlockPos, access: LevelAccessor): Boolean {
|
|
||||||
return parent.test(offset + pos, access)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun offset(offset: BlockPos): Positioned {
|
|
||||||
return Positioned(this.offset + offset, parent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class And(val nodes: ImmutableSet<BlockPredicate>) : BlockPredicate {
|
|
||||||
constructor(vararg nodes: BlockPredicate) : this(ImmutableSet.copyOf(nodes))
|
|
||||||
constructor(nodes: Set<BlockPredicate>) : this(ImmutableSet.copyOf(nodes))
|
|
||||||
|
|
||||||
override fun test(pos: BlockPos, access: LevelAccessor): Boolean {
|
|
||||||
return nodes.all { it.test(pos, access) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class Or(val nodes: ImmutableSet<BlockPredicate>) : BlockPredicate {
|
|
||||||
constructor(vararg nodes: BlockPredicate) : this(ImmutableSet.copyOf(nodes))
|
|
||||||
constructor(nodes: Set<BlockPredicate>) : this(ImmutableSet.copyOf(nodes))
|
|
||||||
|
|
||||||
override fun test(pos: BlockPos, access: LevelAccessor): Boolean {
|
|
||||||
return nodes.any { it.test(pos, access) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object Air : BlockPredicate {
|
|
||||||
override fun test(pos: BlockPos, access: LevelAccessor): Boolean {
|
|
||||||
return access.getBlockStateNow(pos).isAir
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object NotAir : BlockPredicate {
|
|
||||||
override fun test(pos: BlockPos, access: LevelAccessor): Boolean {
|
|
||||||
return !access.getBlockStateNow(pos).isAir
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fun multiblockConfiguration(configurator: MultiblockBuilder.Node.() -> Unit): MultiblockFactory {
|
|
||||||
val builder = MultiblockBuilder()
|
|
||||||
configurator.invoke(builder.root())
|
|
||||||
return builder.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fun <reified T : BlockEntity> multiblockEntity(): MultiblockBuilder.EntityTag<T> {
|
|
||||||
return MultiblockBuilder.EntityTag(T::class)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val GLOBAL_BLOCK_TAG = Any()
|
|
||||||
|
|
||||||
class MultiblockBuilder {
|
|
||||||
private val nodes = Object2ObjectOpenHashMap<BlockPos, Node>()
|
|
||||||
private val customChecks = ArrayList<Predicate<Multiblock>>()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tags specific [T] block entities to be exposed through [Multiblock.blockEntities] when specific [Part]s are tagged using [Part.tag]
|
|
||||||
*/
|
|
||||||
class EntityTag<T : BlockEntity>(val clazz: KClass<T>, val predicate: Predicate<in T> = Predicate { true }) : Predicate<BlockEntity> {
|
|
||||||
override fun test(t: BlockEntity): Boolean {
|
|
||||||
return clazz.isInstance(t) && predicate.test(t as T)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class Strategy {
|
|
||||||
OR, AND
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds custom [predicate] which determines whenever selected multiblock configuration is valid
|
|
||||||
*
|
|
||||||
* Code inside predicate can safely call [Multiblock.blocks], [Multiblock.blockEntities], etc to determine whenever
|
|
||||||
* all demands are met
|
|
||||||
*/
|
|
||||||
fun customCheck(predicate: Predicate<Multiblock>): MultiblockBuilder {
|
|
||||||
customChecks.add(predicate)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("unchecked_cast")
|
|
||||||
abstract inner class Part<T : Part<T>> {
|
|
||||||
val predicates = ObjectArraySet<BlockPredicate>()
|
|
||||||
val children = ObjectArraySet<Part<*>>()
|
|
||||||
|
|
||||||
val builder: MultiblockBuilder
|
|
||||||
get() = this@MultiblockBuilder
|
|
||||||
|
|
||||||
val blockStateTags = ObjectArraySet<Any>()
|
|
||||||
val blockTags = ObjectArraySet<Any>()
|
|
||||||
val blockEntityTags = ObjectArraySet<EntityTag<*>>()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Marks this node report its block in [Multiblock.blockStates] method when called with specified [value]
|
|
||||||
*
|
|
||||||
* [value] is searched using "value" semantics (`==`)
|
|
||||||
*/
|
|
||||||
fun tagBlockState(value: Any): T {
|
|
||||||
blockStateTags.add(value)
|
|
||||||
return this as T
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Marks this node report its block in [Multiblock.blockStates] method when called without arguments
|
|
||||||
*/
|
|
||||||
fun tagBlockState(): T {
|
|
||||||
blockStateTags.add(GLOBAL_BLOCK_TAG)
|
|
||||||
return this as T
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Marks this node report its block in [Multiblock.blocks] method when called with specified [value]
|
|
||||||
*
|
|
||||||
* [value] is searched for using "value" semantics (`==`)
|
|
||||||
*/
|
|
||||||
fun tagBlock(value: Any): T {
|
|
||||||
blockTags.add(value)
|
|
||||||
return this as T
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Marks this node report its block in [Multiblock.blocks] method when called without arguments
|
|
||||||
*/
|
|
||||||
fun tagBlock(): T {
|
|
||||||
blockTags.add(GLOBAL_BLOCK_TAG)
|
|
||||||
return this as T
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Marks this node to report block entities put at its position when called [Multiblock.blockEntities] with specified [value]
|
|
||||||
*
|
|
||||||
* [value] is searched for using "identity" semantics (`===`)
|
|
||||||
*/
|
|
||||||
fun tag(value: EntityTag<*>): T {
|
|
||||||
blockEntityTags.add(value)
|
|
||||||
return this as T
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes all predicates, custom and prebuilt alike
|
|
||||||
*/
|
|
||||||
fun clear(): T {
|
|
||||||
predicates.clear()
|
|
||||||
return this as T
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a custom predicate on this node's position
|
|
||||||
*/
|
|
||||||
fun predicate(predicate: BlockPredicate): T {
|
|
||||||
predicates.add(predicate)
|
|
||||||
return this as T
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a block predicate, matching any valid blockstate of [block] on this node's position
|
|
||||||
*/
|
|
||||||
fun block(block: Block): T {
|
|
||||||
predicates.add { pos, access ->
|
|
||||||
// use getChunk directly because we don't want to chunkload
|
|
||||||
access.getBlockStateNow(pos).block == block
|
|
||||||
}
|
|
||||||
|
|
||||||
return this as T
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a block tag predicate on this node's position
|
|
||||||
*/
|
|
||||||
fun block(block: TagKey<Block>): T {
|
|
||||||
predicates.add { pos, access ->
|
|
||||||
// use getChunk directly because we don't want to chunkload
|
|
||||||
access.getBlockStateNow(pos).`is`(block)
|
|
||||||
}
|
|
||||||
|
|
||||||
return this as T
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds "nothing" predicate (no block should be present) on this node's position
|
|
||||||
*/
|
|
||||||
fun air(): T {
|
|
||||||
predicate(BlockPredicate.Air)
|
|
||||||
return this as T
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a blockstate predicate, matching exactly provided [state] on this node's position
|
|
||||||
*/
|
|
||||||
fun block(state: BlockState): T {
|
|
||||||
predicates.add { pos, access ->
|
|
||||||
// use getChunk directly because we don't want to chunkload
|
|
||||||
access.getChunk(SectionPos.blockToSectionCoord(pos.x), SectionPos.blockToSectionCoord(pos.z), ChunkStatus.FULL, false)?.getBlockState(pos) == state
|
|
||||||
}
|
|
||||||
|
|
||||||
return this as T
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates "and" subnode, which requires all its children to test true
|
|
||||||
*
|
|
||||||
* @see And
|
|
||||||
*/
|
|
||||||
fun and(): And<T> {
|
|
||||||
return And(this as T)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates "or" subnode, which requires at least one of its children to test true
|
|
||||||
*
|
|
||||||
* @see or
|
|
||||||
*/
|
|
||||||
fun or(): Or<T> {
|
|
||||||
return Or(this as T)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates "and" subnode, which requires all its children to test true
|
|
||||||
*
|
|
||||||
* @see And
|
|
||||||
*/
|
|
||||||
fun and(configurator: And<T>.() -> Unit): And<T> {
|
|
||||||
return And(this as T).also(configurator)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates "or" subnode, which requires at least one of its children to test true
|
|
||||||
*
|
|
||||||
* @see or
|
|
||||||
*/
|
|
||||||
fun or(configurator: Or<T>.() -> Unit): Or<T> {
|
|
||||||
return Or(this as T).also(configurator)
|
|
||||||
}
|
|
||||||
|
|
||||||
protected fun build(pos: BlockPos): MultiblockFactory.Part {
|
|
||||||
return MultiblockFactory.Part(
|
|
||||||
pos, strategy, ImmutableList.copyOf(predicates),
|
|
||||||
children.stream().map { it.build(pos) }.collect(ImmutableList.toImmutableList()),
|
|
||||||
ImmutableSet.copyOf(blockStateTags),
|
|
||||||
ImmutableSet.copyOf(blockTags),
|
|
||||||
ImmutableSet.copyOf(blockEntityTags),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract val strategy: Strategy
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Logical AND node, which tests `true` if all of its children test `true`
|
|
||||||
*/
|
|
||||||
inner class And<P : Part<P>>(val parent: P) : Part<And<P>>() {
|
|
||||||
init {
|
|
||||||
parent.children.add(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun end() = parent
|
|
||||||
|
|
||||||
override val strategy: Strategy
|
|
||||||
get() = Strategy.AND
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Logical OR node, which tests `true` if at least one of its children test `true`
|
|
||||||
*
|
|
||||||
* This is default behavior of newly created nodes, including the multiblock root node
|
|
||||||
*/
|
|
||||||
inner class Or<P : Part<P>>(val parent: P) : Part<Or<P>>() {
|
|
||||||
init {
|
|
||||||
parent.children.add(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun end() = parent
|
|
||||||
|
|
||||||
override val strategy: Strategy
|
|
||||||
get() = Strategy.OR
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Behaves as if being [Or] node
|
|
||||||
*/
|
|
||||||
inner class Node(val pos: BlockPos) : Part<Node>() {
|
|
||||||
init {
|
|
||||||
check(nodes.put(pos, this) == null) { "Trying to create new node at $pos while already having one" }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates new node relative to this node at [diff]
|
|
||||||
*/
|
|
||||||
fun relative(diff: Vec3i): Node {
|
|
||||||
return node(pos + diff)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates new node relative to this node at [dir] side
|
|
||||||
*/
|
|
||||||
fun relative(dir: Direction): Node {
|
|
||||||
return relative(dir.normal)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates new node relative to this node at [dir] side
|
|
||||||
*/
|
|
||||||
fun relative(dir: RelativeSide): Node {
|
|
||||||
return relative(dir.default)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates new node relative to this node at [diff], and configures it in-place using provided [configurator] expression
|
|
||||||
*/
|
|
||||||
fun relative(diff: Vec3i, configurator: Node.() -> Unit): Node {
|
|
||||||
return node(pos + diff).also(configurator)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates new node relative to this node at [dir] side, and configures it in-place using provided [configurator] expression
|
|
||||||
*/
|
|
||||||
fun relative(dir: Direction, configurator: Node.() -> Unit): Node {
|
|
||||||
return relative(dir.normal).also(configurator)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates new node relative to this node at [dir] side, and configures it in-place using provided [configurator] expression
|
|
||||||
*/
|
|
||||||
fun relative(dir: RelativeSide, configurator: Node.() -> Unit): Node {
|
|
||||||
return relative(dir.default).also(configurator)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun front() = relative(RelativeSide.FRONT)
|
|
||||||
fun back() = relative(RelativeSide.BACK)
|
|
||||||
fun left() = relative(RelativeSide.LEFT)
|
|
||||||
fun right() = relative(RelativeSide.RIGHT)
|
|
||||||
fun top() = relative(RelativeSide.TOP)
|
|
||||||
fun bottom() = relative(RelativeSide.BOTTOM)
|
|
||||||
|
|
||||||
fun front(block: Block) = relative(RelativeSide.FRONT).also { it.block(block) }
|
|
||||||
fun back(block: Block) = relative(RelativeSide.BACK).also { it.block(block) }
|
|
||||||
fun left(block: Block) = relative(RelativeSide.LEFT).also { it.block(block) }
|
|
||||||
fun right(block: Block) = relative(RelativeSide.RIGHT).also { it.block(block) }
|
|
||||||
fun top(block: Block) = relative(RelativeSide.TOP).also { it.block(block) }
|
|
||||||
fun bottom(block: Block) = relative(RelativeSide.BOTTOM).also { it.block(block) }
|
|
||||||
|
|
||||||
fun front(block: BlockState) = relative(RelativeSide.FRONT).also { it.block(block) }
|
|
||||||
fun back(block: BlockState) = relative(RelativeSide.BACK).also { it.block(block) }
|
|
||||||
fun left(block: BlockState) = relative(RelativeSide.LEFT).also { it.block(block) }
|
|
||||||
fun right(block: BlockState) = relative(RelativeSide.RIGHT).also { it.block(block) }
|
|
||||||
fun top(block: BlockState) = relative(RelativeSide.TOP).also { it.block(block) }
|
|
||||||
fun bottom(block: BlockState) = relative(RelativeSide.BOTTOM).also { it.block(block) }
|
|
||||||
|
|
||||||
fun front(predicate: BlockPredicate) = relative(RelativeSide.FRONT).also { it.predicate(predicate) }
|
|
||||||
fun back(predicate: BlockPredicate) = relative(RelativeSide.BACK).also { it.predicate(predicate) }
|
|
||||||
fun left(predicate: BlockPredicate) = relative(RelativeSide.LEFT).also { it.predicate(predicate) }
|
|
||||||
fun right(predicate: BlockPredicate) = relative(RelativeSide.RIGHT).also { it.predicate(predicate) }
|
|
||||||
fun top(predicate: BlockPredicate) = relative(RelativeSide.TOP).also { it.predicate(predicate) }
|
|
||||||
fun bottom(predicate: BlockPredicate) = relative(RelativeSide.BOTTOM).also { it.predicate(predicate) }
|
|
||||||
|
|
||||||
fun front(configurator: Node.() -> Unit) = relative(RelativeSide.FRONT).also(configurator)
|
|
||||||
fun back(configurator: Node.() -> Unit) = relative(RelativeSide.BACK).also(configurator)
|
|
||||||
fun left(configurator: Node.() -> Unit) = relative(RelativeSide.LEFT).also(configurator)
|
|
||||||
fun right(configurator: Node.() -> Unit) = relative(RelativeSide.RIGHT).also(configurator)
|
|
||||||
fun top(configurator: Node.() -> Unit) = relative(RelativeSide.TOP).also(configurator)
|
|
||||||
fun bottom(configurator: Node.() -> Unit) = relative(RelativeSide.BOTTOM).also(configurator)
|
|
||||||
|
|
||||||
fun front(block: Block, configurator: Node.() -> Unit) = relative(RelativeSide.FRONT).also { it.block(block) }.also(configurator)
|
|
||||||
fun back(block: Block, configurator: Node.() -> Unit) = relative(RelativeSide.BACK).also { it.block(block) }.also(configurator)
|
|
||||||
fun left(block: Block, configurator: Node.() -> Unit) = relative(RelativeSide.LEFT).also { it.block(block) }.also(configurator)
|
|
||||||
fun right(block: Block, configurator: Node.() -> Unit) = relative(RelativeSide.RIGHT).also { it.block(block) }.also(configurator)
|
|
||||||
fun top(block: Block, configurator: Node.() -> Unit) = relative(RelativeSide.TOP).also { it.block(block) }.also(configurator)
|
|
||||||
fun bottom(block: Block, configurator: Node.() -> Unit) = relative(RelativeSide.BOTTOM).also { it.block(block) }.also(configurator)
|
|
||||||
|
|
||||||
fun front(block: BlockState, configurator: Node.() -> Unit) = relative(RelativeSide.FRONT).also { it.block(block) }.also(configurator)
|
|
||||||
fun back(block: BlockState, configurator: Node.() -> Unit) = relative(RelativeSide.BACK).also { it.block(block) }.also(configurator)
|
|
||||||
fun left(block: BlockState, configurator: Node.() -> Unit) = relative(RelativeSide.LEFT).also { it.block(block) }.also(configurator)
|
|
||||||
fun right(block: BlockState, configurator: Node.() -> Unit) = relative(RelativeSide.RIGHT).also { it.block(block) }.also(configurator)
|
|
||||||
fun top(block: BlockState, configurator: Node.() -> Unit) = relative(RelativeSide.TOP).also { it.block(block) }.also(configurator)
|
|
||||||
fun bottom(block: BlockState, configurator: Node.() -> Unit) = relative(RelativeSide.BOTTOM).also { it.block(block) }.also(configurator)
|
|
||||||
|
|
||||||
fun front(predicate: BlockPredicate, configurator: Node.() -> Unit) = relative(RelativeSide.FRONT).also { it.predicate(predicate) }.also(configurator)
|
|
||||||
fun back(predicate: BlockPredicate, configurator: Node.() -> Unit) = relative(RelativeSide.BACK).also { it.predicate(predicate) }.also(configurator)
|
|
||||||
fun left(predicate: BlockPredicate, configurator: Node.() -> Unit) = relative(RelativeSide.LEFT).also { it.predicate(predicate) }.also(configurator)
|
|
||||||
fun right(predicate: BlockPredicate, configurator: Node.() -> Unit) = relative(RelativeSide.RIGHT).also { it.predicate(predicate) }.also(configurator)
|
|
||||||
fun top(predicate: BlockPredicate, configurator: Node.() -> Unit) = relative(RelativeSide.TOP).also { it.predicate(predicate) }.also(configurator)
|
|
||||||
fun bottom(predicate: BlockPredicate, configurator: Node.() -> Unit) = relative(RelativeSide.BOTTOM).also { it.predicate(predicate) }.also(configurator)
|
|
||||||
|
|
||||||
fun build(): MultiblockFactory.Part {
|
|
||||||
return build(pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
override var strategy: Strategy = Strategy.OR
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates new (or returns existing one) node at specified position and returns it
|
|
||||||
*/
|
|
||||||
fun node(at: BlockPos): Node {
|
|
||||||
return nodes[at] ?: Node(at)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns node which represents multiblock "root" position (relative position of 0 0 0)
|
|
||||||
*/
|
|
||||||
fun root() = node(BlockPos.ZERO)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns node which represents multiblock "root" position (relative position of 0 0 0), and configures it using provided [configurator] expression
|
|
||||||
*/
|
|
||||||
fun root(configurator: Node.() -> Unit) = node(BlockPos.ZERO).also(configurator)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Makes a deep copy of this [MultiblockBuilder], which can be modified independently of this builder
|
|
||||||
*/
|
|
||||||
fun copy(): MultiblockBuilder {
|
|
||||||
val copied = MultiblockBuilder()
|
|
||||||
|
|
||||||
for ((k, v) in nodes) {
|
|
||||||
val node = copied.Node(k)
|
|
||||||
copied.nodes[k] = node
|
|
||||||
node.predicates.addAll(v.predicates)
|
|
||||||
}
|
|
||||||
|
|
||||||
return copied
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates [MultiblockFactory] out of this [MultiblockBuilder].
|
|
||||||
*
|
|
||||||
* Created factory does not share reference(s) to this builder, and this builder can be mutated further without consequences.
|
|
||||||
*/
|
|
||||||
fun build(): MultiblockFactory {
|
|
||||||
return MultiblockFactory(nodes.values.iterator().map { it.build() }.collect(ImmutableSet.toImmutableSet()), ImmutableList.copyOf(customChecks))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class MultiblockFactory(val north: ImmutableSet<Part>, val customChecks: ImmutableList<Predicate<Multiblock>>) {
|
|
||||||
data class Part(
|
|
||||||
val pos: BlockPos,
|
|
||||||
val strategy: MultiblockBuilder.Strategy,
|
|
||||||
val predicate: ImmutableList<BlockPredicate>,
|
|
||||||
val children: ImmutableList<Part>,
|
|
||||||
val blockStateTags: ImmutableSet<Any>,
|
|
||||||
val blockTags: ImmutableSet<Any>,
|
|
||||||
val blockEntityTags: ImmutableSet<MultiblockBuilder.EntityTag<*>>,
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bakes multiblock configuration with specified [pos] as multiblock's root position
|
|
||||||
*/
|
|
||||||
fun create(pos: BlockPos): Multiblock {
|
|
||||||
return Multiblock(pos, this)
|
|
||||||
}
|
|
||||||
|
|
||||||
val south: ImmutableSet<Part> = north.iterator().map { it.copy(pos = it.pos.rotate(Rotation.CLOCKWISE_180)) }.collect(ImmutableSet.toImmutableSet())
|
|
||||||
val west: ImmutableSet<Part> = north.iterator().map { it.copy(pos = it.pos.rotate(Rotation.COUNTERCLOCKWISE_90)) }.collect(ImmutableSet.toImmutableSet())
|
|
||||||
val east: ImmutableSet<Part> = north.iterator().map { it.copy(pos = it.pos.rotate(Rotation.CLOCKWISE_90)) }.collect(ImmutableSet.toImmutableSet())
|
|
||||||
}
|
|
||||||
|
|
||||||
class Multiblock(pos: BlockPos, factory: MultiblockFactory) {
|
|
||||||
var isValid = false
|
|
||||||
private set
|
|
||||||
|
|
||||||
private val customChecks = factory.customChecks
|
|
||||||
private var collectFailedParts = false
|
|
||||||
private val north by lazy(LazyThreadSafetyMode.NONE) { Config(Direction.NORTH, pos, factory.north) }
|
|
||||||
private val south by lazy(LazyThreadSafetyMode.NONE) { Config(Direction.SOUTH, pos, factory.south) }
|
|
||||||
private val west by lazy(LazyThreadSafetyMode.NONE) { Config(Direction.WEST, pos, factory.west) }
|
|
||||||
private val east by lazy(LazyThreadSafetyMode.NONE) { Config(Direction.EAST, pos, factory.east) }
|
|
||||||
private val configurations = SupplierList(::north, ::south, ::west, ::east)
|
|
||||||
|
|
||||||
private inner class BEList<T : BlockEntity>(val tag: MultiblockBuilder.EntityTag<T>) {
|
|
||||||
val list = ArrayList<T>()
|
|
||||||
val set = ObjectArraySet<T>()
|
|
||||||
val setView: Set<T> = ObjectSets.unmodifiable(set)
|
|
||||||
|
|
||||||
fun add(blockEntity: BlockEntity) {
|
|
||||||
if (tag.test(blockEntity)) {
|
|
||||||
if (set.add(blockEntity as T)) {
|
|
||||||
blockEntityListeners
|
|
||||||
.computeIfAbsent(blockEntity.level) { WeakHashMap() }
|
|
||||||
.computeIfAbsent(blockEntity) { WeakHashSet() }
|
|
||||||
.add(this@Multiblock)
|
|
||||||
}
|
|
||||||
|
|
||||||
list.add(blockEntity)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun remove(blockEntity: BlockEntity) {
|
|
||||||
if (tag.test(blockEntity)) {
|
|
||||||
check(list.remove(blockEntity as T)) { "Unable to remove $blockEntity from tag $tag" }
|
|
||||||
|
|
||||||
if (blockEntity !in list) {
|
|
||||||
set.remove(blockEntity)
|
|
||||||
|
|
||||||
val getSet = blockEntityListeners[blockEntity.level]?.get(blockEntity)
|
|
||||||
|
|
||||||
if (getSet != null) {
|
|
||||||
getSet.remove(this@Multiblock)
|
|
||||||
|
|
||||||
if (getSet.isEmpty()) {
|
|
||||||
blockEntityListeners[blockEntity.level]?.remove(blockEntity)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun blockEntityRemoved(blockEntity: BlockEntity): Boolean {
|
|
||||||
if (blockEntity in list) {
|
|
||||||
while (list.remove(blockEntity)) {}
|
|
||||||
set.remove(blockEntity)
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
fun clear() {
|
|
||||||
set.forEach {
|
|
||||||
val getSet = checkNotNull(blockEntityListeners[it.level]) { "Consistency check failed: No subscriber lists for level ${it.level}" }.get(it)
|
|
||||||
checkNotNull(getSet) { "Consistency check failed: No subscriber list for $it" }
|
|
||||||
check(getSet.remove(this@Multiblock)) { "Consistency check failed: Can't remove ${this@Multiblock} from $it subscriber list" }
|
|
||||||
|
|
||||||
if (getSet.isEmpty()) {
|
|
||||||
blockEntityListeners[it.level]?.remove(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
list.clear()
|
|
||||||
set.clear()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private inner class Config(val direction: Direction, val pos: BlockPos, parts: Collection<MultiblockFactory.Part>) {
|
|
||||||
private inner class Part(val pos: BlockPos, val prototype: MultiblockFactory.Part) : Comparable<Part> {
|
|
||||||
private var blockEntity: BlockEntity? = null
|
|
||||||
private var blockState: BlockState? = null
|
|
||||||
private val assignedBlockEntityLists = ArrayList<BEList<*>>(prototype.blockEntityTags.size)
|
|
||||||
private val assignedBlockStateLists = ArrayList<Reference2IntMap<BlockState>>()
|
|
||||||
private val assignedBlockLists = ArrayList<Reference2IntMap<Block>>()
|
|
||||||
private val children: ImmutableList<Part> by lazy(LazyThreadSafetyMode.NONE) { prototype.children.stream().map { Part(pos, it) }.collect(ImmutableList.toImmutableList()) }
|
|
||||||
|
|
||||||
override fun compareTo(other: Part): Int {
|
|
||||||
val cmp = SectionPos.blockToSectionCoord(pos.x).compareTo(SectionPos.blockToSectionCoord(other.pos.x))
|
|
||||||
if (cmp != 0) return cmp
|
|
||||||
return SectionPos.blockToSectionCoord(pos.z).compareTo(SectionPos.blockToSectionCoord(other.pos.z))
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
prototype.blockEntityTags.forEach {
|
|
||||||
assignedBlockEntityLists.add(getBlockEntityList(it))
|
|
||||||
}
|
|
||||||
|
|
||||||
prototype.blockStateTags.forEach {
|
|
||||||
assignedBlockStateLists.add(getBlockStateList(it))
|
|
||||||
}
|
|
||||||
|
|
||||||
prototype.blockTags.forEach {
|
|
||||||
assignedBlockLists.add(getBlockList(it))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var lastSuccessfulPathPredicate = -1
|
|
||||||
private var lastSuccessfulPathChildren = -1
|
|
||||||
|
|
||||||
fun test(levelAccessor: LevelAccessor): Boolean {
|
|
||||||
val test = when (prototype.strategy) {
|
|
||||||
MultiblockBuilder.Strategy.OR -> {
|
|
||||||
var status = true
|
|
||||||
|
|
||||||
if (prototype.predicate.isNotEmpty()) {
|
|
||||||
if (lastSuccessfulPathPredicate != -1 && !prototype.predicate[lastSuccessfulPathPredicate].test(pos, levelAccessor)) {
|
|
||||||
lastSuccessfulPathPredicate = -1
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lastSuccessfulPathPredicate == -1) {
|
|
||||||
lastSuccessfulPathPredicate = prototype.predicate.indexOfFirst { it.test(pos, levelAccessor) }
|
|
||||||
status = lastSuccessfulPathPredicate != -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status && children.isNotEmpty()) {
|
|
||||||
if (lastSuccessfulPathChildren != -1 && !children[lastSuccessfulPathChildren].test(levelAccessor)) {
|
|
||||||
lastSuccessfulPathChildren = -1
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lastSuccessfulPathChildren == -1) {
|
|
||||||
lastSuccessfulPathChildren = children.indexOfFirst { it.test(levelAccessor) }
|
|
||||||
status = lastSuccessfulPathChildren != -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
status
|
|
||||||
}
|
|
||||||
|
|
||||||
MultiblockBuilder.Strategy.AND -> prototype.predicate.all { it.test(pos, levelAccessor) } && children.all { it.test(levelAccessor) }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (test) {
|
|
||||||
if (assignedBlockEntityLists.isNotEmpty()) {
|
|
||||||
val be1 = blockEntity
|
|
||||||
val be2 = levelAccessor.getBlockEntity(pos)
|
|
||||||
|
|
||||||
if (be1 != be2) {
|
|
||||||
if (be1 != null) {
|
|
||||||
assignedBlockEntityLists.forEach { it.remove(be1) }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (be2 != null) {
|
|
||||||
assignedBlockEntityLists.forEach { it.add(be2) }
|
|
||||||
}
|
|
||||||
|
|
||||||
blockEntity = be2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (assignedBlockStateLists.isNotEmpty() || assignedBlockLists.isNotEmpty()) {
|
|
||||||
val state = levelAccessor.getBlockState(pos)
|
|
||||||
val old = blockState
|
|
||||||
|
|
||||||
if (state != old) {
|
|
||||||
if (old != null) {
|
|
||||||
assignedBlockStateLists.forEach {
|
|
||||||
it[old] = it.getInt(old) - 1
|
|
||||||
check(it.getInt(old) >= 0) { "Consistency check failed: Counter for block state $old turned negative" }
|
|
||||||
}
|
|
||||||
|
|
||||||
assignedBlockLists.forEach {
|
|
||||||
it[old.block] = it.getInt(old.block) - 1
|
|
||||||
check(it.getInt(old.block) >= 0) { "Consistency check failed: Counter for block ${old.block.registryName} turned negative" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assignedBlockStateLists.forEach {
|
|
||||||
it[state] = it.getInt(state) + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
assignedBlockLists.forEach {
|
|
||||||
it[state.block] = it.getInt(state.block) + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
blockState = state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
clearFull()
|
|
||||||
}
|
|
||||||
|
|
||||||
return test
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun clearFull() {
|
|
||||||
val blockEntity = blockEntity
|
|
||||||
|
|
||||||
if (blockEntity != null) {
|
|
||||||
assignedBlockEntityLists.forEach { it.remove(blockEntity) }
|
|
||||||
}
|
|
||||||
|
|
||||||
val blockState = blockState
|
|
||||||
|
|
||||||
if (blockState != null) {
|
|
||||||
assignedBlockStateLists.forEach {
|
|
||||||
it[blockState] = it.getInt(blockState) - 1
|
|
||||||
check(it.getInt(blockState) >= 0) { "Consistency check failed: Counter for block state $blockState turned negative" }
|
|
||||||
}
|
|
||||||
|
|
||||||
assignedBlockLists.forEach {
|
|
||||||
it[blockState.block] = it.getInt(blockState.block) - 1
|
|
||||||
check(it.getInt(blockState.block) >= 0) { "Consistency check failed: Counter for block ${blockState.block.registryName} turned negative" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.blockEntity = null
|
|
||||||
this.blockState = null
|
|
||||||
|
|
||||||
lastSuccessfulPathPredicate = -1
|
|
||||||
lastSuccessfulPathChildren = -1
|
|
||||||
|
|
||||||
// avoid allocating iterator when empty
|
|
||||||
if (children.isNotEmpty())
|
|
||||||
children.forEach { it.clearFull() }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun clear() {
|
|
||||||
blockEntity = null
|
|
||||||
blockState = null
|
|
||||||
|
|
||||||
lastSuccessfulPathPredicate = -1
|
|
||||||
lastSuccessfulPathChildren = -1
|
|
||||||
|
|
||||||
children.forEach { it.clear() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun <T : BlockEntity> getBlockEntityList(tag: MultiblockBuilder.EntityTag<T>): BEList<T> {
|
|
||||||
val existing = tag2BlockEntity[tag]
|
|
||||||
|
|
||||||
if (existing != null)
|
|
||||||
return existing as BEList<T>
|
|
||||||
|
|
||||||
val new = BEList(tag)
|
|
||||||
blockEntityLists.add(new)
|
|
||||||
tag2BlockEntity[tag] = new
|
|
||||||
return new
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getBlockList(tag: Any): Reference2IntMap<Block> {
|
|
||||||
val existing = tag2Block[tag]
|
|
||||||
|
|
||||||
if (existing != null)
|
|
||||||
return existing
|
|
||||||
|
|
||||||
val new = Reference2IntOpenHashMap<Block>()
|
|
||||||
tag2Block[tag] = new
|
|
||||||
tag2BlockViews[tag] = Reference2IntMaps.unmodifiable(new)
|
|
||||||
blockLists.add(new)
|
|
||||||
return new
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getBlockStateList(tag: Any): Reference2IntMap<BlockState> {
|
|
||||||
val existing = tag2BlockState[tag]
|
|
||||||
|
|
||||||
if (existing != null)
|
|
||||||
return existing
|
|
||||||
|
|
||||||
val new = Reference2IntOpenHashMap<BlockState>()
|
|
||||||
tag2BlockState[tag] = new
|
|
||||||
tag2BlockStateViews[tag] = Reference2IntMaps.unmodifiable(new)
|
|
||||||
blockStateLists.add(new)
|
|
||||||
return new
|
|
||||||
}
|
|
||||||
|
|
||||||
private val tag2BlockEntity = HashMap<MultiblockBuilder.EntityTag<*>, BEList<*>>()
|
|
||||||
private val tag2BlockState = HashMap<Any, Reference2IntMap<BlockState>>()
|
|
||||||
private val tag2Block = HashMap<Any, Reference2IntMap<Block>>()
|
|
||||||
|
|
||||||
private val tag2BlockStateViews = HashMap<Any, Reference2IntMap<BlockState>>()
|
|
||||||
private val tag2BlockViews = HashMap<Any, Reference2IntMap<Block>>()
|
|
||||||
|
|
||||||
private val blockEntityLists = ArrayList<BEList<*>>()
|
|
||||||
private val blockStateLists = ArrayList<Reference2IntMap<BlockState>>()
|
|
||||||
private val blockLists = ArrayList<Reference2IntMap<Block>>()
|
|
||||||
|
|
||||||
private val globalBlocks = getBlockList(GLOBAL_BLOCK_TAG)
|
|
||||||
private val globalBlockStates = getBlockStateList(GLOBAL_BLOCK_TAG)
|
|
||||||
private val globalBlocksView = tag2BlockViews[GLOBAL_BLOCK_TAG]!!
|
|
||||||
private val globalBlockStatesView = tag2BlockStateViews[GLOBAL_BLOCK_TAG]!!
|
|
||||||
|
|
||||||
private val parts: ImmutableList<Part> = parts.stream()
|
|
||||||
.map { Part(it.pos + pos, it) }
|
|
||||||
.sorted() // group/localize parts by in-world chunks to maximize getChunk() cache hit rate
|
|
||||||
.collect(ImmutableList.toImmutableList())
|
|
||||||
|
|
||||||
fun clear() {
|
|
||||||
blockEntityLists.forEach { it.clear() }
|
|
||||||
blockStateLists.forEach { it.clear() }
|
|
||||||
blockLists.forEach { it.clear() }
|
|
||||||
parts.forEach { it.clear() }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun update(levelAccessor: LevelAccessor): Boolean {
|
|
||||||
var valid = true
|
|
||||||
|
|
||||||
for (part in parts) {
|
|
||||||
valid = part.test(levelAccessor)
|
|
||||||
if (!valid) break
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!valid)
|
|
||||||
clear()
|
|
||||||
|
|
||||||
return valid
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T : BlockEntity> blockEntities(tag: MultiblockBuilder.EntityTag<T>): Set<T> {
|
|
||||||
return (tag2BlockEntity[tag]?.setView ?: setOf()) as Set<T>
|
|
||||||
}
|
|
||||||
|
|
||||||
fun blocks(tag: Any): Reference2IntMap<Block> {
|
|
||||||
return tag2BlockViews[tag] ?: Reference2IntMaps.emptyMap()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun blockStates(tag: Any): Reference2IntMap<BlockState> {
|
|
||||||
return tag2BlockStateViews[tag] ?: Reference2IntMaps.emptyMap()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun blocks(): Reference2IntMap<Block> {
|
|
||||||
return globalBlocksView
|
|
||||||
}
|
|
||||||
|
|
||||||
fun blockStates(): Reference2IntMap<BlockState> {
|
|
||||||
return globalBlockStatesView
|
|
||||||
}
|
|
||||||
|
|
||||||
fun blockEntityRemoved(blockEntity: BlockEntity): Boolean {
|
|
||||||
var any = false
|
|
||||||
|
|
||||||
blockEntityLists.forEach {
|
|
||||||
any = it.blockEntityRemoved(blockEntity) || any
|
|
||||||
}
|
|
||||||
|
|
||||||
return any
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var activeConfig: Config? = null
|
|
||||||
|
|
||||||
val currentDirection: Direction?
|
|
||||||
get() = activeConfig?.direction
|
|
||||||
|
|
||||||
fun <T : BlockEntity> blockEntities(tag: MultiblockBuilder.EntityTag<T>): Set<T> {
|
|
||||||
if (!isValid) return emptySet()
|
|
||||||
return activeConfig?.blockEntities(tag) ?: setOf()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns block counts present on nodes tagged by [tag]
|
|
||||||
*/
|
|
||||||
fun blocks(tag: Any): Reference2IntMap<Block> {
|
|
||||||
if (!isValid) return Reference2IntMaps.emptyMap()
|
|
||||||
return activeConfig?.blocks(tag) ?: Reference2IntMaps.emptyMap()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns blockstate counts present on nodes tagged by [tag]
|
|
||||||
*/
|
|
||||||
fun blockStates(tag: Any): Reference2IntMap<BlockState> {
|
|
||||||
if (!isValid) return Reference2IntMaps.emptyMap()
|
|
||||||
return activeConfig?.blockStates(tag) ?: Reference2IntMaps.emptyMap()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns block counts present on nodes tagged by [MultiblockBuilder.Part.tagBlock] without arguments
|
|
||||||
*/
|
|
||||||
fun blocks(): Reference2IntMap<Block> {
|
|
||||||
if (!isValid) return Reference2IntMaps.emptyMap()
|
|
||||||
return activeConfig?.blocks() ?: Reference2IntMaps.emptyMap()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns blockstate counts present on nodes tagged by [MultiblockBuilder.Part.tagBlock] without arguments
|
|
||||||
*/
|
|
||||||
fun blockStates(): Reference2IntMap<BlockState> {
|
|
||||||
if (!isValid) return Reference2IntMaps.emptyMap()
|
|
||||||
return activeConfig?.blockStates() ?: Reference2IntMaps.emptyMap()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun blockEntityRemoved(blockEntity: BlockEntity) {
|
|
||||||
activeConfig?.blockEntityRemoved(blockEntity)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun update(levelAccessor: LevelAccessor): Boolean {
|
|
||||||
val activeConfig = activeConfig
|
|
||||||
|
|
||||||
if (activeConfig != null && activeConfig.update(levelAccessor) && customChecks.all { it.test(this) }) {
|
|
||||||
return true
|
|
||||||
} else if (activeConfig != null) {
|
|
||||||
for (config in configurations) {
|
|
||||||
if (config !== activeConfig && config.update(levelAccessor)) {
|
|
||||||
this.activeConfig = config
|
|
||||||
|
|
||||||
if (customChecks.all { it.test(this) })
|
|
||||||
return true
|
|
||||||
else {
|
|
||||||
config.clear()
|
|
||||||
this.activeConfig = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.activeConfig = null
|
|
||||||
this.isValid = false
|
|
||||||
return false
|
|
||||||
} else {
|
|
||||||
for (config in configurations) {
|
|
||||||
if (config.update(levelAccessor)) {
|
|
||||||
this.activeConfig = config
|
|
||||||
|
|
||||||
if (customChecks.all { it.test(this) }) {
|
|
||||||
this.isValid = true
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
config.clear()
|
|
||||||
this.activeConfig = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun update(levelAccessor: LevelAccessor, direction: Direction): Boolean {
|
|
||||||
if (activeConfig?.direction != direction) {
|
|
||||||
activeConfig?.clear()
|
|
||||||
activeConfig = null
|
|
||||||
}
|
|
||||||
|
|
||||||
val config = when (direction) {
|
|
||||||
Direction.NORTH -> north
|
|
||||||
Direction.SOUTH -> south
|
|
||||||
Direction.WEST -> west
|
|
||||||
Direction.EAST -> east
|
|
||||||
else -> throw IllegalArgumentException(direction.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
activeConfig = config
|
|
||||||
isValid = config.update(levelAccessor) && customChecks.all { it.test(this) }
|
|
||||||
|
|
||||||
if (!isValid) {
|
|
||||||
config.clear()
|
|
||||||
activeConfig = null
|
|
||||||
}
|
|
||||||
|
|
||||||
return isValid
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,93 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.core.chart
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectIterators
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||||
|
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.util.ITickable
|
||||||
|
import ru.dbotthepony.mc.otm.network.syncher.DynamicSynchableGroup
|
||||||
|
import ru.dbotthepony.mc.otm.network.syncher.IRemoteState
|
||||||
|
import ru.dbotthepony.mc.otm.network.syncher.ISynchable
|
||||||
|
import java.util.function.Supplier
|
||||||
|
|
||||||
|
abstract class AbstractCombinedHistoryChart<V : Any, CHART : AbstractHistoryChart<V>>(
|
||||||
|
protected val provider: Supplier<Iterator<CHART>>,
|
||||||
|
final override val width: Int,
|
||||||
|
final override val resolution: Int
|
||||||
|
) : IHistoryChart<V>, ISynchable, ITickable {
|
||||||
|
protected abstract fun factory(stream: RegistryFriendlyByteBuf): CHART
|
||||||
|
protected abstract fun networkMetadata(stream: RegistryFriendlyByteBuf, chart: CHART)
|
||||||
|
private val group = DynamicSynchableGroup(::factory) { networkMetadata(it, this) }
|
||||||
|
protected abstract fun sum(values: List<V>): V
|
||||||
|
|
||||||
|
override val isRemote: Boolean
|
||||||
|
get() = group.isRemote
|
||||||
|
override val hasRemotes: Boolean
|
||||||
|
get() = group.hasRemotes
|
||||||
|
|
||||||
|
override fun tick() {
|
||||||
|
if (group.hasRemotes) {
|
||||||
|
val current = ObjectOpenHashSet(provider.get())
|
||||||
|
group.retainAll(current)
|
||||||
|
|
||||||
|
for (v in current) {
|
||||||
|
if (v !in group) {
|
||||||
|
group.add(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
group.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final override fun iterator(): Iterator<V> {
|
||||||
|
val iterators = if (group.isRemote)
|
||||||
|
group.iterator().map { it.iterator() }.toList()
|
||||||
|
else
|
||||||
|
provider.get()
|
||||||
|
.map { it.iterator() }
|
||||||
|
.filter { it.hasNext() }
|
||||||
|
.toList()
|
||||||
|
|
||||||
|
if (iterators.isEmpty()) {
|
||||||
|
return ObjectIterators.emptyIterator()
|
||||||
|
} else {
|
||||||
|
val buffer = ArrayList<V>(iterators.size)
|
||||||
|
|
||||||
|
return object : Iterator<V> {
|
||||||
|
override fun hasNext(): Boolean {
|
||||||
|
return iterators.isNotEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun next(): V {
|
||||||
|
buffer.clear()
|
||||||
|
|
||||||
|
iterators.removeIf {
|
||||||
|
buffer.add(it.next())
|
||||||
|
!it.hasNext()
|
||||||
|
}
|
||||||
|
|
||||||
|
return sum(buffer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final override fun get(index: Int): V {
|
||||||
|
if (group.isRemote) {
|
||||||
|
return sum(group.map { it[index] })
|
||||||
|
} else {
|
||||||
|
return sum(provider.get().map { it[index] }.toList())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final override fun read(stream: RegistryFriendlyByteBuf) {
|
||||||
|
group.read(stream)
|
||||||
|
}
|
||||||
|
|
||||||
|
final override fun createRemoteState(listener: Runnable): IRemoteState {
|
||||||
|
return group.createRemoteState(listener)
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package ru.dbotthepony.mc.otm.core
|
package ru.dbotthepony.mc.otm.core.chart
|
||||||
|
|
||||||
import com.mojang.serialization.Codec
|
import com.mojang.serialization.Codec
|
||||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||||
@ -14,16 +14,9 @@ import ru.dbotthepony.mc.otm.network.syncher.ISynchable
|
|||||||
import java.util.concurrent.CopyOnWriteArrayList
|
import java.util.concurrent.CopyOnWriteArrayList
|
||||||
|
|
||||||
abstract class AbstractHistoryChart<V : Any>(
|
abstract class AbstractHistoryChart<V : Any>(
|
||||||
/**
|
final override val resolution: Int,
|
||||||
* How many measurements one graph value contains
|
final override val width: Int,
|
||||||
*/
|
) : IHistoryChart<V>, ISynchable, INBTSerializable<CompoundTag> {
|
||||||
val resolution: Int,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Limit on how many graph values to store
|
|
||||||
*/
|
|
||||||
val width: Int,
|
|
||||||
) : Iterable<V>, INBTSerializable<CompoundTag>, ISynchable {
|
|
||||||
constructor(ticks: Int) : this(1, ticks)
|
constructor(ticks: Int) : this(1, ticks)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@ -39,13 +32,13 @@ abstract class AbstractHistoryChart<V : Any>(
|
|||||||
protected abstract fun sum(input: List<V>): V
|
protected abstract fun sum(input: List<V>): V
|
||||||
protected abstract fun identity(): V
|
protected abstract fun identity(): V
|
||||||
|
|
||||||
fun calculateAverage(width: Int = this.width): V {
|
fun calculateAverage(width: Int): V {
|
||||||
require(width >= 0) { "Invalid time frame: $width" }
|
require(width >= 0) { "Invalid time frame: $width" }
|
||||||
|
|
||||||
if (width == 0 || this.values.isEmpty())
|
if (width == 0 || this.values.isEmpty())
|
||||||
return identity()
|
return identity()
|
||||||
|
|
||||||
return calculateAverage(this.values.subList(0, width.coerceAtMost(this.values.size)))
|
return calculateAverage(values.subList(0, width.coerceAtMost(values.size)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun calculateSum(width: Int = this.width): V {
|
fun calculateSum(width: Int = this.width): V {
|
||||||
@ -61,6 +54,11 @@ abstract class AbstractHistoryChart<V : Any>(
|
|||||||
abstract val streamCodec: MatteryStreamCodec<RegistryFriendlyByteBuf, V>
|
abstract val streamCodec: MatteryStreamCodec<RegistryFriendlyByteBuf, V>
|
||||||
|
|
||||||
private val remotes = CopyOnWriteArrayList<RemoteState>()
|
private val remotes = CopyOnWriteArrayList<RemoteState>()
|
||||||
|
final override val hasRemotes: Boolean
|
||||||
|
get() = remotes.isNotEmpty()
|
||||||
|
|
||||||
|
final override var isRemote: Boolean = false
|
||||||
|
private set
|
||||||
|
|
||||||
private inner class RemoteState(val listener: Runnable) : IRemoteState {
|
private inner class RemoteState(val listener: Runnable) : IRemoteState {
|
||||||
init {
|
init {
|
||||||
@ -98,7 +96,9 @@ abstract class AbstractHistoryChart<V : Any>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun read(stream: RegistryFriendlyByteBuf) {
|
final override fun read(stream: RegistryFriendlyByteBuf) {
|
||||||
|
isRemote = true
|
||||||
|
|
||||||
if (stream.readBoolean()) {
|
if (stream.readBoolean()) {
|
||||||
values.clear()
|
values.clear()
|
||||||
}
|
}
|
||||||
@ -110,11 +110,11 @@ abstract class AbstractHistoryChart<V : Any>(
|
|||||||
while (values.size > width) values.removeLast()
|
while (values.size > width) values.removeLast()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createRemoteState(listener: Runnable): IRemoteState {
|
final override fun createRemoteState(listener: Runnable): IRemoteState {
|
||||||
return RemoteState(listener)
|
return RemoteState(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun calculateAverage(): V {
|
final override fun calculateAverage(): V {
|
||||||
if (values.isEmpty()) {
|
if (values.isEmpty()) {
|
||||||
return identity()
|
return identity()
|
||||||
}
|
}
|
||||||
@ -143,7 +143,7 @@ abstract class AbstractHistoryChart<V : Any>(
|
|||||||
val size: Int
|
val size: Int
|
||||||
get() = values.size
|
get() = values.size
|
||||||
|
|
||||||
operator fun get(index: Int): V {
|
final override operator fun get(index: Int): V {
|
||||||
return values.getOrNull(index) ?: identity()
|
return values.getOrNull(index) ?: identity()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,7 +158,7 @@ abstract class AbstractHistoryChart<V : Any>(
|
|||||||
it.group(
|
it.group(
|
||||||
list.fieldOf("accumulator").forGetter { it.accumulated },
|
list.fieldOf("accumulator").forGetter { it.accumulated },
|
||||||
list.fieldOf("values").forGetter { it.values },
|
list.fieldOf("values").forGetter { it.values },
|
||||||
).apply(it, ::SData)
|
).apply(it) { a, b -> SData(a, b) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.core.chart
|
||||||
|
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||||
|
import ru.dbotthepony.mc.otm.core.collect.reduce
|
||||||
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
|
import java.util.function.Supplier
|
||||||
|
|
||||||
|
class CombinedDecimalHistoryChart(
|
||||||
|
provider: Supplier<Iterator<DecimalHistoryChart>>,
|
||||||
|
resolution: Int,
|
||||||
|
width: Int,
|
||||||
|
) : AbstractCombinedHistoryChart<Decimal, DecimalHistoryChart>(provider, width, resolution) {
|
||||||
|
constructor(provider: Supplier<Iterator<DecimalHistoryChart>>, ticks: Int) : this(provider, 1, ticks)
|
||||||
|
|
||||||
|
override fun sum(values: List<Decimal>): Decimal {
|
||||||
|
return values.iterator().reduce(Decimal.ZERO, Decimal::plus)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun calculateAverage(): Decimal {
|
||||||
|
return this.iterator().reduce(Decimal.ZERO, Decimal::plus) / Decimal(width)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun factory(stream: RegistryFriendlyByteBuf): DecimalHistoryChart {
|
||||||
|
return DecimalHistoryChart(stream.readVarInt(), stream.readVarInt())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun networkMetadata(stream: RegistryFriendlyByteBuf, chart: DecimalHistoryChart) {
|
||||||
|
stream.writeVarInt(chart.resolution)
|
||||||
|
stream.writeVarInt(chart.width)
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,10 @@
|
|||||||
package ru.dbotthepony.mc.otm.core
|
package ru.dbotthepony.mc.otm.core.chart
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList
|
import com.google.common.collect.ImmutableList
|
||||||
import com.mojang.serialization.Codec
|
import com.mojang.serialization.Codec
|
||||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||||
import ru.dbotthepony.mc.otm.core.collect.reduce
|
import ru.dbotthepony.mc.otm.core.collect.reduce
|
||||||
|
import ru.dbotthepony.mc.otm.core.immutableList
|
||||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
import ru.dbotthepony.mc.otm.data.DecimalCodec
|
import ru.dbotthepony.mc.otm.data.DecimalCodec
|
||||||
import ru.dbotthepony.mc.otm.network.MatteryStreamCodec
|
import ru.dbotthepony.mc.otm.network.MatteryStreamCodec
|
||||||
@ -33,10 +34,6 @@ class DecimalHistoryChart : AbstractHistoryChart<Decimal> {
|
|||||||
override val streamCodec: MatteryStreamCodec<RegistryFriendlyByteBuf, Decimal>
|
override val streamCodec: MatteryStreamCodec<RegistryFriendlyByteBuf, Decimal>
|
||||||
get() = DecimalCodec.NETWORK
|
get() = DecimalCodec.NETWORK
|
||||||
|
|
||||||
fun calcWeightedAverage(): Decimal {
|
|
||||||
return calcWeightedAverage(this::get)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val HISTORY_WEIGHTERS: ImmutableList<Decimal> = immutableList {
|
private val HISTORY_WEIGHTERS: ImmutableList<Decimal> = immutableList {
|
||||||
/*for (i in 0 until 20) {
|
/*for (i in 0 until 20) {
|
@ -0,0 +1,20 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.core.chart
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common interface for reading chart values
|
||||||
|
*/
|
||||||
|
interface IHistoryChart<V : Any> : Iterable<V> {
|
||||||
|
/**
|
||||||
|
* Limit on how many graph values to store
|
||||||
|
*/
|
||||||
|
val width: Int
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How many measurements one graph value contains
|
||||||
|
*/
|
||||||
|
val resolution: Int
|
||||||
|
|
||||||
|
operator fun get(index: Int): V
|
||||||
|
|
||||||
|
fun calculateAverage(): V
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.core.multiblock
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity
|
||||||
|
import java.util.Collections
|
||||||
|
|
||||||
|
class BlockEntitySet<T : BlockEntity>(private val listener: GlobalBlockEntityRemovalListener, val tag: BlockEntityTag<T>) {
|
||||||
|
private val items = Reference2IntOpenHashMap<T>()
|
||||||
|
val set: Set<T> = Collections.unmodifiableSet(items.keys)
|
||||||
|
|
||||||
|
fun add(blockEntity: BlockEntity) {
|
||||||
|
if (tag.test(blockEntity)) {
|
||||||
|
val new = items.getInt(blockEntity) + 1
|
||||||
|
|
||||||
|
if (new == 1)
|
||||||
|
GlobalBlockEntityRemovalListener.listen(blockEntity, listener)
|
||||||
|
|
||||||
|
items.put(blockEntity as T, new)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun remove(blockEntity: BlockEntity) {
|
||||||
|
if (tag.test(blockEntity)) {
|
||||||
|
val existing = items.getInt(blockEntity)
|
||||||
|
|
||||||
|
if (existing == 1) {
|
||||||
|
GlobalBlockEntityRemovalListener.stopListening(blockEntity, listener)
|
||||||
|
items.removeInt(blockEntity)
|
||||||
|
} else if (existing > 1) {
|
||||||
|
items.put(blockEntity as T, existing - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun blockEntityRemoved(blockEntity: BlockEntity): Boolean {
|
||||||
|
return items.removeInt(blockEntity as T) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clear() {
|
||||||
|
items.keys.forEach {
|
||||||
|
GlobalBlockEntityRemovalListener.stopListening(it, listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
items.clear()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.core.multiblock
|
||||||
|
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity
|
||||||
|
import java.util.function.Predicate
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
inline fun <reified T : BlockEntity> multiblockEntity(): BlockEntityTag<T> {
|
||||||
|
return BlockEntityTag(T::class)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tags specific [T] block (or any subclassed one) entities to be exposed through [ShapedMultiblock.blockEntities] when specific [Part]s are tagged using [Part.tag]
|
||||||
|
*
|
||||||
|
* Optionally, can have [predicate] specified, which narrows which block entities can be accepted by this tag
|
||||||
|
*/
|
||||||
|
class BlockEntityTag<T : BlockEntity>(val clazz: KClass<T>, val predicate: Predicate<in T> = Predicate { true }) :
|
||||||
|
Predicate<BlockEntity> {
|
||||||
|
override fun test(t: BlockEntity): Boolean {
|
||||||
|
return clazz.isInstance(t) && predicate.test(t as T)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,83 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.core.multiblock
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.core.Vec3i
|
||||||
|
import net.minecraft.world.level.LevelAccessor
|
||||||
|
import net.minecraft.world.level.block.Rotation
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity
|
||||||
|
import net.minecraft.world.level.block.state.BlockState
|
||||||
|
import ru.dbotthepony.mc.otm.core.getBlockStateNow
|
||||||
|
import ru.dbotthepony.mc.otm.core.math.plus
|
||||||
|
|
||||||
|
fun interface BlockPredicate {
|
||||||
|
fun test(pos: BlockPos, access: LevelAccessor, blockState: Lazy<BlockState>, blockEntity: Lazy<BlockEntity?>): Boolean
|
||||||
|
|
||||||
|
fun rotate(rotation: Rotation): BlockPredicate {
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun and(other: BlockPredicate): BlockPredicate {
|
||||||
|
return BlockPredicate { pos, access, blockState, blockEntity -> test(pos, access, blockState, blockEntity) && other.test(pos, access, blockState, blockEntity) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun or(other: BlockPredicate): BlockPredicate {
|
||||||
|
return BlockPredicate { pos, access, blockState, blockEntity -> test(pos, access, blockState, blockEntity) || other.test(pos, access, blockState, blockEntity) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun and(other: BlockPredicate, vararg others: BlockPredicate): BlockPredicate {
|
||||||
|
return And(ImmutableSet.copyOf(listOf(other, *others)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun or(other: BlockPredicate, vararg others: BlockPredicate): BlockPredicate {
|
||||||
|
return Or(ImmutableSet.copyOf(listOf(other, *others)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun offset(offset: BlockPos): Positioned {
|
||||||
|
return Positioned(offset, this)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun offset(offset: Vec3i): Positioned {
|
||||||
|
return offset(BlockPos(offset))
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Positioned(val offset: BlockPos, val parent: BlockPredicate) : BlockPredicate {
|
||||||
|
override fun test(pos: BlockPos, access: LevelAccessor, blockState: Lazy<BlockState>, blockEntity: Lazy<BlockEntity?>): Boolean {
|
||||||
|
return parent.test(offset + pos, access, blockState, blockEntity)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun offset(offset: BlockPos): Positioned {
|
||||||
|
return Positioned(this.offset + offset, parent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class And(val nodes: ImmutableSet<BlockPredicate>) : BlockPredicate {
|
||||||
|
constructor(vararg nodes: BlockPredicate) : this(ImmutableSet.copyOf(nodes))
|
||||||
|
constructor(nodes: Set<BlockPredicate>) : this(ImmutableSet.copyOf(nodes))
|
||||||
|
|
||||||
|
override fun test(pos: BlockPos, access: LevelAccessor, blockState: Lazy<BlockState>, blockEntity: Lazy<BlockEntity?>): Boolean {
|
||||||
|
return nodes.all { it.test(pos, access, blockState, blockEntity) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Or(val nodes: ImmutableSet<BlockPredicate>) : BlockPredicate {
|
||||||
|
constructor(vararg nodes: BlockPredicate) : this(ImmutableSet.copyOf(nodes))
|
||||||
|
constructor(nodes: Set<BlockPredicate>) : this(ImmutableSet.copyOf(nodes))
|
||||||
|
|
||||||
|
override fun test(pos: BlockPos, access: LevelAccessor, blockState: Lazy<BlockState>, blockEntity: Lazy<BlockEntity?>): Boolean {
|
||||||
|
return nodes.any { it.test(pos, access, blockState, blockEntity) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object Air : BlockPredicate {
|
||||||
|
override fun test(pos: BlockPos, access: LevelAccessor, blockState: Lazy<BlockState>, blockEntity: Lazy<BlockEntity?>): Boolean {
|
||||||
|
return blockState.value.isAir
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object NotAir : BlockPredicate {
|
||||||
|
override fun test(pos: BlockPos, access: LevelAccessor, blockState: Lazy<BlockState>, blockEntity: Lazy<BlockEntity?>): Boolean {
|
||||||
|
return !blockState.value.isAir
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.core.multiblock
|
||||||
|
|
||||||
|
import net.minecraft.world.level.Level
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity
|
||||||
|
import ru.dbotthepony.mc.otm.core.collect.WeakHashSet
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
interface GlobalBlockEntityRemovalListener {
|
||||||
|
fun blockEntityRemoved(blockEntity: BlockEntity): Boolean
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val BLOCK_ENTITY_LISTENERS = WeakHashMap<Level, WeakHashMap<BlockEntity, WeakHashSet<GlobalBlockEntityRemovalListener>>>()
|
||||||
|
|
||||||
|
fun onBlockEntityInvalidated(blockEntity: BlockEntity) {
|
||||||
|
BLOCK_ENTITY_LISTENERS[blockEntity.level]?.get(blockEntity)?.forEach { it.blockEntityRemoved(blockEntity) }
|
||||||
|
BLOCK_ENTITY_LISTENERS[blockEntity.level]?.remove(blockEntity)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun listen(blockEntity: BlockEntity, listener: GlobalBlockEntityRemovalListener) {
|
||||||
|
BLOCK_ENTITY_LISTENERS
|
||||||
|
.computeIfAbsent(blockEntity.level) { WeakHashMap() }
|
||||||
|
.computeIfAbsent(blockEntity) { WeakHashSet() }
|
||||||
|
.add(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stopListening(blockEntity: BlockEntity, listener: GlobalBlockEntityRemovalListener) {
|
||||||
|
val levelMap = BLOCK_ENTITY_LISTENERS[blockEntity.level] ?: return
|
||||||
|
val getSet = levelMap[blockEntity]
|
||||||
|
|
||||||
|
if (getSet != null) {
|
||||||
|
getSet.remove(listener)
|
||||||
|
|
||||||
|
if (getSet.isEmpty()) {
|
||||||
|
levelMap.remove(blockEntity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.core.multiblock
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.objects.Reference2IntMap
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.core.Direction
|
||||||
|
import net.minecraft.world.level.Level
|
||||||
|
import net.minecraft.world.level.block.Block
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity
|
||||||
|
import net.minecraft.world.level.block.state.BlockState
|
||||||
|
import ru.dbotthepony.mc.otm.core.collect.WeakHashSet
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
interface IMultiblockAccess {
|
||||||
|
/**
|
||||||
|
* Whenever this multiblock is valid (all checks passed)
|
||||||
|
*/
|
||||||
|
val isValid: Boolean
|
||||||
|
val currentDirection: Direction?
|
||||||
|
val currentNodes: Map<BlockPos, IMultiblockNode>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns block counts present on nodes tagged by [tag]
|
||||||
|
*/
|
||||||
|
fun blocks(tag: Any): Reference2IntMap<Block>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns blockstate counts present on nodes tagged by [tag]
|
||||||
|
*/
|
||||||
|
fun blockStates(tag: Any): Reference2IntMap<BlockState>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns block counts present on nodes tagged by [GLOBAL_BLOCK_TAG]
|
||||||
|
*/
|
||||||
|
fun blocks(): Reference2IntMap<Block> {
|
||||||
|
return blocks(GLOBAL_BLOCK_TAG)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns blockstate counts present on nodes tagged by [GLOBAL_BLOCK_TAG]
|
||||||
|
*/
|
||||||
|
fun blockStates(): Reference2IntMap<BlockState> {
|
||||||
|
return blockStates(GLOBAL_BLOCK_TAG)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T : BlockEntity> blockEntities(tag: BlockEntityTag<T>): Set<T>
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val GLOBAL_BLOCK_TAG = Any()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.core.multiblock
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
|
||||||
|
interface IMultiblockNode {
|
||||||
|
val pos: BlockPos
|
||||||
|
val status: NodeStatus
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.core.multiblock
|
||||||
|
|
||||||
|
enum class NodeStatus {
|
||||||
|
UNKNOWN,
|
||||||
|
VALID,
|
||||||
|
INVALID;
|
||||||
|
}
|
@ -0,0 +1,627 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.core.multiblock
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList
|
||||||
|
import com.google.common.collect.ImmutableMap
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntArrayList
|
||||||
|
import it.unimi.dsi.fastutil.objects.Reference2IntMap
|
||||||
|
import it.unimi.dsi.fastutil.objects.Reference2IntMaps
|
||||||
|
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.core.Direction
|
||||||
|
import net.minecraft.core.SectionPos
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||||
|
import net.minecraft.world.level.LevelAccessor
|
||||||
|
import net.minecraft.world.level.block.Block
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity
|
||||||
|
import net.minecraft.world.level.block.state.BlockState
|
||||||
|
import ru.dbotthepony.mc.otm.core.addAll
|
||||||
|
import ru.dbotthepony.mc.otm.core.collect.collect
|
||||||
|
import ru.dbotthepony.mc.otm.core.collect.map
|
||||||
|
import ru.dbotthepony.mc.otm.core.getBlockStateNow
|
||||||
|
import ru.dbotthepony.mc.otm.core.math.plus
|
||||||
|
import ru.dbotthepony.mc.otm.core.registryName
|
||||||
|
import ru.dbotthepony.mc.otm.network.syncher.IRemoteState
|
||||||
|
import ru.dbotthepony.mc.otm.network.syncher.ISynchable
|
||||||
|
import java.io.Closeable
|
||||||
|
import java.util.LinkedList
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList
|
||||||
|
import java.util.function.BooleanSupplier
|
||||||
|
import kotlin.collections.ArrayDeque
|
||||||
|
import kotlin.collections.ArrayList
|
||||||
|
import kotlin.collections.HashMap
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [close] is not required to be explicitly called, but it will help in freeing allocated memory faster
|
||||||
|
*/
|
||||||
|
class ShapedMultiblock(pos: BlockPos, factory: ShapedMultiblockFactory) : IMultiblockAccess, ISynchable, Closeable, GlobalBlockEntityRemovalListener {
|
||||||
|
override var isValid: Boolean = false
|
||||||
|
private set
|
||||||
|
|
||||||
|
private val customChecks = factory.customChecks
|
||||||
|
private val north = Config(Direction.NORTH, pos, factory.north)
|
||||||
|
private val south = Config(Direction.SOUTH, pos, factory.south)
|
||||||
|
private val west = Config(Direction.WEST, pos, factory.west)
|
||||||
|
private val east = Config(Direction.EAST, pos, factory.east)
|
||||||
|
private val configurations = ImmutableList.of(north, south, west, east)
|
||||||
|
|
||||||
|
override val hasRemotes: Boolean
|
||||||
|
get() = remotes.isNotEmpty()
|
||||||
|
|
||||||
|
private inner class Config(override val currentDirection: Direction, val pos: BlockPos, parts: Collection<ShapedMultiblockFactory.Part>) : IMultiblockAccess, ISynchable, GlobalBlockEntityRemovalListener, Comparable<Config> {
|
||||||
|
private inner class Part(override val pos: BlockPos, val prototype: ShapedMultiblockFactory.Part) : Comparable<Part>, IMultiblockNode {
|
||||||
|
var index = -1
|
||||||
|
|
||||||
|
private var blockEntity: BlockEntity? = null
|
||||||
|
private var blockState: BlockState? = null
|
||||||
|
private val assignedBlockEntityLists = ArrayList<BlockEntitySet<*>>(prototype.blockEntityTags.size)
|
||||||
|
private val assignedBlockStateLists = ArrayList<Reference2IntMap<BlockState>>()
|
||||||
|
private val assignedBlockLists = ArrayList<Reference2IntMap<Block>>()
|
||||||
|
private val children: ImmutableList<Part> by lazy(LazyThreadSafetyMode.NONE) { prototype.children.iterator().map { Part(pos, it) }.collect(ImmutableList.toImmutableList()) }
|
||||||
|
|
||||||
|
override fun compareTo(other: Part): Int {
|
||||||
|
val cmp = SectionPos.blockToSectionCoord(pos.x).compareTo(SectionPos.blockToSectionCoord(other.pos.x))
|
||||||
|
if (cmp != 0) return cmp
|
||||||
|
return SectionPos.blockToSectionCoord(pos.z).compareTo(SectionPos.blockToSectionCoord(other.pos.z))
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
prototype.blockEntityTags.forEach {
|
||||||
|
assignedBlockEntityLists.add(getBlockEntityList(it))
|
||||||
|
}
|
||||||
|
|
||||||
|
prototype.blockStateTags.forEach {
|
||||||
|
assignedBlockStateLists.add(getBlockStateList(it))
|
||||||
|
}
|
||||||
|
|
||||||
|
prototype.blockTags.forEach {
|
||||||
|
assignedBlockLists.add(getBlockList(it))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var lastSuccessfulPathPredicate = -1
|
||||||
|
private var lastSuccessfulPathChildren = -1
|
||||||
|
|
||||||
|
override var status: NodeStatus = NodeStatus.UNKNOWN
|
||||||
|
private set
|
||||||
|
|
||||||
|
fun read(stream: RegistryFriendlyByteBuf) {
|
||||||
|
status = NodeStatus.entries[stream.readUnsignedByte().toInt()]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun write(stream: RegistryFriendlyByteBuf) {
|
||||||
|
stream.writeByte(status.ordinal)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun orPredicates(levelAccessor: LevelAccessor, blockState: Lazy<BlockState>, blockEntity: Lazy<BlockEntity?>): Boolean {
|
||||||
|
if (prototype.predicate.isNotEmpty()) {
|
||||||
|
if (lastSuccessfulPathPredicate != -1 && !prototype.predicate[lastSuccessfulPathPredicate].test(pos, levelAccessor, blockState, blockEntity)) {
|
||||||
|
lastSuccessfulPathPredicate = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastSuccessfulPathPredicate == -1) {
|
||||||
|
lastSuccessfulPathPredicate = prototype.predicate.indexOfFirst { it.test(pos, levelAccessor, blockState, blockEntity) }
|
||||||
|
return lastSuccessfulPathPredicate != -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun orChildren(levelAccessor: LevelAccessor, blockState: Lazy<BlockState>, blockEntity: Lazy<BlockEntity?>): Boolean {
|
||||||
|
if (children.isNotEmpty()) {
|
||||||
|
if (lastSuccessfulPathChildren != -1 && !children[lastSuccessfulPathChildren].test0(levelAccessor, blockState, blockEntity)) {
|
||||||
|
lastSuccessfulPathChildren = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastSuccessfulPathChildren == -1) {
|
||||||
|
lastSuccessfulPathChildren = children.indexOfFirst { it.test0(levelAccessor, blockState, blockEntity) }
|
||||||
|
return lastSuccessfulPathChildren != -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun test0(levelAccessor: LevelAccessor, blockState: Lazy<BlockState>, blockEntity: Lazy<BlockEntity?>): Boolean {
|
||||||
|
val test = when (prototype.strategy) {
|
||||||
|
Strategy.OR_BOTH -> orPredicates(levelAccessor, blockState, blockEntity) && orChildren(levelAccessor, blockState, blockEntity)
|
||||||
|
|
||||||
|
Strategy.OR_EITHER ->
|
||||||
|
prototype.predicate.isNotEmpty() && orPredicates(levelAccessor, blockState, blockEntity) ||
|
||||||
|
children.isNotEmpty() && orChildren(levelAccessor, blockState, blockEntity) ||
|
||||||
|
children.isEmpty() && prototype.predicate.isEmpty()
|
||||||
|
|
||||||
|
Strategy.AND -> prototype.predicate.all { it.test(pos, levelAccessor, blockState, blockEntity) } && children.all { it.test0(levelAccessor, blockState, blockEntity) }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (test) {
|
||||||
|
if (assignedBlockEntityLists.isNotEmpty()) {
|
||||||
|
val be1 = this.blockEntity
|
||||||
|
val be2 = blockEntity.value
|
||||||
|
|
||||||
|
if (be1 != be2) {
|
||||||
|
if (be1 != null) {
|
||||||
|
assignedBlockEntityLists.forEach { it.remove(be1) }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (be2 != null) {
|
||||||
|
assignedBlockEntityLists.forEach { it.add(be2) }
|
||||||
|
}
|
||||||
|
|
||||||
|
this.blockEntity = be2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (assignedBlockStateLists.isNotEmpty() || assignedBlockLists.isNotEmpty()) {
|
||||||
|
val state = blockState.value
|
||||||
|
val old = this.blockState
|
||||||
|
|
||||||
|
if (state != old) {
|
||||||
|
if (old != null) {
|
||||||
|
assignedBlockStateLists.forEach {
|
||||||
|
it[old] = it.getInt(old) - 1
|
||||||
|
check(it.getInt(old) >= 0) { "Consistency check failed: Counter for block state $old turned negative" }
|
||||||
|
}
|
||||||
|
|
||||||
|
assignedBlockLists.forEach {
|
||||||
|
it[old.block] = it.getInt(old.block) - 1
|
||||||
|
check(it.getInt(old.block) >= 0) { "Consistency check failed: Counter for block ${old.block.registryName} turned negative" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assignedBlockStateLists.forEach {
|
||||||
|
it[state] = it.getInt(state) + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
assignedBlockLists.forEach {
|
||||||
|
it[state.block] = it.getInt(state.block) + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
this.blockState = state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
clearFull()
|
||||||
|
}
|
||||||
|
|
||||||
|
return test
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test(levelAccessor: LevelAccessor): Boolean {
|
||||||
|
val blockEntity = lazy(LazyThreadSafetyMode.NONE) { levelAccessor.getBlockEntity(pos) }
|
||||||
|
val blockState = lazy(LazyThreadSafetyMode.NONE) { levelAccessor.getBlockStateNow(pos) }
|
||||||
|
val status = test0(levelAccessor, blockState, blockEntity)
|
||||||
|
|
||||||
|
val previous = this.status
|
||||||
|
this.status = if (status) NodeStatus.VALID else NodeStatus.INVALID
|
||||||
|
|
||||||
|
if (previous !== this.status && index != -1) {
|
||||||
|
pushNetworkPartUpdate(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
return status
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun clearFull() {
|
||||||
|
val blockEntity = blockEntity
|
||||||
|
|
||||||
|
if (blockEntity != null) {
|
||||||
|
assignedBlockEntityLists.forEach { it.remove(blockEntity) }
|
||||||
|
}
|
||||||
|
|
||||||
|
val blockState = blockState
|
||||||
|
|
||||||
|
if (blockState != null) {
|
||||||
|
assignedBlockStateLists.forEach {
|
||||||
|
it[blockState] = it.getInt(blockState) - 1
|
||||||
|
check(it.getInt(blockState) >= 0) { "Consistency check failed: Counter for block state $blockState turned negative" }
|
||||||
|
}
|
||||||
|
|
||||||
|
assignedBlockLists.forEach {
|
||||||
|
it[blockState.block] = it.getInt(blockState.block) - 1
|
||||||
|
check(it.getInt(blockState.block) >= 0) { "Consistency check failed: Counter for block ${blockState.block.registryName} turned negative" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.blockEntity = null
|
||||||
|
this.blockState = null
|
||||||
|
|
||||||
|
lastSuccessfulPathPredicate = -1
|
||||||
|
lastSuccessfulPathChildren = -1
|
||||||
|
|
||||||
|
// avoid allocating iterator when empty
|
||||||
|
if (children.isNotEmpty())
|
||||||
|
children.forEach { it.clearFull() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clear() {
|
||||||
|
blockEntity = null
|
||||||
|
blockState = null
|
||||||
|
|
||||||
|
lastSuccessfulPathPredicate = -1
|
||||||
|
lastSuccessfulPathChildren = -1
|
||||||
|
|
||||||
|
children.forEach { it.clear() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val index = when (currentDirection) {
|
||||||
|
Direction.NORTH -> 0
|
||||||
|
Direction.SOUTH -> 1
|
||||||
|
Direction.WEST -> 2
|
||||||
|
Direction.EAST -> 3
|
||||||
|
else -> throw RuntimeException()
|
||||||
|
}
|
||||||
|
|
||||||
|
override val currentNodes: Map<BlockPos, IMultiblockNode>
|
||||||
|
get() = parts
|
||||||
|
|
||||||
|
private val tag2BlockEntity = HashMap<BlockEntityTag<*>, BlockEntitySet<*>>()
|
||||||
|
private val tag2BlockState = HashMap<Any, Reference2IntMap<BlockState>>()
|
||||||
|
private val tag2Block = HashMap<Any, Reference2IntMap<Block>>()
|
||||||
|
|
||||||
|
private val tag2BlockStateViews = HashMap<Any, Reference2IntMap<BlockState>>()
|
||||||
|
private val tag2BlockViews = HashMap<Any, Reference2IntMap<Block>>()
|
||||||
|
|
||||||
|
init {
|
||||||
|
getBlockList(IMultiblockAccess.GLOBAL_BLOCK_TAG)
|
||||||
|
getBlockStateList(IMultiblockAccess.GLOBAL_BLOCK_TAG)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val globalBlocksView = tag2BlockViews[IMultiblockAccess.GLOBAL_BLOCK_TAG]!!
|
||||||
|
private val globalBlockStatesView = tag2BlockStateViews[IMultiblockAccess.GLOBAL_BLOCK_TAG]!!
|
||||||
|
|
||||||
|
private fun <T : BlockEntity> getBlockEntityList(tag: BlockEntityTag<T>): BlockEntitySet<T> {
|
||||||
|
val existing = tag2BlockEntity[tag]
|
||||||
|
|
||||||
|
if (existing != null)
|
||||||
|
return existing as BlockEntitySet<T>
|
||||||
|
|
||||||
|
val new = BlockEntitySet(this, tag)
|
||||||
|
tag2BlockEntity[tag] = new
|
||||||
|
return new
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getBlockList(tag: Any): Reference2IntMap<Block> {
|
||||||
|
val existing = tag2Block[tag]
|
||||||
|
|
||||||
|
if (existing != null)
|
||||||
|
return existing
|
||||||
|
|
||||||
|
val new = Reference2IntOpenHashMap<Block>()
|
||||||
|
tag2Block[tag] = new
|
||||||
|
tag2BlockViews[tag] = Reference2IntMaps.unmodifiable(new)
|
||||||
|
return new
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getBlockStateList(tag: Any): Reference2IntMap<BlockState> {
|
||||||
|
val existing = tag2BlockState[tag]
|
||||||
|
|
||||||
|
if (existing != null)
|
||||||
|
return existing
|
||||||
|
|
||||||
|
val new = Reference2IntOpenHashMap<BlockState>()
|
||||||
|
tag2BlockState[tag] = new
|
||||||
|
tag2BlockStateViews[tag] = Reference2IntMaps.unmodifiable(new)
|
||||||
|
return new
|
||||||
|
}
|
||||||
|
|
||||||
|
val parts: ImmutableMap<BlockPos, Part> = parts.stream()
|
||||||
|
.map { Part(it.pos + pos, it) }
|
||||||
|
.sorted() // group/localize parts by in-world chunks to maximize getChunk() cache hit rate
|
||||||
|
.collect(ImmutableMap.toImmutableMap({ it.pos }, { it }))
|
||||||
|
|
||||||
|
init {
|
||||||
|
for ((i, part) in this.parts.values.withIndex()) {
|
||||||
|
part.index = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clear() {
|
||||||
|
tag2BlockEntity.values.forEach { it.clear() }
|
||||||
|
tag2BlockState.values.forEach { it.clear() }
|
||||||
|
tag2Block.values.forEach { it.clear() }
|
||||||
|
parts.values.forEach { it.clear() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private val networkChangelog = ArrayDeque<Part>()
|
||||||
|
private var networkVersion = 0
|
||||||
|
|
||||||
|
private fun pushNetworkPartUpdate(part: Part) {
|
||||||
|
networkChangelog.addFirst(part)
|
||||||
|
networkVersion++
|
||||||
|
while (networkChangelog.size > parts.size) networkChangelog.removeLast()
|
||||||
|
}
|
||||||
|
|
||||||
|
override var isValid: Boolean = false
|
||||||
|
private set
|
||||||
|
|
||||||
|
private var iterator = this.parts.values.iterator()
|
||||||
|
private var validParts = 0
|
||||||
|
|
||||||
|
override fun compareTo(other: Config): Int {
|
||||||
|
var cmp = validParts.compareTo(other.validParts)
|
||||||
|
if (cmp == 0) cmp = currentDirection.compareTo(other.currentDirection)
|
||||||
|
return cmp
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateRemaining(levelAccessor: LevelAccessor) {
|
||||||
|
val networkVersion = networkVersion
|
||||||
|
|
||||||
|
if (!isValid) { // update rest
|
||||||
|
for (part in iterator) {
|
||||||
|
if (part.test(levelAccessor)) {
|
||||||
|
validParts++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (part in iterator) {
|
||||||
|
if (part.test(levelAccessor))
|
||||||
|
validParts++
|
||||||
|
else {
|
||||||
|
isValid = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.networkVersion != networkVersion) {
|
||||||
|
remotes.forEach { it.listener.run() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun update(levelAccessor: LevelAccessor, updateEverything: Boolean = remotes.isNotEmpty()): Boolean {
|
||||||
|
isValid = true
|
||||||
|
iterator = this.parts.values.iterator()
|
||||||
|
validParts = 0
|
||||||
|
updateRemaining(levelAccessor)
|
||||||
|
|
||||||
|
if (updateEverything && iterator.hasNext()) {
|
||||||
|
updateRemaining(levelAccessor)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isValid) clear()
|
||||||
|
return isValid
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun <T : BlockEntity> blockEntities(tag: BlockEntityTag<T>): Set<T> {
|
||||||
|
return (tag2BlockEntity[tag]?.set ?: setOf()) as Set<T>
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun blocks(tag: Any): Reference2IntMap<Block> {
|
||||||
|
return tag2BlockViews[tag] ?: Reference2IntMaps.emptyMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun blockStates(tag: Any): Reference2IntMap<BlockState> {
|
||||||
|
return tag2BlockStateViews[tag] ?: Reference2IntMaps.emptyMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun blocks(): Reference2IntMap<Block> {
|
||||||
|
return globalBlocksView
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun blockStates(): Reference2IntMap<BlockState> {
|
||||||
|
return globalBlockStatesView
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun blockEntityRemoved(blockEntity: BlockEntity): Boolean {
|
||||||
|
var any = false
|
||||||
|
tag2BlockEntity.values.forEach { any = it.blockEntityRemoved(blockEntity) || any }
|
||||||
|
return any
|
||||||
|
}
|
||||||
|
|
||||||
|
private val remotes = CopyOnWriteArrayList<RemoteState>()
|
||||||
|
|
||||||
|
override val hasRemotes: Boolean
|
||||||
|
get() = remotes.isNotEmpty()
|
||||||
|
|
||||||
|
override var isRemote: Boolean = false
|
||||||
|
private set
|
||||||
|
|
||||||
|
override fun read(stream: RegistryFriendlyByteBuf) {
|
||||||
|
isRemote = true
|
||||||
|
|
||||||
|
if (stream.readBoolean()) {
|
||||||
|
for (part in parts.values) {
|
||||||
|
part.read(stream)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var index = stream.readVarInt()
|
||||||
|
val list = parts.values.asList()
|
||||||
|
|
||||||
|
while (index != 0) {
|
||||||
|
list[index - 1].read(stream)
|
||||||
|
index = stream.readVarInt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class RemoteState(val listener: Runnable) : IRemoteState {
|
||||||
|
init {
|
||||||
|
remotes.add(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var version = -1
|
||||||
|
|
||||||
|
override fun write(stream: RegistryFriendlyByteBuf) {
|
||||||
|
if (version == -1 || networkVersion - version >= networkChangelog.size) {
|
||||||
|
stream.writeBoolean(true)
|
||||||
|
|
||||||
|
for (part in parts.values) {
|
||||||
|
part.write(stream)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stream.writeBoolean(false)
|
||||||
|
val itr = networkChangelog.listIterator(networkVersion - version)
|
||||||
|
|
||||||
|
while (itr.hasPrevious()) {
|
||||||
|
val part = itr.previous()
|
||||||
|
stream.writeVarInt(part.index + 1)
|
||||||
|
part.write(stream)
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.writeByte(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
version = networkVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun invalidate() {
|
||||||
|
version = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
remotes.remove(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createRemoteState(listener: Runnable): IRemoteState {
|
||||||
|
return RemoteState(listener)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override var isRemote: Boolean = false
|
||||||
|
private set
|
||||||
|
|
||||||
|
override fun read(stream: RegistryFriendlyByteBuf) {
|
||||||
|
isRemote = true
|
||||||
|
isValid = stream.readBoolean()
|
||||||
|
activeConfig = configurations[stream.readUnsignedByte().toInt()]
|
||||||
|
activeConfig.read(stream)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val remotes = CopyOnWriteArrayList<RemoteState>()
|
||||||
|
|
||||||
|
private inner class RemoteState(val listener: Runnable) : IRemoteState {
|
||||||
|
private val remotes = configurations.map { it.createRemoteState(listener) }
|
||||||
|
|
||||||
|
init {
|
||||||
|
this@ShapedMultiblock.remotes.add(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun write(stream: RegistryFriendlyByteBuf) {
|
||||||
|
stream.writeBoolean(isValid)
|
||||||
|
stream.writeByte(activeConfig.index)
|
||||||
|
remotes[activeConfig.index].write(stream)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun invalidate() {
|
||||||
|
remotes.forEach { it.invalidate() }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
remotes.forEach { it.close() }
|
||||||
|
this@ShapedMultiblock.remotes.remove(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createRemoteState(listener: Runnable): IRemoteState {
|
||||||
|
return RemoteState(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var activeConfig: Config = north
|
||||||
|
|
||||||
|
override val currentDirection: Direction?
|
||||||
|
get() = if (isValid) activeConfig.currentDirection else null
|
||||||
|
|
||||||
|
override val currentNodes: Map<BlockPos, IMultiblockNode>
|
||||||
|
get() = activeConfig.parts
|
||||||
|
|
||||||
|
override fun <T : BlockEntity> blockEntities(tag: BlockEntityTag<T>): Set<T> {
|
||||||
|
if (!isValid) return setOf()
|
||||||
|
return activeConfig.blockEntities(tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun blocks(tag: Any): Reference2IntMap<Block> {
|
||||||
|
if (!isValid) return Reference2IntMaps.emptyMap()
|
||||||
|
return activeConfig.blocks(tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun blockStates(tag: Any): Reference2IntMap<BlockState> {
|
||||||
|
if (!isValid) return Reference2IntMaps.emptyMap()
|
||||||
|
return activeConfig.blockStates(tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun blocks(): Reference2IntMap<Block> {
|
||||||
|
if (!isValid) return Reference2IntMaps.emptyMap()
|
||||||
|
return activeConfig.blocks()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun blockStates(): Reference2IntMap<BlockState> {
|
||||||
|
if (!isValid) return Reference2IntMaps.emptyMap()
|
||||||
|
return activeConfig.blockStates()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun blockEntityRemoved(blockEntity: BlockEntity): Boolean {
|
||||||
|
return activeConfig.blockEntityRemoved(blockEntity)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun update(levelAccessor: LevelAccessor): Boolean {
|
||||||
|
val configurations = LinkedList<Config>()
|
||||||
|
val configurations0 = ArrayList(this.configurations)
|
||||||
|
|
||||||
|
while (configurations0.isNotEmpty()) {
|
||||||
|
val max = configurations0.max()
|
||||||
|
configurations0.remove(max)
|
||||||
|
configurations.add(max)
|
||||||
|
}
|
||||||
|
|
||||||
|
isValid = false
|
||||||
|
|
||||||
|
while (configurations.isNotEmpty()) {
|
||||||
|
val config = configurations.removeFirst()
|
||||||
|
|
||||||
|
if (config.update(levelAccessor)) {
|
||||||
|
if (customChecks.all { it.test(config) }) {
|
||||||
|
activeConfig = config
|
||||||
|
isValid = true
|
||||||
|
remotes.forEach { it.listener.run() }
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
config.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun update(levelAccessor: LevelAccessor, direction: Direction): Boolean {
|
||||||
|
var changes = false
|
||||||
|
|
||||||
|
if (activeConfig.currentDirection != direction && isValid) {
|
||||||
|
activeConfig.clear()
|
||||||
|
isValid = false
|
||||||
|
changes = true
|
||||||
|
}
|
||||||
|
|
||||||
|
val config = when (direction) {
|
||||||
|
Direction.NORTH -> north
|
||||||
|
Direction.SOUTH -> south
|
||||||
|
Direction.WEST -> west
|
||||||
|
Direction.EAST -> east
|
||||||
|
else -> throw IllegalArgumentException(direction.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
changes = changes || activeConfig != config
|
||||||
|
activeConfig = config
|
||||||
|
isValid = config.update(levelAccessor)
|
||||||
|
|
||||||
|
if (isValid) {
|
||||||
|
isValid = customChecks.all { it.test(config) }
|
||||||
|
if (!isValid) config.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changes) {
|
||||||
|
remotes.forEach { it.listener.run() }
|
||||||
|
}
|
||||||
|
|
||||||
|
return isValid
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
remotes.forEach { it.close() }
|
||||||
|
configurations.forEach { it.clear() }
|
||||||
|
isValid = false
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,365 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.core.multiblock
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList
|
||||||
|
import com.google.common.collect.ImmutableSet
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.core.Direction
|
||||||
|
import net.minecraft.core.SectionPos
|
||||||
|
import net.minecraft.core.Vec3i
|
||||||
|
import net.minecraft.tags.TagKey
|
||||||
|
import net.minecraft.world.level.block.Block
|
||||||
|
import net.minecraft.world.level.block.state.BlockState
|
||||||
|
import net.minecraft.world.level.chunk.status.ChunkStatus
|
||||||
|
import ru.dbotthepony.mc.otm.core.collect.collect
|
||||||
|
import ru.dbotthepony.mc.otm.core.collect.map
|
||||||
|
import ru.dbotthepony.mc.otm.core.getBlockStateNow
|
||||||
|
import ru.dbotthepony.mc.otm.core.math.RelativeSide
|
||||||
|
import ru.dbotthepony.mc.otm.core.math.plus
|
||||||
|
import java.util.function.Predicate
|
||||||
|
|
||||||
|
inline fun shapedMultiblock(configurator: ShapedMultiblockBuilder.Node.() -> Unit): ShapedMultiblockFactory {
|
||||||
|
val builder = ShapedMultiblockBuilder()
|
||||||
|
configurator.invoke(builder.root())
|
||||||
|
return builder.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
class ShapedMultiblockBuilder {
|
||||||
|
private val nodes = Object2ObjectOpenHashMap<BlockPos, Node>()
|
||||||
|
private val customChecks = ArrayList<Predicate<IMultiblockAccess>>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds custom [predicate] which determines whenever selected multiblock configuration is valid
|
||||||
|
*
|
||||||
|
* Code inside predicate can safely call [ShapedMultiblock.blocks], [ShapedMultiblock.blockEntities], etc to determine whenever
|
||||||
|
* all demands are met
|
||||||
|
*/
|
||||||
|
fun customCheck(predicate: Predicate<IMultiblockAccess>): ShapedMultiblockBuilder {
|
||||||
|
customChecks.add(predicate)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("unchecked_cast")
|
||||||
|
abstract inner class Part<T : Part<T>> {
|
||||||
|
val predicates = ObjectArraySet<BlockPredicate>()
|
||||||
|
val children = ObjectArraySet<Part<*>>()
|
||||||
|
|
||||||
|
val builder: ShapedMultiblockBuilder
|
||||||
|
get() = this@ShapedMultiblockBuilder
|
||||||
|
|
||||||
|
val blockStateTags = ObjectArraySet<Any>()
|
||||||
|
val blockTags = ObjectArraySet<Any>()
|
||||||
|
val blockEntityTags = ObjectArraySet<BlockEntityTag<*>>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks this node report its block in [ShapedMultiblock.blockStates] method when called with specified [value]
|
||||||
|
*
|
||||||
|
* [value] is searched using "value" semantics (`==`)
|
||||||
|
*/
|
||||||
|
fun tagBlockState(value: Any): T {
|
||||||
|
blockStateTags.add(value)
|
||||||
|
return this as T
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks this node report its block in [ShapedMultiblock.blockStates] method when called without arguments
|
||||||
|
*/
|
||||||
|
fun tagBlockState(): T {
|
||||||
|
blockStateTags.add(IMultiblockAccess.GLOBAL_BLOCK_TAG)
|
||||||
|
return this as T
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks this node report its block in [ShapedMultiblock.blocks] method when called with specified [value]
|
||||||
|
*
|
||||||
|
* [value] is searched for using "value" semantics (`==`)
|
||||||
|
*/
|
||||||
|
fun tagBlock(value: Any): T {
|
||||||
|
blockTags.add(value)
|
||||||
|
return this as T
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks this node report its block in [ShapedMultiblock.blocks] method when called without arguments
|
||||||
|
*/
|
||||||
|
fun tagBlock(): T {
|
||||||
|
blockTags.add(IMultiblockAccess.GLOBAL_BLOCK_TAG)
|
||||||
|
return this as T
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks this node to report block entities put at its position when called [ShapedMultiblock.blockEntities] with specified [value]
|
||||||
|
*
|
||||||
|
* [value] is searched for using "identity" semantics (`===`)
|
||||||
|
*/
|
||||||
|
fun tag(value: BlockEntityTag<*>): T {
|
||||||
|
blockEntityTags.add(value)
|
||||||
|
return this as T
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all predicates, custom and prebuilt alike
|
||||||
|
*/
|
||||||
|
fun clear(): T {
|
||||||
|
predicates.clear()
|
||||||
|
return this as T
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a custom predicate on this node's position
|
||||||
|
*/
|
||||||
|
fun predicate(predicate: BlockPredicate): T {
|
||||||
|
predicates.add(predicate)
|
||||||
|
return this as T
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a block predicate, matching any valid blockstate of [block] on this node's position
|
||||||
|
*/
|
||||||
|
fun block(block: Block): T {
|
||||||
|
predicates.add { pos, access, blockState, blockEntity ->
|
||||||
|
blockState.value.block === block
|
||||||
|
}
|
||||||
|
|
||||||
|
return this as T
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a block tag predicate on this node's position
|
||||||
|
*/
|
||||||
|
fun block(block: TagKey<Block>): T {
|
||||||
|
predicates.add { pos, access, blockState, blockEntity ->
|
||||||
|
blockState.value.`is`(block)
|
||||||
|
}
|
||||||
|
|
||||||
|
return this as T
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds "nothing" predicate (no block should be present) on this node's position
|
||||||
|
*/
|
||||||
|
fun air(): T {
|
||||||
|
predicate(BlockPredicate.Air)
|
||||||
|
return this as T
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a blockstate predicate, matching exactly provided [state] on this node's position
|
||||||
|
*/
|
||||||
|
fun block(state: BlockState): T {
|
||||||
|
predicates.add { pos, access, blockState, blockEntity ->
|
||||||
|
blockState.value === state
|
||||||
|
}
|
||||||
|
|
||||||
|
return this as T
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates "and" subnode, which requires all its children to test true
|
||||||
|
*
|
||||||
|
* @see And
|
||||||
|
*/
|
||||||
|
fun and(): SubNode<T> {
|
||||||
|
return SubNode(this as T, Strategy.AND)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates "or" subnode, which requires at least one of its children to test true
|
||||||
|
*/
|
||||||
|
fun or(): SubNode<T> {
|
||||||
|
return SubNode(this as T, Strategy.OR_EITHER)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates "and" subnode, which requires all its children to test true
|
||||||
|
*/
|
||||||
|
fun and(configurator: SubNode<T>.() -> Unit): SubNode<T> {
|
||||||
|
return SubNode(this as T, Strategy.AND).also(configurator)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates "or" subnode, which requires at least one of its children to test true
|
||||||
|
*/
|
||||||
|
fun or(configurator: SubNode<T>.() -> Unit): SubNode<T> {
|
||||||
|
return SubNode(this as T, Strategy.OR_EITHER).also(configurator)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun build(pos: BlockPos): ShapedMultiblockFactory.Part {
|
||||||
|
return ShapedMultiblockFactory.Part(
|
||||||
|
pos, strategy, ImmutableList.copyOf(predicates),
|
||||||
|
children.stream().map { it.build(pos) }.collect(ImmutableList.toImmutableList()),
|
||||||
|
ImmutableSet.copyOf(blockStateTags),
|
||||||
|
ImmutableSet.copyOf(blockTags),
|
||||||
|
ImmutableSet.copyOf(blockEntityTags),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract val strategy: Strategy
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logical subnode, which combines all children using [strategy]
|
||||||
|
*/
|
||||||
|
inner class SubNode<P : Part<P>>(val parent: P, override val strategy: Strategy) : Part<SubNode<P>>() {
|
||||||
|
init {
|
||||||
|
parent.children.add(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun end() = parent
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* By default, tests children using [Strategy.OR_BOTH] strategy
|
||||||
|
*/
|
||||||
|
inner class Node(val pos: BlockPos) : Part<Node>() {
|
||||||
|
init {
|
||||||
|
check(nodes.put(pos, this) == null) { "Trying to create new node at $pos while already having one" }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates new node relative to this node at [diff], which behaves as if being [Or] node
|
||||||
|
*/
|
||||||
|
fun relative(diff: Vec3i): Node {
|
||||||
|
return node(pos + diff)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates new node relative to this node at [dir] side, which behaves as if being [Or] node
|
||||||
|
*/
|
||||||
|
fun relative(dir: Direction): Node {
|
||||||
|
return relative(dir.normal)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates new node relative to this node at [dir] side, which behaves as if being [Or] node
|
||||||
|
*/
|
||||||
|
fun relative(dir: RelativeSide): Node {
|
||||||
|
return relative(dir.default)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates new node relative to this node at [diff], and configures it in-place using provided [configurator] expression, which behaves as if being [Or] node
|
||||||
|
*/
|
||||||
|
fun relative(diff: Vec3i, configurator: Node.() -> Unit): Node {
|
||||||
|
return node(pos + diff).also(configurator)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates new node relative to this node at [dir] side, and configures it in-place using provided [configurator] expression, which behaves as if being [Or] node
|
||||||
|
*/
|
||||||
|
fun relative(dir: Direction, configurator: Node.() -> Unit): Node {
|
||||||
|
return relative(dir.normal).also(configurator)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates new node relative to this node at [dir] side, and configures it in-place using provided [configurator] expression, which behaves as if being [Or] node
|
||||||
|
*/
|
||||||
|
fun relative(dir: RelativeSide, configurator: Node.() -> Unit): Node {
|
||||||
|
return relative(dir.default).also(configurator)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun front() = relative(RelativeSide.FRONT)
|
||||||
|
fun back() = relative(RelativeSide.BACK)
|
||||||
|
fun left() = relative(RelativeSide.LEFT)
|
||||||
|
fun right() = relative(RelativeSide.RIGHT)
|
||||||
|
fun top() = relative(RelativeSide.TOP)
|
||||||
|
fun bottom() = relative(RelativeSide.BOTTOM)
|
||||||
|
|
||||||
|
fun front(block: Block) = relative(RelativeSide.FRONT).also { it.block(block) }
|
||||||
|
fun back(block: Block) = relative(RelativeSide.BACK).also { it.block(block) }
|
||||||
|
fun left(block: Block) = relative(RelativeSide.LEFT).also { it.block(block) }
|
||||||
|
fun right(block: Block) = relative(RelativeSide.RIGHT).also { it.block(block) }
|
||||||
|
fun top(block: Block) = relative(RelativeSide.TOP).also { it.block(block) }
|
||||||
|
fun bottom(block: Block) = relative(RelativeSide.BOTTOM).also { it.block(block) }
|
||||||
|
|
||||||
|
fun front(block: BlockState) = relative(RelativeSide.FRONT).also { it.block(block) }
|
||||||
|
fun back(block: BlockState) = relative(RelativeSide.BACK).also { it.block(block) }
|
||||||
|
fun left(block: BlockState) = relative(RelativeSide.LEFT).also { it.block(block) }
|
||||||
|
fun right(block: BlockState) = relative(RelativeSide.RIGHT).also { it.block(block) }
|
||||||
|
fun top(block: BlockState) = relative(RelativeSide.TOP).also { it.block(block) }
|
||||||
|
fun bottom(block: BlockState) = relative(RelativeSide.BOTTOM).also { it.block(block) }
|
||||||
|
|
||||||
|
fun front(predicate: BlockPredicate) = relative(RelativeSide.FRONT).also { it.predicate(predicate) }
|
||||||
|
fun back(predicate: BlockPredicate) = relative(RelativeSide.BACK).also { it.predicate(predicate) }
|
||||||
|
fun left(predicate: BlockPredicate) = relative(RelativeSide.LEFT).also { it.predicate(predicate) }
|
||||||
|
fun right(predicate: BlockPredicate) = relative(RelativeSide.RIGHT).also { it.predicate(predicate) }
|
||||||
|
fun top(predicate: BlockPredicate) = relative(RelativeSide.TOP).also { it.predicate(predicate) }
|
||||||
|
fun bottom(predicate: BlockPredicate) = relative(RelativeSide.BOTTOM).also { it.predicate(predicate) }
|
||||||
|
|
||||||
|
fun front(configurator: Node.() -> Unit) = relative(RelativeSide.FRONT).also(configurator)
|
||||||
|
fun back(configurator: Node.() -> Unit) = relative(RelativeSide.BACK).also(configurator)
|
||||||
|
fun left(configurator: Node.() -> Unit) = relative(RelativeSide.LEFT).also(configurator)
|
||||||
|
fun right(configurator: Node.() -> Unit) = relative(RelativeSide.RIGHT).also(configurator)
|
||||||
|
fun top(configurator: Node.() -> Unit) = relative(RelativeSide.TOP).also(configurator)
|
||||||
|
fun bottom(configurator: Node.() -> Unit) = relative(RelativeSide.BOTTOM).also(configurator)
|
||||||
|
|
||||||
|
fun front(block: Block, configurator: Node.() -> Unit) = relative(RelativeSide.FRONT).also { it.block(block) }.also(configurator)
|
||||||
|
fun back(block: Block, configurator: Node.() -> Unit) = relative(RelativeSide.BACK).also { it.block(block) }.also(configurator)
|
||||||
|
fun left(block: Block, configurator: Node.() -> Unit) = relative(RelativeSide.LEFT).also { it.block(block) }.also(configurator)
|
||||||
|
fun right(block: Block, configurator: Node.() -> Unit) = relative(RelativeSide.RIGHT).also { it.block(block) }.also(configurator)
|
||||||
|
fun top(block: Block, configurator: Node.() -> Unit) = relative(RelativeSide.TOP).also { it.block(block) }.also(configurator)
|
||||||
|
fun bottom(block: Block, configurator: Node.() -> Unit) = relative(RelativeSide.BOTTOM).also { it.block(block) }.also(configurator)
|
||||||
|
|
||||||
|
fun front(block: BlockState, configurator: Node.() -> Unit) = relative(RelativeSide.FRONT).also { it.block(block) }.also(configurator)
|
||||||
|
fun back(block: BlockState, configurator: Node.() -> Unit) = relative(RelativeSide.BACK).also { it.block(block) }.also(configurator)
|
||||||
|
fun left(block: BlockState, configurator: Node.() -> Unit) = relative(RelativeSide.LEFT).also { it.block(block) }.also(configurator)
|
||||||
|
fun right(block: BlockState, configurator: Node.() -> Unit) = relative(RelativeSide.RIGHT).also { it.block(block) }.also(configurator)
|
||||||
|
fun top(block: BlockState, configurator: Node.() -> Unit) = relative(RelativeSide.TOP).also { it.block(block) }.also(configurator)
|
||||||
|
fun bottom(block: BlockState, configurator: Node.() -> Unit) = relative(RelativeSide.BOTTOM).also { it.block(block) }.also(configurator)
|
||||||
|
|
||||||
|
fun front(predicate: BlockPredicate, configurator: Node.() -> Unit) = relative(RelativeSide.FRONT).also { it.predicate(predicate) }.also(configurator)
|
||||||
|
fun back(predicate: BlockPredicate, configurator: Node.() -> Unit) = relative(RelativeSide.BACK).also { it.predicate(predicate) }.also(configurator)
|
||||||
|
fun left(predicate: BlockPredicate, configurator: Node.() -> Unit) = relative(RelativeSide.LEFT).also { it.predicate(predicate) }.also(configurator)
|
||||||
|
fun right(predicate: BlockPredicate, configurator: Node.() -> Unit) = relative(RelativeSide.RIGHT).also { it.predicate(predicate) }.also(configurator)
|
||||||
|
fun top(predicate: BlockPredicate, configurator: Node.() -> Unit) = relative(RelativeSide.TOP).also { it.predicate(predicate) }.also(configurator)
|
||||||
|
fun bottom(predicate: BlockPredicate, configurator: Node.() -> Unit) = relative(RelativeSide.BOTTOM).also { it.predicate(predicate) }.also(configurator)
|
||||||
|
|
||||||
|
fun build(): ShapedMultiblockFactory.Part {
|
||||||
|
return build(pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
override var strategy: Strategy = Strategy.OR_EITHER
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates new (or returns existing one) node at specified position and returns it
|
||||||
|
*/
|
||||||
|
fun node(at: BlockPos): Node {
|
||||||
|
return nodes[at] ?: Node(at)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns node which represents multiblock "root" position (relative position of 0 0 0)
|
||||||
|
*/
|
||||||
|
fun root() = node(BlockPos.ZERO)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns node which represents multiblock "root" position (relative position of 0 0 0), and configures it using provided [configurator] expression
|
||||||
|
*/
|
||||||
|
fun root(configurator: Node.() -> Unit) = node(BlockPos.ZERO).also(configurator)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a deep copy of this [ShapedMultiblockBuilder], which can be modified independently of this builder
|
||||||
|
*/
|
||||||
|
fun copy(): ShapedMultiblockBuilder {
|
||||||
|
val copied = ShapedMultiblockBuilder()
|
||||||
|
|
||||||
|
for ((k, v) in nodes) {
|
||||||
|
val node = copied.Node(k)
|
||||||
|
copied.nodes[k] = node
|
||||||
|
node.predicates.addAll(v.predicates)
|
||||||
|
}
|
||||||
|
|
||||||
|
return copied
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates [ShapedMultiblockFactory] out of this [ShapedMultiblockBuilder].
|
||||||
|
*
|
||||||
|
* Created factory does not share reference(s) to this builder, and this builder can be mutated further without consequences.
|
||||||
|
*/
|
||||||
|
fun build(): ShapedMultiblockFactory {
|
||||||
|
return ShapedMultiblockFactory(nodes.values.iterator().map { it.build() }.collect(ImmutableSet.toImmutableSet()), ImmutableList.copyOf(customChecks))
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.core.multiblock
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList
|
||||||
|
import com.google.common.collect.ImmutableSet
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.world.level.block.Rotation
|
||||||
|
import ru.dbotthepony.mc.otm.core.collect.collect
|
||||||
|
import ru.dbotthepony.mc.otm.core.collect.map
|
||||||
|
import java.util.function.Predicate
|
||||||
|
|
||||||
|
class ShapedMultiblockFactory(val north: ImmutableSet<Part>, val customChecks: ImmutableList<Predicate<IMultiblockAccess>>) {
|
||||||
|
data class Part(
|
||||||
|
val pos: BlockPos,
|
||||||
|
val strategy: Strategy,
|
||||||
|
val predicate: ImmutableList<BlockPredicate>,
|
||||||
|
val children: ImmutableList<Part>,
|
||||||
|
val blockStateTags: ImmutableSet<Any>,
|
||||||
|
val blockTags: ImmutableSet<Any>,
|
||||||
|
val blockEntityTags: ImmutableSet<BlockEntityTag<*>>,
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bakes multiblock configuration with specified [pos] as multiblock's root position
|
||||||
|
*/
|
||||||
|
fun create(pos: BlockPos): ShapedMultiblock {
|
||||||
|
return ShapedMultiblock(pos, this)
|
||||||
|
}
|
||||||
|
|
||||||
|
val south: ImmutableSet<Part> = north.iterator().map { it.copy(pos = it.pos.rotate(Rotation.CLOCKWISE_180)) }.collect(ImmutableSet.toImmutableSet())
|
||||||
|
val west: ImmutableSet<Part> = north.iterator().map { it.copy(pos = it.pos.rotate(Rotation.CLOCKWISE_90)) }.collect(ImmutableSet.toImmutableSet())
|
||||||
|
val east: ImmutableSet<Part> = north.iterator().map { it.copy(pos = it.pos.rotate(Rotation.COUNTERCLOCKWISE_90)) }.collect(ImmutableSet.toImmutableSet())
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.core.multiblock
|
||||||
|
|
||||||
|
enum class Strategy {
|
||||||
|
/**
|
||||||
|
* Tests `true` if and only if both are true:
|
||||||
|
* * at least one predicate tests `true` (or there are no predicates)
|
||||||
|
* * at least one children node tests `true` (or there are no children nodes)
|
||||||
|
*/
|
||||||
|
OR_BOTH,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as [OR_BOTH], but equates predicates and children in single check, so
|
||||||
|
* if single predicate or single children tests true, node tests true.
|
||||||
|
*
|
||||||
|
* This is default behavior of multiblock nodes, and implied operation of "or" function.
|
||||||
|
*/
|
||||||
|
OR_EITHER,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests `true` if and only if:
|
||||||
|
* * all predicates tests `true` (or there are no predicates)
|
||||||
|
* * all children node tests `true` (or there are no children nodes)
|
||||||
|
*/
|
||||||
|
AND;
|
||||||
|
}
|
@ -17,6 +17,7 @@ import ru.dbotthepony.mc.otm.core.TranslatableComponent
|
|||||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
import ru.dbotthepony.mc.otm.core.math.isNegative
|
import ru.dbotthepony.mc.otm.core.math.isNegative
|
||||||
import ru.dbotthepony.mc.otm.core.math.isZero
|
import ru.dbotthepony.mc.otm.core.math.isZero
|
||||||
|
import ru.dbotthepony.mc.otm.menu.widget.IProfiledLevelGaugeWidget
|
||||||
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
|
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.util.function.BooleanSupplier
|
import java.util.function.BooleanSupplier
|
||||||
@ -278,7 +279,7 @@ fun formatTickDuration(ticks: Int, longFormat: Boolean = false): String {
|
|||||||
*/
|
*/
|
||||||
fun formatHistory(
|
fun formatHistory(
|
||||||
result: MutableList<Either<FormattedText, TooltipComponent>>,
|
result: MutableList<Either<FormattedText, TooltipComponent>>,
|
||||||
widget: ProfiledLevelGaugeWidget<*>,
|
widget: IProfiledLevelGaugeWidget,
|
||||||
bias: Int = 0,
|
bias: Int = 0,
|
||||||
decimals: Int = 3,
|
decimals: Int = 3,
|
||||||
verbose: BooleanSupplier = never,
|
verbose: BooleanSupplier = never,
|
||||||
@ -293,7 +294,7 @@ fun formatHistory(
|
|||||||
|
|
||||||
private fun formatHistoryChart(
|
private fun formatHistoryChart(
|
||||||
result: MutableList<Either<FormattedText, TooltipComponent>>,
|
result: MutableList<Either<FormattedText, TooltipComponent>>,
|
||||||
widget: ProfiledLevelGaugeWidget<*>,
|
widget: IProfiledLevelGaugeWidget,
|
||||||
bias: Int = 0,
|
bias: Int = 0,
|
||||||
decimals: Int = 3,
|
decimals: Int = 3,
|
||||||
verbose: BooleanSupplier = never,
|
verbose: BooleanSupplier = never,
|
||||||
@ -378,7 +379,7 @@ private fun formatHistoryChart(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
val max = maxOf(widget.received.max(), widget.transferred.max())
|
val max = maxOf(widget.received.maxOrNull() ?: Decimal.ZERO, widget.transferred.maxOrNull() ?: Decimal.ZERO)
|
||||||
val labelNames = Float2ObjectArrayMap<Component>()
|
val labelNames = Float2ObjectArrayMap<Component>()
|
||||||
|
|
||||||
labelNames[0.5f] = Decimal.ZERO.formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias)
|
labelNames[0.5f] = Decimal.ZERO.formatSiComponent(suffix, decimals, formatAsReadable = verbose, bias = bias)
|
||||||
@ -446,7 +447,7 @@ private fun formatHistoryChart(
|
|||||||
|
|
||||||
private fun formatHistoryLines(
|
private fun formatHistoryLines(
|
||||||
result: MutableList<Either<FormattedText, TooltipComponent>>,
|
result: MutableList<Either<FormattedText, TooltipComponent>>,
|
||||||
widget: ProfiledLevelGaugeWidget<*>,
|
widget: IProfiledLevelGaugeWidget,
|
||||||
bias: Int = 0,
|
bias: Int = 0,
|
||||||
decimals: Int = 3,
|
decimals: Int = 3,
|
||||||
verbose: BooleanSupplier = never,
|
verbose: BooleanSupplier = never,
|
||||||
@ -476,8 +477,8 @@ private fun formatHistoryLines(
|
|||||||
}
|
}
|
||||||
|
|
||||||
addLine(
|
addLine(
|
||||||
widget.received.calcWeightedAverage(),
|
widget.received.calculateAverage(),
|
||||||
widget.transferred.calcWeightedAverage()
|
widget.transferred.calculateAverage()
|
||||||
)
|
)
|
||||||
|
|
||||||
if (verbose.asBoolean) {
|
if (verbose.asBoolean) {
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.core.util
|
||||||
|
|
||||||
|
import ru.dbotthepony.kommons.util.Delegate
|
||||||
|
import ru.dbotthepony.kommons.util.KOptional
|
||||||
|
import kotlin.properties.Delegates
|
||||||
|
import kotlin.properties.ReadWriteProperty
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Different from [Delegates.notNull] by allowing [V] to be nullable
|
||||||
|
*/
|
||||||
|
class NotNullVar<V> : Delegate<V>, ReadWriteProperty<Any?, V> {
|
||||||
|
private var value: KOptional<V> = KOptional()
|
||||||
|
|
||||||
|
override fun accept(t: V) {
|
||||||
|
value = KOptional(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun get(): V {
|
||||||
|
value.ifPresent { return it }
|
||||||
|
throw IllegalStateException("Uninitialized variable")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getValue(thisRef: Any?, property: KProperty<*>): V {
|
||||||
|
value.ifPresent { return it }
|
||||||
|
throw IllegalStateException("Uninitialized variable ${property.name}")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setValue(thisRef: Any?, property: KProperty<*>, value: V) {
|
||||||
|
accept(value)
|
||||||
|
}
|
||||||
|
}
|
@ -52,7 +52,7 @@ import ru.dbotthepony.mc.otm.network.decode
|
|||||||
import ru.dbotthepony.mc.otm.network.encode
|
import ru.dbotthepony.mc.otm.network.encode
|
||||||
import ru.dbotthepony.mc.otm.network.nullable
|
import ru.dbotthepony.mc.otm.network.nullable
|
||||||
import ru.dbotthepony.mc.otm.network.readByteListUnbounded
|
import ru.dbotthepony.mc.otm.network.readByteListUnbounded
|
||||||
import ru.dbotthepony.mc.otm.network.syncher.Syncher
|
import ru.dbotthepony.mc.otm.network.syncher.SynchableGroup
|
||||||
import ru.dbotthepony.mc.otm.network.wrap
|
import ru.dbotthepony.mc.otm.network.wrap
|
||||||
import ru.dbotthepony.mc.otm.network.writeByteListUnbounded
|
import ru.dbotthepony.mc.otm.network.writeByteListUnbounded
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -76,7 +76,7 @@ abstract class MatteryMenu(
|
|||||||
/**
|
/**
|
||||||
* Server->Client synchronizer
|
* Server->Client synchronizer
|
||||||
*/
|
*/
|
||||||
val mSynchronizer = Syncher()
|
val mSynchronizer = SynchableGroup()
|
||||||
val synchronizerRemote = mSynchronizer.Remote()
|
val synchronizerRemote = mSynchronizer.Remote()
|
||||||
val player: Player get() = inventory.player
|
val player: Player get() = inventory.player
|
||||||
val random: RandomSource = RandomSource.create()
|
val random: RandomSource = RandomSource.create()
|
||||||
|
@ -16,7 +16,7 @@ import ru.dbotthepony.mc.otm.menu.input.ItemConfigPlayerInput
|
|||||||
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
|
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
|
||||||
import ru.dbotthepony.mc.otm.registry.MMenus
|
import ru.dbotthepony.mc.otm.registry.MMenus
|
||||||
|
|
||||||
class BatteryBankMenu @JvmOverloads constructor(
|
class BatteryBankMenu(
|
||||||
p_38852_: Int,
|
p_38852_: Int,
|
||||||
inventory: Inventory,
|
inventory: Inventory,
|
||||||
tile: BatteryBankBlockEntity? = null,
|
tile: BatteryBankBlockEntity? = null,
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.menu.tech
|
||||||
|
|
||||||
|
import net.minecraft.world.entity.player.Inventory
|
||||||
|
import ru.dbotthepony.mc.otm.block.entity.blackhole.BlackHoleGeneratorBlockEntity
|
||||||
|
import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
||||||
|
import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback
|
||||||
|
import ru.dbotthepony.mc.otm.menu.widget.CombinedProfiledLevelGaugeWidget
|
||||||
|
import ru.dbotthepony.mc.otm.registry.MMenus
|
||||||
|
|
||||||
|
class BlackHoleGeneratorMenu(
|
||||||
|
p_38852_: Int,
|
||||||
|
inventory: Inventory,
|
||||||
|
tile: BlackHoleGeneratorBlockEntity? = null,
|
||||||
|
) : MatteryMenu(MMenus.BLACK_HOLE_GENERATOR, p_38852_, inventory, tile) {
|
||||||
|
val drawBuildingGuide = BooleanInputWithFeedback(this, tile?.let { it::drawBuildingGuide })
|
||||||
|
val energy = CombinedProfiledLevelGaugeWidget(this, tile?.energy)
|
||||||
|
val matter = CombinedProfiledLevelGaugeWidget(this, tile?.matter)
|
||||||
|
}
|
@ -8,7 +8,7 @@ import ru.dbotthepony.kommons.util.setValue
|
|||||||
import ru.dbotthepony.mc.otm.block.entity.RedstoneSetting
|
import ru.dbotthepony.mc.otm.block.entity.RedstoneSetting
|
||||||
import ru.dbotthepony.mc.otm.block.tech.EnergyCounterBlock
|
import ru.dbotthepony.mc.otm.block.tech.EnergyCounterBlock
|
||||||
import ru.dbotthepony.mc.otm.block.entity.tech.EnergyCounterBlockEntity
|
import ru.dbotthepony.mc.otm.block.entity.tech.EnergyCounterBlockEntity
|
||||||
import ru.dbotthepony.mc.otm.core.DecimalHistoryChart
|
import ru.dbotthepony.mc.otm.core.chart.DecimalHistoryChart
|
||||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
import ru.dbotthepony.mc.otm.core.math.toDecimal
|
import ru.dbotthepony.mc.otm.core.math.toDecimal
|
||||||
import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.menu.widget
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectIterators
|
||||||
|
import ru.dbotthepony.kommons.util.getValue
|
||||||
|
import ru.dbotthepony.mc.otm.capability.AbstractProfiledStorage
|
||||||
|
import ru.dbotthepony.mc.otm.capability.AbstractProfiledStorage.Companion.HISTORY_SIZE
|
||||||
|
import ru.dbotthepony.mc.otm.capability.IProfiledStorage
|
||||||
|
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
|
||||||
|
import ru.dbotthepony.mc.otm.capability.matter.IMatterStorage
|
||||||
|
import ru.dbotthepony.mc.otm.core.chart.CombinedDecimalHistoryChart
|
||||||
|
import ru.dbotthepony.mc.otm.core.chart.DecimalHistoryChart
|
||||||
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
|
import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
||||||
|
import ru.dbotthepony.mc.otm.network.StreamCodecs
|
||||||
|
import ru.dbotthepony.mc.otm.network.syncher.SynchableGroup
|
||||||
|
import java.util.function.Supplier
|
||||||
|
|
||||||
|
class CombinedProfiledLevelGaugeWidget<P : IProfiledStorage.Combined>(
|
||||||
|
synchronizer: SynchableGroup,
|
||||||
|
val storage: P?,
|
||||||
|
override val gauge: LevelGaugeWidget = LevelGaugeWidget(synchronizer)
|
||||||
|
) : IProfiledLevelGaugeWidget, IProfiledStorage.Combined {
|
||||||
|
constructor(
|
||||||
|
menu: MatteryMenu,
|
||||||
|
storage: P?,
|
||||||
|
gauge: LevelGaugeWidget = LevelGaugeWidget(menu.mSynchronizer)
|
||||||
|
) : this(menu.mSynchronizer, storage, gauge)
|
||||||
|
|
||||||
|
override val received = storage?.received ?: CombinedDecimalHistoryChart(Supplier { ObjectIterators.emptyIterator() }, ticks = HISTORY_SIZE)
|
||||||
|
override val transferred = storage?.transferred ?: CombinedDecimalHistoryChart(Supplier { ObjectIterators.emptyIterator() }, ticks = HISTORY_SIZE)
|
||||||
|
|
||||||
|
override val receivedThisTick by synchronizer.computed({ storage?.receivedThisTick ?: Decimal.ZERO }, StreamCodecs.DECIMAL)
|
||||||
|
override val transferredThisTick by synchronizer.computed({ storage?.transferredThisTick ?: Decimal.ZERO }, StreamCodecs.DECIMAL)
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (storage is IMatterStorage)
|
||||||
|
gauge.with(storage)
|
||||||
|
else if (storage is IMatteryEnergyStorage)
|
||||||
|
gauge.with(storage)
|
||||||
|
|
||||||
|
synchronizer.add(received)
|
||||||
|
synchronizer.add(transferred)
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,7 @@ package ru.dbotthepony.mc.otm.menu.widget
|
|||||||
|
|
||||||
import net.neoforged.neoforge.fluids.FluidStack
|
import net.neoforged.neoforge.fluids.FluidStack
|
||||||
import net.neoforged.neoforge.fluids.capability.IFluidHandler
|
import net.neoforged.neoforge.fluids.capability.IFluidHandler
|
||||||
import ru.dbotthepony.mc.otm.network.syncher.Syncher
|
import ru.dbotthepony.mc.otm.network.syncher.SynchableGroup
|
||||||
import ru.dbotthepony.kommons.util.getValue
|
import ru.dbotthepony.kommons.util.getValue
|
||||||
import ru.dbotthepony.mc.otm.container.get
|
import ru.dbotthepony.mc.otm.container.get
|
||||||
import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
||||||
@ -10,7 +10,7 @@ import ru.dbotthepony.mc.otm.network.wrap
|
|||||||
import java.util.function.IntSupplier
|
import java.util.function.IntSupplier
|
||||||
import java.util.function.Supplier
|
import java.util.function.Supplier
|
||||||
|
|
||||||
class FluidGaugeWidget(synchronizer: Syncher) {
|
class FluidGaugeWidget(synchronizer: SynchableGroup) {
|
||||||
constructor(menu: MatteryMenu) : this(menu.mSynchronizer)
|
constructor(menu: MatteryMenu) : this(menu.mSynchronizer)
|
||||||
|
|
||||||
var maxCapacitySupplier = IntSupplier { 0 }
|
var maxCapacitySupplier = IntSupplier { 0 }
|
||||||
@ -27,7 +27,7 @@ class FluidGaugeWidget(synchronizer: Syncher) {
|
|||||||
return (fluid.amount.toFloat() / maxCapacity.toFloat()).coerceIn(0f, 1f)
|
return (fluid.amount.toFloat() / maxCapacity.toFloat()).coerceIn(0f, 1f)
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(synchronizer: Syncher, fluid: IFluidHandler?, tank: Int = 0) : this(synchronizer) {
|
constructor(synchronizer: SynchableGroup, fluid: IFluidHandler?, tank: Int = 0) : this(synchronizer) {
|
||||||
if (fluid != null) {
|
if (fluid != null) {
|
||||||
with(fluid, tank)
|
with(fluid, tank)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.menu.widget
|
||||||
|
|
||||||
|
import ru.dbotthepony.mc.otm.capability.IProfiledStorage
|
||||||
|
|
||||||
|
interface IProfiledLevelGaugeWidget : IProfiledStorage {
|
||||||
|
val gauge: LevelGaugeWidget
|
||||||
|
}
|
@ -7,10 +7,10 @@ import ru.dbotthepony.mc.otm.capability.matter.IPatternStorage
|
|||||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
||||||
import ru.dbotthepony.mc.otm.network.StreamCodecs
|
import ru.dbotthepony.mc.otm.network.StreamCodecs
|
||||||
import ru.dbotthepony.mc.otm.network.syncher.Syncher
|
import ru.dbotthepony.mc.otm.network.syncher.SynchableGroup
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
class LevelGaugeWidget(synchronizer: Syncher) {
|
class LevelGaugeWidget(synchronizer: SynchableGroup) {
|
||||||
constructor(menu: MatteryMenu) : this(menu.mSynchronizer)
|
constructor(menu: MatteryMenu) : this(menu.mSynchronizer)
|
||||||
|
|
||||||
var levelProvider = { Decimal.ONE }
|
var levelProvider = { Decimal.ONE }
|
||||||
@ -56,7 +56,7 @@ class LevelGaugeWidget(synchronizer: Syncher) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
synchronizer: Syncher,
|
synchronizer: SynchableGroup,
|
||||||
power: IMatteryEnergyStorage?
|
power: IMatteryEnergyStorage?
|
||||||
) : this(synchronizer) {
|
) : this(synchronizer) {
|
||||||
if (power != null) {
|
if (power != null) {
|
||||||
@ -65,7 +65,7 @@ class LevelGaugeWidget(synchronizer: Syncher) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
synchronizer: Syncher,
|
synchronizer: SynchableGroup,
|
||||||
matter: IMatterStorage?
|
matter: IMatterStorage?
|
||||||
) : this(synchronizer) {
|
) : this(synchronizer) {
|
||||||
if (matter != null) {
|
if (matter != null) {
|
||||||
@ -74,7 +74,7 @@ class LevelGaugeWidget(synchronizer: Syncher) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
synchronizer: Syncher,
|
synchronizer: SynchableGroup,
|
||||||
patterns: IPatternStorage?
|
patterns: IPatternStorage?
|
||||||
) : this(synchronizer) {
|
) : this(synchronizer) {
|
||||||
if (patterns != null) {
|
if (patterns != null) {
|
||||||
@ -83,7 +83,7 @@ class LevelGaugeWidget(synchronizer: Syncher) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
synchronizer: Syncher,
|
synchronizer: SynchableGroup,
|
||||||
level: () -> Decimal,
|
level: () -> Decimal,
|
||||||
maxLevel: () -> Decimal,
|
maxLevel: () -> Decimal,
|
||||||
) : this(synchronizer) {
|
) : this(synchronizer) {
|
||||||
|
@ -3,30 +3,31 @@ package ru.dbotthepony.mc.otm.menu.widget
|
|||||||
import ru.dbotthepony.kommons.util.getValue
|
import ru.dbotthepony.kommons.util.getValue
|
||||||
import ru.dbotthepony.mc.otm.capability.AbstractProfiledStorage
|
import ru.dbotthepony.mc.otm.capability.AbstractProfiledStorage
|
||||||
import ru.dbotthepony.mc.otm.capability.AbstractProfiledStorage.Companion.HISTORY_SIZE
|
import ru.dbotthepony.mc.otm.capability.AbstractProfiledStorage.Companion.HISTORY_SIZE
|
||||||
|
import ru.dbotthepony.mc.otm.capability.IProfiledStorage
|
||||||
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
|
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
|
||||||
import ru.dbotthepony.mc.otm.capability.matter.IMatterStorage
|
import ru.dbotthepony.mc.otm.capability.matter.IMatterStorage
|
||||||
import ru.dbotthepony.mc.otm.core.DecimalHistoryChart
|
import ru.dbotthepony.mc.otm.core.chart.DecimalHistoryChart
|
||||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
||||||
import ru.dbotthepony.mc.otm.network.StreamCodecs
|
import ru.dbotthepony.mc.otm.network.StreamCodecs
|
||||||
import ru.dbotthepony.mc.otm.network.syncher.Syncher
|
import ru.dbotthepony.mc.otm.network.syncher.SynchableGroup
|
||||||
|
|
||||||
class ProfiledLevelGaugeWidget<P : AbstractProfiledStorage<*>>(
|
class ProfiledLevelGaugeWidget<P : AbstractProfiledStorage<*>>(
|
||||||
synchronizer: Syncher,
|
synchronizer: SynchableGroup,
|
||||||
val storage: P?,
|
val storage: P?,
|
||||||
val gauge: LevelGaugeWidget = LevelGaugeWidget(synchronizer)
|
override val gauge: LevelGaugeWidget = LevelGaugeWidget(synchronizer)
|
||||||
) {
|
) : IProfiledLevelGaugeWidget {
|
||||||
constructor(
|
constructor(
|
||||||
menu: MatteryMenu,
|
menu: MatteryMenu,
|
||||||
storage: P?,
|
storage: P?,
|
||||||
gauge: LevelGaugeWidget = LevelGaugeWidget(menu.mSynchronizer)
|
gauge: LevelGaugeWidget = LevelGaugeWidget(menu.mSynchronizer)
|
||||||
) : this(menu.mSynchronizer, storage, gauge)
|
) : this(menu.mSynchronizer, storage, gauge)
|
||||||
|
|
||||||
val received = storage?.received ?: DecimalHistoryChart(ticks = HISTORY_SIZE)
|
override val received = storage?.received ?: DecimalHistoryChart(ticks = HISTORY_SIZE)
|
||||||
val transferred = storage?.transferred ?: DecimalHistoryChart(ticks = HISTORY_SIZE)
|
override val transferred = storage?.transferred ?: DecimalHistoryChart(ticks = HISTORY_SIZE)
|
||||||
|
|
||||||
val receivedThisTick by synchronizer.computed({ storage?.receivedThisTick ?: Decimal.ZERO }, StreamCodecs.DECIMAL)
|
override val receivedThisTick by synchronizer.computed({ storage?.receivedThisTick ?: Decimal.ZERO }, StreamCodecs.DECIMAL)
|
||||||
val transferredThisTick by synchronizer.computed({ storage?.transferredThisTick ?: Decimal.ZERO }, StreamCodecs.DECIMAL)
|
override val transferredThisTick by synchronizer.computed({ storage?.transferredThisTick ?: Decimal.ZERO }, StreamCodecs.DECIMAL)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (storage is IMatterStorage)
|
if (storage is IMatterStorage)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package ru.dbotthepony.mc.otm.menu.widget
|
package ru.dbotthepony.mc.otm.menu.widget
|
||||||
|
|
||||||
import ru.dbotthepony.mc.otm.network.syncher.Syncher
|
import ru.dbotthepony.mc.otm.network.syncher.SynchableGroup
|
||||||
import ru.dbotthepony.kommons.util.getValue
|
import ru.dbotthepony.kommons.util.getValue
|
||||||
import ru.dbotthepony.mc.otm.block.entity.MachineJobEventLoop
|
import ru.dbotthepony.mc.otm.block.entity.MachineJobEventLoop
|
||||||
import ru.dbotthepony.mc.otm.block.entity.MatteryWorkerBlockEntity
|
import ru.dbotthepony.mc.otm.block.entity.MatteryWorkerBlockEntity
|
||||||
@ -9,7 +9,7 @@ import ru.dbotthepony.mc.otm.menu.MatteryMenu
|
|||||||
import java.util.function.BooleanSupplier
|
import java.util.function.BooleanSupplier
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
class ProgressGaugeWidget(synchronizer: Syncher) {
|
class ProgressGaugeWidget(synchronizer: SynchableGroup) {
|
||||||
constructor(menu: MatteryMenu) : this(menu.mSynchronizer)
|
constructor(menu: MatteryMenu) : this(menu.mSynchronizer)
|
||||||
|
|
||||||
var progressSupplier: FloatSupplier = FloatSupplier { 0f }
|
var progressSupplier: FloatSupplier = FloatSupplier { 0f }
|
||||||
@ -44,14 +44,14 @@ class ProgressGaugeWidget(synchronizer: Syncher) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
synchronizer: Syncher,
|
synchronizer: SynchableGroup,
|
||||||
progress: FloatSupplier
|
progress: FloatSupplier
|
||||||
) : this(synchronizer) {
|
) : this(synchronizer) {
|
||||||
this.progressSupplier = progress
|
this.progressSupplier = progress
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
synchronizer: Syncher,
|
synchronizer: SynchableGroup,
|
||||||
blockEntity: MatteryWorkerBlockEntity<*>?
|
blockEntity: MatteryWorkerBlockEntity<*>?
|
||||||
) : this(synchronizer) {
|
) : this(synchronizer) {
|
||||||
if (blockEntity != null) {
|
if (blockEntity != null) {
|
||||||
@ -60,7 +60,7 @@ class ProgressGaugeWidget(synchronizer: Syncher) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
synchronizer: Syncher,
|
synchronizer: SynchableGroup,
|
||||||
job: MachineJobEventLoop<*>?
|
job: MachineJobEventLoop<*>?
|
||||||
) : this(synchronizer) {
|
) : this(synchronizer) {
|
||||||
if (job != null) {
|
if (job != null) {
|
||||||
|
@ -0,0 +1,94 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.network.syncher
|
||||||
|
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||||
|
import ru.dbotthepony.kommons.util.KOptional
|
||||||
|
import ru.dbotthepony.mc.otm.network.MatteryStreamCodec
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList
|
||||||
|
|
||||||
|
class DynamicSynchable<D, V : ISynchable>(
|
||||||
|
initialValue: KOptional<D>,
|
||||||
|
private val codec: MatteryStreamCodec<RegistryFriendlyByteBuf, D>,
|
||||||
|
private val factory: (D) -> V
|
||||||
|
) : ISynchable {
|
||||||
|
constructor(codec: MatteryStreamCodec<RegistryFriendlyByteBuf, D>, factory: (D) -> V) : this(KOptional(), codec, factory)
|
||||||
|
|
||||||
|
private var _synchable: KOptional<V> = KOptional()
|
||||||
|
|
||||||
|
val synchable: V
|
||||||
|
get() = _synchable.orThrow { IllegalStateException("Not initialized") }
|
||||||
|
|
||||||
|
private var _value: KOptional<D> = KOptional()
|
||||||
|
private val remotes = CopyOnWriteArrayList<RemoteState>()
|
||||||
|
|
||||||
|
var value: D
|
||||||
|
get() = _value.orThrow { IllegalStateException("Not initialized") }
|
||||||
|
set(value) {
|
||||||
|
if (_value.isEmpty || _value.value != value) {
|
||||||
|
_value = KOptional(value)
|
||||||
|
_synchable = KOptional(factory(value))
|
||||||
|
|
||||||
|
remotes.forEach { it.invalidateFull() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
initialValue.ifPresent {
|
||||||
|
_value = KOptional(it)
|
||||||
|
_synchable = KOptional(factory(it))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class RemoteState(val listener: Runnable) : IRemoteState {
|
||||||
|
private var valueChanged = true
|
||||||
|
private var remote = synchable.createRemoteState(listener)
|
||||||
|
|
||||||
|
override fun write(stream: RegistryFriendlyByteBuf) {
|
||||||
|
stream.writeBoolean(valueChanged)
|
||||||
|
|
||||||
|
if (valueChanged) {
|
||||||
|
codec.encode(stream, _value.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
valueChanged = false
|
||||||
|
remote.write(stream)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun invalidateFull() {
|
||||||
|
valueChanged = true
|
||||||
|
remote.close()
|
||||||
|
remote = synchable.createRemoteState(listener)
|
||||||
|
listener.run()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun invalidate() {
|
||||||
|
valueChanged = true
|
||||||
|
remote.invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
remotes.remove(this)
|
||||||
|
remote.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override var isRemote: Boolean = false
|
||||||
|
private set
|
||||||
|
|
||||||
|
override fun read(stream: RegistryFriendlyByteBuf) {
|
||||||
|
isRemote = true
|
||||||
|
|
||||||
|
if (stream.readBoolean()) {
|
||||||
|
_value = KOptional(codec.decode(stream))
|
||||||
|
_synchable = KOptional(factory(_value.value))
|
||||||
|
}
|
||||||
|
|
||||||
|
_synchable.value.read(stream)
|
||||||
|
}
|
||||||
|
|
||||||
|
override val hasRemotes: Boolean
|
||||||
|
get() = remotes.isNotEmpty()
|
||||||
|
|
||||||
|
override fun createRemoteState(listener: Runnable): IRemoteState {
|
||||||
|
return RemoteState(listener)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,353 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.network.syncher
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntArraySet
|
||||||
|
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||||
|
import ru.dbotthepony.kommons.util.KOptional
|
||||||
|
import ru.dbotthepony.mc.otm.core.IDAllocator
|
||||||
|
import java.io.Closeable
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Syncher group/set, which deals with synchables of only one type, and which are created and removed
|
||||||
|
* on remote (e.g. server adding and removing synchables at will), which makes it distinct from
|
||||||
|
* [SynchableGroup], in which attached synchables are created/removed manually on both sides.
|
||||||
|
*/
|
||||||
|
class DynamicSynchableGroup<T : ISynchable>(
|
||||||
|
/**
|
||||||
|
* Constructs new [T] instance locally, when remote created one.
|
||||||
|
* Data written by [writer] must be read here, if there is any.
|
||||||
|
*/
|
||||||
|
private val reader: RegistryFriendlyByteBuf.() -> T,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows to write additional data to network stream during
|
||||||
|
* first-time networking of [T] to remote
|
||||||
|
*/
|
||||||
|
private val writer: T.(RegistryFriendlyByteBuf) -> Unit = {}
|
||||||
|
) : ISynchable, MutableSet<T> {
|
||||||
|
constructor(factory: () -> T) : this({ factory() }, {})
|
||||||
|
|
||||||
|
private inner class RemoteState(val listener: Runnable) : IRemoteState {
|
||||||
|
private inner class RemoteSlot(val slot: Slot<T>, fromConstructor: Boolean) : Runnable, Closeable {
|
||||||
|
val remoteState = slot.synchable.createRemoteState(this)
|
||||||
|
val isDirty = AtomicBoolean(true)
|
||||||
|
var isRemoved = false
|
||||||
|
private set
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (!fromConstructor)
|
||||||
|
removals.remove(slot.id)
|
||||||
|
|
||||||
|
firstTime.add(this)
|
||||||
|
dirty.add(this)
|
||||||
|
|
||||||
|
if (firstTime.size != 1 && !fromConstructor) {
|
||||||
|
listener.run()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun run() {
|
||||||
|
if (isDirty.compareAndSet(false, true)) {
|
||||||
|
dirty.add(this)
|
||||||
|
listener.run()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun markClean() {
|
||||||
|
isDirty.set(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
isRemoved = true
|
||||||
|
isDirty.set(true)
|
||||||
|
remoteState.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val remotes = HashMap<Slot<T>, RemoteSlot>()
|
||||||
|
private val dirty = ConcurrentLinkedQueue<RemoteSlot>()
|
||||||
|
private val firstTime = ArrayList<RemoteSlot>()
|
||||||
|
private val removals = IntArraySet()
|
||||||
|
private var sendClear = false
|
||||||
|
|
||||||
|
init {
|
||||||
|
value2slot.values.forEach {
|
||||||
|
remotes[it] = RemoteSlot(it, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
remoteStates.add(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun add(element: Slot<T>) {
|
||||||
|
remotes[element] = RemoteSlot(element, false)
|
||||||
|
listener.run()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun remove(element: Slot<T>) {
|
||||||
|
val removed = remotes.remove(element)
|
||||||
|
checkNotNull(removed)
|
||||||
|
check(removed.slot == element)
|
||||||
|
removed.close()
|
||||||
|
listener.run()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun write(stream: RegistryFriendlyByteBuf) {
|
||||||
|
if (sendClear) {
|
||||||
|
sendClear = false
|
||||||
|
stream.writeByte(CLEAR)
|
||||||
|
}
|
||||||
|
|
||||||
|
run {
|
||||||
|
val itr = removals.iterator()
|
||||||
|
|
||||||
|
while (itr.hasNext()) {
|
||||||
|
stream.writeByte(REMOVE_ENTRY)
|
||||||
|
stream.writeVarInt(itr.nextInt())
|
||||||
|
}
|
||||||
|
|
||||||
|
removals.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
firstTime.forEach {
|
||||||
|
stream.writeByte(ADD_ENTRY)
|
||||||
|
stream.writeVarInt(it.slot.id)
|
||||||
|
writer(it.slot.synchable, stream)
|
||||||
|
}
|
||||||
|
|
||||||
|
firstTime.clear()
|
||||||
|
|
||||||
|
val seen = ReferenceOpenHashSet<RemoteSlot>()
|
||||||
|
val slots = ArrayList<RemoteSlot>()
|
||||||
|
var next = dirty.poll()
|
||||||
|
|
||||||
|
while (next != null) {
|
||||||
|
if (!next.isRemoved) {
|
||||||
|
val status = seen.add(next)
|
||||||
|
next.markClean()
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
slots.add(next)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
next = dirty.poll()
|
||||||
|
}
|
||||||
|
|
||||||
|
slots.forEach {
|
||||||
|
stream.writeByte(SYNC_ENTRY)
|
||||||
|
stream.writeVarInt(it.slot.id)
|
||||||
|
it.remoteState.write(stream)
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.writeByte(END)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun invalidate() {
|
||||||
|
firstTime.clear()
|
||||||
|
dirty.clear()
|
||||||
|
removals.clear() // is it necessary?
|
||||||
|
|
||||||
|
remotes.values.forEach {
|
||||||
|
firstTime.add(it)
|
||||||
|
dirty.add(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
remotes.values.forEach { it.close() }
|
||||||
|
|
||||||
|
firstTime.clear()
|
||||||
|
dirty.clear()
|
||||||
|
removals.clear()
|
||||||
|
remotes.clear()
|
||||||
|
|
||||||
|
remoteStates.remove(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clear() {
|
||||||
|
sendClear = true
|
||||||
|
firstTime.clear()
|
||||||
|
dirty.clear()
|
||||||
|
removals.clear()
|
||||||
|
remotes.values.forEach { it.close() }
|
||||||
|
remotes.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class Slot<T : Any>(val synchable: T, val id: Int)
|
||||||
|
|
||||||
|
private val remoteStates = CopyOnWriteArrayList<RemoteState>()
|
||||||
|
private val value2slot = HashMap<T, Slot<T>>()
|
||||||
|
private val id2slot = Int2ObjectOpenHashMap<Slot<T>>()
|
||||||
|
private val idAllocator = IDAllocator()
|
||||||
|
|
||||||
|
override val hasRemotes: Boolean
|
||||||
|
get() = remoteStates.isNotEmpty()
|
||||||
|
|
||||||
|
override var isRemote: Boolean = false
|
||||||
|
private set
|
||||||
|
|
||||||
|
override fun read(stream: RegistryFriendlyByteBuf) {
|
||||||
|
isRemote = true
|
||||||
|
var action = stream.readByte().toInt()
|
||||||
|
|
||||||
|
while (action != END) {
|
||||||
|
when (action) {
|
||||||
|
REMOVE_ENTRY -> {
|
||||||
|
val id = stream.readVarInt()
|
||||||
|
val slot = checkNotNull(id2slot.remove(id)) { "No such slot with ID: $id" }
|
||||||
|
check(value2slot.remove(slot.synchable) == value2slot)
|
||||||
|
remoteStates.forEach { it.remove(slot) }
|
||||||
|
}
|
||||||
|
|
||||||
|
ADD_ENTRY -> {
|
||||||
|
val id = stream.readVarInt()
|
||||||
|
|
||||||
|
// it is expected that during very frequent updates entry gets removed and new entry takes up their ID,
|
||||||
|
// so we allow implicit removal if ID is allocated by some other entry before old entry could have a chance
|
||||||
|
// to broadcast that it have been removed
|
||||||
|
|
||||||
|
if (id2slot.containsKey(id)) {
|
||||||
|
val slot = id2slot.remove(id)!!
|
||||||
|
check(value2slot.remove(slot.synchable) == value2slot)
|
||||||
|
remoteStates.forEach { it.remove(slot) }
|
||||||
|
}
|
||||||
|
|
||||||
|
val value = reader(stream)
|
||||||
|
val slot = Slot(value, id)
|
||||||
|
value2slot[value] = slot
|
||||||
|
id2slot[id] = slot
|
||||||
|
}
|
||||||
|
|
||||||
|
SYNC_ENTRY -> {
|
||||||
|
val id = stream.readVarInt()
|
||||||
|
val slot = checkNotNull(id2slot.get(id)) { "No such slot with ID: $id" }
|
||||||
|
slot.synchable.read(stream)
|
||||||
|
}
|
||||||
|
|
||||||
|
CLEAR -> {
|
||||||
|
value2slot.clear()
|
||||||
|
id2slot.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> throw IllegalArgumentException("Unknown network action: $action")
|
||||||
|
}
|
||||||
|
|
||||||
|
action = stream.readByte().toInt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createRemoteState(listener: Runnable): IRemoteState {
|
||||||
|
return RemoteState(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun add(element: T): Boolean {
|
||||||
|
if (element in value2slot) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
val slot = Slot(element, idAllocator.allocate())
|
||||||
|
value2slot[element] = slot
|
||||||
|
id2slot[slot.id] = slot
|
||||||
|
remoteStates.forEach { it.add(slot) }
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override val size: Int
|
||||||
|
get() = value2slot.size
|
||||||
|
|
||||||
|
override fun addAll(elements: Collection<T>): Boolean {
|
||||||
|
var any = false
|
||||||
|
elements.forEach { any = add(it) || any }
|
||||||
|
return any
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun clear() {
|
||||||
|
if (value2slot.isNotEmpty()) {
|
||||||
|
value2slot.clear()
|
||||||
|
id2slot.clear()
|
||||||
|
|
||||||
|
remoteStates.forEach { it.clear() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun contains(element: T): Boolean {
|
||||||
|
return element in value2slot
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun containsAll(elements: Collection<T>): Boolean {
|
||||||
|
return value2slot.keys.containsAll(elements)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isEmpty(): Boolean {
|
||||||
|
return value2slot.isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun iterator(): MutableIterator<T> {
|
||||||
|
return object : MutableIterator<T> {
|
||||||
|
private val parent = value2slot.values.iterator()
|
||||||
|
private var last: KOptional<Slot<T>> = KOptional()
|
||||||
|
|
||||||
|
override fun hasNext(): Boolean {
|
||||||
|
return parent.hasNext()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun next(): T {
|
||||||
|
val slot = parent.next()
|
||||||
|
last = KOptional(slot)
|
||||||
|
return slot.synchable
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun remove() {
|
||||||
|
parent.remove()
|
||||||
|
val slot = last.value
|
||||||
|
checkNotNull(id2slot.remove(slot.id))
|
||||||
|
remoteStates.forEach { it.remove(slot) }
|
||||||
|
last = KOptional()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun remove(element: T): Boolean {
|
||||||
|
if (element !in value2slot) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
val slot = value2slot.remove(element)!!
|
||||||
|
checkNotNull(id2slot.remove(slot.id))
|
||||||
|
remoteStates.forEach { it.remove(slot) }
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeAll(elements: Collection<T>): Boolean {
|
||||||
|
var any = false
|
||||||
|
elements.forEach { any = remove(it) || any }
|
||||||
|
return any
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun retainAll(elements: Collection<T>): Boolean {
|
||||||
|
val itr = iterator()
|
||||||
|
var any = false
|
||||||
|
|
||||||
|
for (element in itr) {
|
||||||
|
if (element !in elements) {
|
||||||
|
itr.remove()
|
||||||
|
any = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return any
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val END = 0
|
||||||
|
private const val ADD_ENTRY = 1
|
||||||
|
private const val SYNC_ENTRY = 2
|
||||||
|
private const val REMOVE_ENTRY = 3
|
||||||
|
private const val CLEAR = 4
|
||||||
|
}
|
||||||
|
}
|
@ -3,19 +3,32 @@ package ru.dbotthepony.mc.otm.network.syncher
|
|||||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||||
import ru.dbotthepony.kommons.util.Observer
|
import ru.dbotthepony.kommons.util.Observer
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object capable of synching its state to remotes
|
||||||
|
*/
|
||||||
interface ISynchable : Observer {
|
interface ISynchable : Observer {
|
||||||
fun read(stream: RegistryFriendlyByteBuf)
|
fun read(stream: RegistryFriendlyByteBuf)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provided [listener] is to be considered thread-safe, and can be called at any time
|
* Provided [listener] is to be considered thread-safe, and can be called at any time
|
||||||
*
|
*
|
||||||
* Created [IRemoteState] is implied to have [IRemoteState.invalidate] called implicitly internally
|
* Created [IRemoteState] is implied to have [IRemoteState.invalidate] called implicitly internally upon creation
|
||||||
*/
|
*/
|
||||||
fun createRemoteState(listener: Runnable): IRemoteState
|
fun createRemoteState(listener: Runnable): IRemoteState
|
||||||
|
|
||||||
val shouldBeObserved: Boolean
|
val shouldBeObserved: Boolean
|
||||||
get() = false
|
get() = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whenever this synchable has remotes, optional operation and can safely always return false
|
||||||
|
*/
|
||||||
|
val hasRemotes: Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whenever [read] has been called at least once
|
||||||
|
*/
|
||||||
|
val isRemote: Boolean
|
||||||
|
|
||||||
override fun observe(): Boolean {
|
override fun observe(): Boolean {
|
||||||
// no op
|
// no op
|
||||||
return false
|
return false
|
||||||
|
@ -5,6 +5,7 @@ import ru.dbotthepony.kommons.util.Listenable
|
|||||||
import ru.dbotthepony.kommons.util.ListenableDelegate
|
import ru.dbotthepony.kommons.util.ListenableDelegate
|
||||||
import ru.dbotthepony.kommons.util.Observer
|
import ru.dbotthepony.kommons.util.Observer
|
||||||
import ru.dbotthepony.mc.otm.network.MatteryStreamCodec
|
import ru.dbotthepony.mc.otm.network.MatteryStreamCodec
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
import java.util.function.Consumer
|
import java.util.function.Consumer
|
||||||
import java.util.function.Supplier
|
import java.util.function.Supplier
|
||||||
|
|
||||||
@ -13,14 +14,26 @@ class SynchableDelegate<V>(val delegate: ListenableDelegate<V>, val codec: Matte
|
|||||||
|
|
||||||
private val listeners = Listenable.Impl<V>()
|
private val listeners = Listenable.Impl<V>()
|
||||||
private val l = delegate.addListener(listeners)
|
private val l = delegate.addListener(listeners)
|
||||||
|
private val remoteCount = AtomicInteger()
|
||||||
|
|
||||||
|
override val hasRemotes: Boolean
|
||||||
|
get() = remoteCount.get() > 0
|
||||||
|
|
||||||
|
override var isRemote: Boolean = false
|
||||||
|
private set
|
||||||
|
|
||||||
override fun read(stream: RegistryFriendlyByteBuf) {
|
override fun read(stream: RegistryFriendlyByteBuf) {
|
||||||
|
isRemote = false
|
||||||
delegate.accept(codec.decode(stream))
|
delegate.accept(codec.decode(stream))
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class Remote(listener: Runnable) : IRemoteState {
|
inner class Remote(listener: Runnable) : IRemoteState {
|
||||||
private val l = listeners.addListener(listener)
|
private val l = listeners.addListener(listener)
|
||||||
|
|
||||||
|
init {
|
||||||
|
remoteCount.incrementAndGet()
|
||||||
|
}
|
||||||
|
|
||||||
override fun write(stream: RegistryFriendlyByteBuf) {
|
override fun write(stream: RegistryFriendlyByteBuf) {
|
||||||
codec.encode(stream, delegate.get())
|
codec.encode(stream, delegate.get())
|
||||||
}
|
}
|
||||||
@ -31,6 +44,7 @@ class SynchableDelegate<V>(val delegate: ListenableDelegate<V>, val codec: Matte
|
|||||||
|
|
||||||
override fun close() {
|
override fun close() {
|
||||||
l.remove()
|
l.remove()
|
||||||
|
remoteCount.decrementAndGet() // unsafe because close() can be called multiple times
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,8 @@ import ru.dbotthepony.kommons.util.Listenable
|
|||||||
import ru.dbotthepony.kommons.util.ListenableDelegate
|
import ru.dbotthepony.kommons.util.ListenableDelegate
|
||||||
import ru.dbotthepony.kommons.util.Observer
|
import ru.dbotthepony.kommons.util.Observer
|
||||||
import ru.dbotthepony.mc.otm.OTM_CLEANER
|
import ru.dbotthepony.mc.otm.OTM_CLEANER
|
||||||
|
import ru.dbotthepony.mc.otm.core.collect.filterNotNull
|
||||||
|
import ru.dbotthepony.mc.otm.core.collect.map
|
||||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
import ru.dbotthepony.mc.otm.network.MatteryStreamCodec
|
import ru.dbotthepony.mc.otm.network.MatteryStreamCodec
|
||||||
import ru.dbotthepony.mc.otm.network.StreamCodecs
|
import ru.dbotthepony.mc.otm.network.StreamCodecs
|
||||||
@ -37,29 +39,36 @@ import java.util.function.DoubleSupplier
|
|||||||
import java.util.function.IntSupplier
|
import java.util.function.IntSupplier
|
||||||
import java.util.function.LongSupplier
|
import java.util.function.LongSupplier
|
||||||
import java.util.function.Supplier
|
import java.util.function.Supplier
|
||||||
import kotlin.collections.ArrayList
|
|
||||||
import kotlin.concurrent.withLock
|
import kotlin.concurrent.withLock
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Universal, one-to-many delegate/value synchronizer.
|
* Universal, one-to-many delegate/value synchronizer.
|
||||||
*
|
*
|
||||||
* Values and delegates can be attached using [add], or by constructing subclassed directly.
|
* Values and delegates can be attached using [add];
|
||||||
* Delta changes are tracked by [Syncher.Remote] instances.
|
* Delta changes are tracked by [SynchableGroup.Remote] instances.
|
||||||
*
|
|
||||||
* In general, this class is not meant to be _structurally_ concurrently mutated by different threads,
|
|
||||||
* to avoid structure corruption a lock is employed.
|
|
||||||
*
|
*
|
||||||
* Attached delegates can be safely mutated by multiple threads concurrently
|
* Attached delegates can be safely mutated by multiple threads concurrently
|
||||||
* (attached [ListenableDelegate] can call [Listenable.addListener] callback from different threads).
|
* (attached [ListenableDelegate] can call [Listenable.addListener] callback from different threads).
|
||||||
|
*
|
||||||
|
* Generally, this class is considered to be "root" of [ISynchable] operation (due to API), but this is not strictly necessary,
|
||||||
|
* and [SynchableGroup] can be as well embedded into other [SynchableGroup]s.
|
||||||
*/
|
*/
|
||||||
@Suppress("UNUSED")
|
@Suppress("UNUSED")
|
||||||
class Syncher : Observer, ISynchable {
|
class SynchableGroup : Observer, ISynchable, Iterable<ISynchable> {
|
||||||
private val lock = ReentrantLock()
|
private val lock = ReentrantLock()
|
||||||
private val slots = ArrayList<Slot?>()
|
private val slots = ArrayList<Slot?>()
|
||||||
private val gaps = IntAVLTreeSet()
|
private val gaps = IntAVLTreeSet()
|
||||||
private val observers = ArrayList<Slot>()
|
private val observers = ArrayList<Slot>()
|
||||||
private val remotes = ArrayList<Remote>()
|
private val remotes = ArrayList<Remote>()
|
||||||
private var isRemote = false
|
override var isRemote = false
|
||||||
|
private set
|
||||||
|
|
||||||
|
override val hasRemotes: Boolean
|
||||||
|
get() = remotes.isNotEmpty()
|
||||||
|
|
||||||
|
override fun iterator(): Iterator<ISynchable> {
|
||||||
|
return slots.iterator().map { it?.synchable }.filterNotNull()
|
||||||
|
}
|
||||||
|
|
||||||
override fun observe(): Boolean {
|
override fun observe(): Boolean {
|
||||||
var any = false
|
var any = false
|
||||||
@ -80,7 +89,7 @@ class Syncher : Observer, ISynchable {
|
|||||||
var readID = stream.readVarInt()
|
var readID = stream.readVarInt()
|
||||||
|
|
||||||
while (readID > 0) {
|
while (readID > 0) {
|
||||||
val slot = lock.withLock { slots.getOrNull(readID - 1) ?: throw IndexOutOfBoundsException("Unknown networked slot ${readID - 1}!") }
|
val slot = slots.getOrNull(readID - 1) ?: throw IndexOutOfBoundsException("Unknown networked slot ${readID - 1}!")
|
||||||
slot.synchable.read(stream)
|
slot.synchable.read(stream)
|
||||||
readID = stream.readVarInt()
|
readID = stream.readVarInt()
|
||||||
}
|
}
|
||||||
@ -95,18 +104,17 @@ class Syncher : Observer, ISynchable {
|
|||||||
internal inner class Slot(val synchable: ISynchable) : Closeable, Observer {
|
internal inner class Slot(val synchable: ISynchable) : Closeable, Observer {
|
||||||
val id: Int
|
val id: Int
|
||||||
|
|
||||||
private val isRemoved = AtomicBoolean()
|
private var isRemoved = false
|
||||||
val remoteSlots = CopyOnWriteArrayList<Remote.RemoteSlot>()
|
val remoteSlots = CopyOnWriteArrayList<Remote.RemoteSlot>()
|
||||||
|
|
||||||
override fun close() {
|
override fun close() {
|
||||||
if (isRemoved.compareAndSet(false, true)) {
|
if (!isRemoved) {
|
||||||
lock.withLock {
|
isRemoved = true
|
||||||
slots[id] = null
|
slots[id] = null
|
||||||
gaps.add(id)
|
gaps.add(id)
|
||||||
|
|
||||||
observers.remove(this)
|
observers.remove(this)
|
||||||
remoteSlots.forEach { it.remove() }
|
remoteSlots.forEach { it.remove() }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,7 +123,7 @@ class Syncher : Observer, ISynchable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun markDirty() {
|
private fun markDirty() {
|
||||||
if (!isRemote && !isRemoved.get()) {
|
if (!isRemote && !isRemoved) {
|
||||||
remoteSlots.forEach {
|
remoteSlots.forEach {
|
||||||
it.markDirty()
|
it.markDirty()
|
||||||
}
|
}
|
||||||
@ -375,7 +383,7 @@ class Syncher : Observer, ISynchable {
|
|||||||
/**
|
/**
|
||||||
* Returns null if this remote is clean.
|
* Returns null if this remote is clean.
|
||||||
*
|
*
|
||||||
* [Syncher.observe] is not called automatically for performance
|
* [SynchableGroup.observe] is not called automatically for performance
|
||||||
* reasons, you must call it manually.
|
* reasons, you must call it manually.
|
||||||
*/
|
*/
|
||||||
fun write(registry: RegistryAccess): ByteArrayList? {
|
fun write(registry: RegistryAccess): ByteArrayList? {
|
||||||
@ -421,18 +429,17 @@ class Syncher : Observer, ISynchable {
|
|||||||
|
|
||||||
override fun close() {
|
override fun close() {
|
||||||
if (!isRemoved) {
|
if (!isRemoved) {
|
||||||
|
isRemoved = true
|
||||||
|
|
||||||
lock.withLock {
|
lock.withLock {
|
||||||
if (!isRemoved) {
|
remoteSlots.forEach {
|
||||||
remoteSlots.forEach {
|
it.remove()
|
||||||
it.remove()
|
it.slot.remoteSlots.remove(it)
|
||||||
it.slot.remoteSlots.remove(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
remotes.remove(this)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
dirty.clear()
|
remotes.remove(this)
|
||||||
|
dirty.clear()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -12,7 +12,12 @@ class SynchableMap<K, V>(
|
|||||||
val keyCodec: MatteryStreamCodec<RegistryFriendlyByteBuf, K>,
|
val keyCodec: MatteryStreamCodec<RegistryFriendlyByteBuf, K>,
|
||||||
val valueCodec: MatteryStreamCodec<RegistryFriendlyByteBuf, V>
|
val valueCodec: MatteryStreamCodec<RegistryFriendlyByteBuf, V>
|
||||||
) : ISynchable {
|
) : ISynchable {
|
||||||
|
override var isRemote: Boolean = false
|
||||||
|
private set
|
||||||
|
|
||||||
override fun read(stream: RegistryFriendlyByteBuf) {
|
override fun read(stream: RegistryFriendlyByteBuf) {
|
||||||
|
isRemote = true
|
||||||
|
|
||||||
var action = stream.readByte().toInt()
|
var action = stream.readByte().toInt()
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
@ -105,6 +110,9 @@ class SynchableMap<K, V>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val hasRemotes: Boolean
|
||||||
|
get() = remoteSlots.isNotEmpty()
|
||||||
|
|
||||||
override fun createRemoteState(listener: Runnable): IRemoteState {
|
override fun createRemoteState(listener: Runnable): IRemoteState {
|
||||||
return RemoteState(listener)
|
return RemoteState(listener)
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import ru.dbotthepony.kommons.util.Listenable
|
|||||||
import ru.dbotthepony.kommons.util.ListenableDelegate
|
import ru.dbotthepony.kommons.util.ListenableDelegate
|
||||||
import ru.dbotthepony.kommons.util.ValueObserver
|
import ru.dbotthepony.kommons.util.ValueObserver
|
||||||
import ru.dbotthepony.mc.otm.network.MatteryStreamCodec
|
import ru.dbotthepony.mc.otm.network.MatteryStreamCodec
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
import java.util.concurrent.locks.ReentrantLock
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
import java.util.function.Consumer
|
import java.util.function.Consumer
|
||||||
import kotlin.concurrent.withLock
|
import kotlin.concurrent.withLock
|
||||||
@ -14,10 +15,15 @@ class SynchableObservedDelegate<V>(val delegate: Delegate<V>, val codec: Mattery
|
|||||||
private object Mark
|
private object Mark
|
||||||
|
|
||||||
private val listeners = Listenable.Impl<V>()
|
private val listeners = Listenable.Impl<V>()
|
||||||
|
private val remoteCount = AtomicInteger()
|
||||||
private var observed: Any? = Mark
|
private var observed: Any? = Mark
|
||||||
private val observeLock = ReentrantLock()
|
private val observeLock = ReentrantLock()
|
||||||
|
|
||||||
|
override var isRemote: Boolean = false
|
||||||
|
private set
|
||||||
|
|
||||||
override fun read(stream: RegistryFriendlyByteBuf) {
|
override fun read(stream: RegistryFriendlyByteBuf) {
|
||||||
|
isRemote = true
|
||||||
val read = codec.decode(stream)
|
val read = codec.decode(stream)
|
||||||
observed = codec.copy(read)
|
observed = codec.copy(read)
|
||||||
delegate.accept(read)
|
delegate.accept(read)
|
||||||
@ -75,6 +81,10 @@ class SynchableObservedDelegate<V>(val delegate: Delegate<V>, val codec: Mattery
|
|||||||
private inner class RemoteState(listener: Runnable) : IRemoteState {
|
private inner class RemoteState(listener: Runnable) : IRemoteState {
|
||||||
private val l = listeners.addListener(listener)
|
private val l = listeners.addListener(listener)
|
||||||
|
|
||||||
|
init {
|
||||||
|
remoteCount.incrementAndGet()
|
||||||
|
}
|
||||||
|
|
||||||
override fun write(stream: RegistryFriendlyByteBuf) {
|
override fun write(stream: RegistryFriendlyByteBuf) {
|
||||||
codec.encode(stream, delegate.get())
|
codec.encode(stream, delegate.get())
|
||||||
}
|
}
|
||||||
@ -85,9 +95,13 @@ class SynchableObservedDelegate<V>(val delegate: Delegate<V>, val codec: Mattery
|
|||||||
|
|
||||||
override fun close() {
|
override fun close() {
|
||||||
l.remove()
|
l.remove()
|
||||||
|
remoteCount.decrementAndGet() // unsafe because close() can be called multiple times
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val hasRemotes: Boolean
|
||||||
|
get() = remoteCount.get() > 0
|
||||||
|
|
||||||
override fun createRemoteState(listener: Runnable): IRemoteState {
|
override fun createRemoteState(listener: Runnable): IRemoteState {
|
||||||
return RemoteState(listener)
|
return RemoteState(listener)
|
||||||
}
|
}
|
||||||
|
@ -87,6 +87,12 @@ class SynchableSet<V>(val delegate: ListenableSet<V>, val codec: MatteryStreamCo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override var isRemote: Boolean = false
|
||||||
|
private set
|
||||||
|
|
||||||
|
override val hasRemotes: Boolean
|
||||||
|
get() = remoteSlots.isNotEmpty()
|
||||||
|
|
||||||
override fun createRemoteState(listener: Runnable): IRemoteState {
|
override fun createRemoteState(listener: Runnable): IRemoteState {
|
||||||
return RemoteState(listener)
|
return RemoteState(listener)
|
||||||
}
|
}
|
||||||
@ -96,6 +102,8 @@ class SynchableSet<V>(val delegate: ListenableSet<V>, val codec: MatteryStreamCo
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun read(stream: RegistryFriendlyByteBuf) {
|
override fun read(stream: RegistryFriendlyByteBuf) {
|
||||||
|
isRemote = true
|
||||||
|
|
||||||
var action = stream.readByte().toInt()
|
var action = stream.readByte().toInt()
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
@ -114,18 +114,17 @@ object MBlockEntities {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun registerClient(event: FMLClientSetupEvent) {
|
private fun registerClient(event: FMLClientSetupEvent) {
|
||||||
event.enqueueWork {
|
BlockEntityRenderers.register(BLACK_HOLE, ::BlackHoleRenderer)
|
||||||
BlockEntityRenderers.register(BLACK_HOLE, ::BlackHoleRenderer)
|
BlockEntityRenderers.register(BLACK_HOLE_GENERATOR, ::BlackHoleGeneratorRenderer)
|
||||||
BlockEntityRenderers.register(GRAVITATION_STABILIZER, ::GravitationStabilizerRenderer)
|
BlockEntityRenderers.register(GRAVITATION_STABILIZER, ::GravitationStabilizerRenderer)
|
||||||
BlockEntityRenderers.register(ENERGY_COUNTER, ::EnergyCounterRenderer)
|
BlockEntityRenderers.register(ENERGY_COUNTER, ::EnergyCounterRenderer)
|
||||||
BlockEntityRenderers.register(BATTERY_BANK, ::BatteryBankRenderer)
|
BlockEntityRenderers.register(BATTERY_BANK, ::BatteryBankRenderer)
|
||||||
BlockEntityRenderers.register(MATTER_CAPACITOR_BANK, ::MatterBatteryBankRenderer)
|
BlockEntityRenderers.register(MATTER_CAPACITOR_BANK, ::MatterBatteryBankRenderer)
|
||||||
BlockEntityRenderers.register(MATTER_RECONSTRUCTOR, ::MatterReconstructorRenderer)
|
BlockEntityRenderers.register(MATTER_RECONSTRUCTOR, ::MatterReconstructorRenderer)
|
||||||
BlockEntityRenderers.register(MATTER_REPLICATOR, ::MatterReplicatorRenderer)
|
BlockEntityRenderers.register(MATTER_REPLICATOR, ::MatterReplicatorRenderer)
|
||||||
BlockEntityRenderers.register(MATTER_SCANNER, ::MatterScannerRenderer)
|
BlockEntityRenderers.register(MATTER_SCANNER, ::MatterScannerRenderer)
|
||||||
BlockEntityRenderers.register(POWERED_SMOKER, ::PoweredSmokerRenderer)
|
BlockEntityRenderers.register(POWERED_SMOKER, ::PoweredSmokerRenderer)
|
||||||
BlockEntityRenderers.register(HOLO_SIGN, ::HoloSignRenderer)
|
BlockEntityRenderers.register(HOLO_SIGN, ::HoloSignRenderer)
|
||||||
BlockEntityRenderers.register(FLUID_TANK, ::FluidTankRenderer)
|
BlockEntityRenderers.register(FLUID_TANK, ::FluidTankRenderer)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,4 +56,7 @@ object MBlockTags {
|
|||||||
val HARDENED_GLASS_YELLOW: TagKey<Block> = BlockTags.create(ResourceLocation("c", "hardened_glass/yellow"))
|
val HARDENED_GLASS_YELLOW: TagKey<Block> = BlockTags.create(ResourceLocation("c", "hardened_glass/yellow"))
|
||||||
|
|
||||||
val MACHINES: TagKey<Block> = BlockTags.create(ResourceLocation(OverdriveThatMatters.MOD_ID, "machines"))
|
val MACHINES: TagKey<Block> = BlockTags.create(ResourceLocation(OverdriveThatMatters.MOD_ID, "machines"))
|
||||||
|
val MULTIBLOCK_STRUCTURE: TagKey<Block> = BlockTags.create(ResourceLocation(OverdriveThatMatters.MOD_ID, "multiblock_structure"))
|
||||||
|
val MULTIBLOCK_HARD_STRUCTURE: TagKey<Block> = BlockTags.create(ResourceLocation(OverdriveThatMatters.MOD_ID, "multiblock_hard_structure"))
|
||||||
|
val MULTIBLOCK_SOFT_STRUCTURE: TagKey<Block> = BlockTags.create(ResourceLocation(OverdriveThatMatters.MOD_ID, "multiblock_soft_structure"))
|
||||||
}
|
}
|
||||||
|
@ -174,7 +174,6 @@ object MBlocks {
|
|||||||
val FLUID_TANK: FluidTankBlock by registry.register(MNames.FLUID_TANK) { FluidTankBlock() }
|
val FLUID_TANK: FluidTankBlock by registry.register(MNames.FLUID_TANK) { FluidTankBlock() }
|
||||||
val DEV_CHEST: DevChestBlock by registry.register(MNames.DEV_CHEST) { DevChestBlock() }
|
val DEV_CHEST: DevChestBlock by registry.register(MNames.DEV_CHEST) { DevChestBlock() }
|
||||||
|
|
||||||
val MULTIBLOCK_STRUCTURE by registry.register(MNames.MULTIBLOCK_STRUCTURE) { MatteryBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).requiresCorrectToolForDrops().destroyTime(2.5f).explosionResistance(160.0f)) }
|
|
||||||
val BLACK_HOLE_GENERATOR by registry.register(MNames.BLACK_HOLE_GENERATOR) { BlackHoleGeneratorBlock() }
|
val BLACK_HOLE_GENERATOR by registry.register(MNames.BLACK_HOLE_GENERATOR) { BlackHoleGeneratorBlock() }
|
||||||
val MATTER_INJECTOR by registry.register(MNames.MATTER_INJECTOR) { RotatableMatteryBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).requiresCorrectToolForDrops().destroyTime(2.5f).explosionResistance(160.0f)) }
|
val MATTER_INJECTOR by registry.register(MNames.MATTER_INJECTOR) { RotatableMatteryBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).requiresCorrectToolForDrops().destroyTime(2.5f).explosionResistance(160.0f)) }
|
||||||
val ANTIMATTER_INJECTOR by registry.register(MNames.ANTIMATTER_INJECTOR) { RotatableMatteryBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).requiresCorrectToolForDrops().destroyTime(2.5f).explosionResistance(160.0f)) }
|
val ANTIMATTER_INJECTOR by registry.register(MNames.ANTIMATTER_INJECTOR) { RotatableMatteryBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).requiresCorrectToolForDrops().destroyTime(2.5f).explosionResistance(160.0f)) }
|
||||||
|
@ -136,6 +136,9 @@ object MItems {
|
|||||||
val MATTER_ENTANGLER: BlockItem by registry.register(MNames.MATTER_ENTANGLER) { BlockItem(MBlocks.MATTER_ENTANGLER, DEFAULT_PROPERTIES) }
|
val MATTER_ENTANGLER: BlockItem by registry.register(MNames.MATTER_ENTANGLER) { BlockItem(MBlocks.MATTER_ENTANGLER, DEFAULT_PROPERTIES) }
|
||||||
|
|
||||||
val BLACK_HOLE_GENERATOR by registry.register(MNames.BLACK_HOLE_GENERATOR) { BlockItem(MBlocks.BLACK_HOLE_GENERATOR, DEFAULT_PROPERTIES) }
|
val BLACK_HOLE_GENERATOR by registry.register(MNames.BLACK_HOLE_GENERATOR) { BlockItem(MBlocks.BLACK_HOLE_GENERATOR, DEFAULT_PROPERTIES) }
|
||||||
|
val MATTER_INJECTOR by registry.register(MNames.MATTER_INJECTOR) { BlockItem(MBlocks.MATTER_INJECTOR, DEFAULT_PROPERTIES) }
|
||||||
|
val ANTIMATTER_INJECTOR by registry.register(MNames.ANTIMATTER_INJECTOR) { BlockItem(MBlocks.ANTIMATTER_INJECTOR, DEFAULT_PROPERTIES) }
|
||||||
|
val HIGH_ENERGY_PARTICLE_COLLECTOR by registry.register(MNames.HIGH_ENERGY_PARTICLE_COLLECTOR) { BlockItem(MBlocks.HIGH_ENERGY_PARTICLE_COLLECTOR, DEFAULT_PROPERTIES) }
|
||||||
val ITEM_INPUT_HATCH by registry.register(MNames.ITEM_INPUT_HATCH) { BlockItem(MBlocks.ITEM_INPUT_HATCH, DEFAULT_PROPERTIES) }
|
val ITEM_INPUT_HATCH by registry.register(MNames.ITEM_INPUT_HATCH) { BlockItem(MBlocks.ITEM_INPUT_HATCH, DEFAULT_PROPERTIES) }
|
||||||
val ITEM_OUTPUT_HATCH by registry.register(MNames.ITEM_OUTPUT_HATCH) { BlockItem(MBlocks.ITEM_OUTPUT_HATCH, DEFAULT_PROPERTIES) }
|
val ITEM_OUTPUT_HATCH by registry.register(MNames.ITEM_OUTPUT_HATCH) { BlockItem(MBlocks.ITEM_OUTPUT_HATCH, DEFAULT_PROPERTIES) }
|
||||||
val ENERGY_INPUT_HATCH by registry.register(MNames.ENERGY_INPUT_HATCH) { BlockItem(MBlocks.ENERGY_INPUT_HATCH, DEFAULT_PROPERTIES) }
|
val ENERGY_INPUT_HATCH by registry.register(MNames.ENERGY_INPUT_HATCH) { BlockItem(MBlocks.ENERGY_INPUT_HATCH, DEFAULT_PROPERTIES) }
|
||||||
|
@ -31,6 +31,7 @@ import ru.dbotthepony.mc.otm.client.screen.tech.AbstractProcessingMachineScreen
|
|||||||
import ru.dbotthepony.mc.otm.client.screen.tech.AndroidChargerScreen
|
import ru.dbotthepony.mc.otm.client.screen.tech.AndroidChargerScreen
|
||||||
import ru.dbotthepony.mc.otm.client.screen.tech.AndroidStationScreen
|
import ru.dbotthepony.mc.otm.client.screen.tech.AndroidStationScreen
|
||||||
import ru.dbotthepony.mc.otm.client.screen.tech.BatteryBankScreen
|
import ru.dbotthepony.mc.otm.client.screen.tech.BatteryBankScreen
|
||||||
|
import ru.dbotthepony.mc.otm.client.screen.tech.BlackHoleGeneratorScreen
|
||||||
import ru.dbotthepony.mc.otm.client.screen.tech.ChemicalGeneratorScreen
|
import ru.dbotthepony.mc.otm.client.screen.tech.ChemicalGeneratorScreen
|
||||||
import ru.dbotthepony.mc.otm.client.screen.tech.CobblerScreen
|
import ru.dbotthepony.mc.otm.client.screen.tech.CobblerScreen
|
||||||
import ru.dbotthepony.mc.otm.client.screen.tech.EnergyCounterScreen
|
import ru.dbotthepony.mc.otm.client.screen.tech.EnergyCounterScreen
|
||||||
@ -63,6 +64,7 @@ import ru.dbotthepony.mc.otm.menu.storage.StoragePowerSupplierMenu
|
|||||||
import ru.dbotthepony.mc.otm.menu.tech.AndroidChargerMenu
|
import ru.dbotthepony.mc.otm.menu.tech.AndroidChargerMenu
|
||||||
import ru.dbotthepony.mc.otm.menu.tech.AndroidStationMenu
|
import ru.dbotthepony.mc.otm.menu.tech.AndroidStationMenu
|
||||||
import ru.dbotthepony.mc.otm.menu.tech.BatteryBankMenu
|
import ru.dbotthepony.mc.otm.menu.tech.BatteryBankMenu
|
||||||
|
import ru.dbotthepony.mc.otm.menu.tech.BlackHoleGeneratorMenu
|
||||||
import ru.dbotthepony.mc.otm.menu.tech.ChemicalGeneratorMenu
|
import ru.dbotthepony.mc.otm.menu.tech.ChemicalGeneratorMenu
|
||||||
import ru.dbotthepony.mc.otm.menu.tech.CobblerMenu
|
import ru.dbotthepony.mc.otm.menu.tech.CobblerMenu
|
||||||
import ru.dbotthepony.mc.otm.menu.tech.EnergyCounterMenu
|
import ru.dbotthepony.mc.otm.menu.tech.EnergyCounterMenu
|
||||||
@ -114,6 +116,7 @@ object MMenus {
|
|||||||
val MATTER_OUTPUT_HATCH by registry.register(MNames.MATTER_OUTPUT_HATCH) { MenuType(MatterHatchMenu::output, FeatureFlags.VANILLA_SET) }
|
val MATTER_OUTPUT_HATCH by registry.register(MNames.MATTER_OUTPUT_HATCH) { MenuType(MatterHatchMenu::output, FeatureFlags.VANILLA_SET) }
|
||||||
val ENERGY_INPUT_HATCH by registry.register(MNames.ENERGY_INPUT_HATCH) { MenuType(EnergyHatchMenu::input, FeatureFlags.VANILLA_SET) }
|
val ENERGY_INPUT_HATCH by registry.register(MNames.ENERGY_INPUT_HATCH) { MenuType(EnergyHatchMenu::input, FeatureFlags.VANILLA_SET) }
|
||||||
val ENERGY_OUTPUT_HATCH by registry.register(MNames.ENERGY_OUTPUT_HATCH) { MenuType(EnergyHatchMenu::output, FeatureFlags.VANILLA_SET) }
|
val ENERGY_OUTPUT_HATCH by registry.register(MNames.ENERGY_OUTPUT_HATCH) { MenuType(EnergyHatchMenu::output, FeatureFlags.VANILLA_SET) }
|
||||||
|
val BLACK_HOLE_GENERATOR by registry.register(MNames.BLACK_HOLE_GENERATOR) { MenuType(::BlackHoleGeneratorMenu, FeatureFlags.VANILLA_SET) }
|
||||||
|
|
||||||
val STORAGE_BUS by registry.register(MNames.STORAGE_BUS) { MenuType(::StorageBusMenu, FeatureFlags.VANILLA_SET) }
|
val STORAGE_BUS by registry.register(MNames.STORAGE_BUS) { MenuType(::StorageBusMenu, FeatureFlags.VANILLA_SET) }
|
||||||
val STORAGE_IMPORTER_EXPORTER by registry.register(MNames.STORAGE_IMPORTER) { MenuType(::StorageImporterExporterMenu, FeatureFlags.VANILLA_SET) }
|
val STORAGE_IMPORTER_EXPORTER by registry.register(MNames.STORAGE_IMPORTER) { MenuType(::StorageImporterExporterMenu, FeatureFlags.VANILLA_SET) }
|
||||||
@ -165,5 +168,6 @@ object MMenus {
|
|||||||
event.register(MATTER_OUTPUT_HATCH, ::MatterHatchScreen)
|
event.register(MATTER_OUTPUT_HATCH, ::MatterHatchScreen)
|
||||||
event.register(ENERGY_INPUT_HATCH, ::EnergyHatchScreen)
|
event.register(ENERGY_INPUT_HATCH, ::EnergyHatchScreen)
|
||||||
event.register(ENERGY_OUTPUT_HATCH, ::EnergyHatchScreen)
|
event.register(ENERGY_OUTPUT_HATCH, ::EnergyHatchScreen)
|
||||||
|
event.register(BLACK_HOLE_GENERATOR, ::BlackHoleGeneratorScreen)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,6 @@ object MNames {
|
|||||||
const val ANDROID_CHARGER = "android_charger"
|
const val ANDROID_CHARGER = "android_charger"
|
||||||
const val INFINITE_WATER_SOURCE = "infinite_water_source"
|
const val INFINITE_WATER_SOURCE = "infinite_water_source"
|
||||||
const val DEV_CHEST = "dev_chest"
|
const val DEV_CHEST = "dev_chest"
|
||||||
const val MULTIBLOCK_STRUCTURE = "multiblock_structure"
|
|
||||||
const val BLACK_HOLE_GENERATOR = "black_hole_generator"
|
const val BLACK_HOLE_GENERATOR = "black_hole_generator"
|
||||||
const val MATTER_INJECTOR = "matter_injector"
|
const val MATTER_INJECTOR = "matter_injector"
|
||||||
const val ANTIMATTER_INJECTOR = "antimatter_injector"
|
const val ANTIMATTER_INJECTOR = "antimatter_injector"
|
||||||
|
Loading…
Reference in New Issue
Block a user