Multiblock test
This commit is contained in:
parent
94afd4f19a
commit
f97ad565d4
@ -0,0 +1,23 @@
|
||||
package ru.dbotthepony.mc.otm.block
|
||||
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.EntityBlock
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.entity.BlockEntityTicker
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import ru.dbotthepony.mc.otm.block.entity.MultiblockTestBlockEntity
|
||||
|
||||
class MultiblockTestBlock : MatteryBlock(), EntityBlock {
|
||||
override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity {
|
||||
return MultiblockTestBlockEntity(blockPos, blockState)
|
||||
}
|
||||
|
||||
override fun <T : BlockEntity?> getTicker(level: Level, blockState: BlockState, blockEntityType: BlockEntityType<T>): BlockEntityTicker<T>? {
|
||||
if (level.isClientSide)
|
||||
return null
|
||||
|
||||
return BlockEntityTicker { _, _, _, tile -> if (tile is MultiblockTestBlockEntity) tile.tick() }
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
package ru.dbotthepony.mc.otm.block.entity
|
||||
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.tags.BlockTags
|
||||
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.HopperBlockEntity
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import ru.dbotthepony.mc.otm.core.multiblockConfiguration
|
||||
import ru.dbotthepony.mc.otm.core.multiblockEntity
|
||||
import ru.dbotthepony.mc.otm.registry.MBlockEntities
|
||||
import ru.dbotthepony.mc.otm.registry.MBlocks
|
||||
|
||||
class MultiblockTestBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryBlockEntity(MBlockEntities.MULTIBLOCK_TEST, blockPos, blockState) {
|
||||
val config = CONFIG.create(blockPos)
|
||||
|
||||
override fun tick() {
|
||||
super.tick()
|
||||
|
||||
if (config.update(level!!)) {
|
||||
println("It matches!")
|
||||
println("hopper block entities: ${config.blockEntities(HOPPERS)}")
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val HOPPERS = multiblockEntity<HopperBlockEntity>()
|
||||
private val FURNACES = multiblockEntity<FurnaceBlockEntity>()
|
||||
|
||||
val CONFIG = multiblockConfiguration {
|
||||
block(MBlocks.MULTIBLOCK_TEST)
|
||||
|
||||
and {
|
||||
top {
|
||||
or {
|
||||
block(Blocks.HOPPER)
|
||||
block(Blocks.CHEST)
|
||||
block(BlockTags.PLANKS)
|
||||
}
|
||||
|
||||
tag(HOPPERS)
|
||||
}
|
||||
|
||||
left(Blocks.FURNACE) {
|
||||
tag(FURNACES)
|
||||
|
||||
top {
|
||||
or {
|
||||
block(Blocks.HOPPER)
|
||||
block(Blocks.CHEST)
|
||||
block(BlockTags.PLANKS)
|
||||
}
|
||||
|
||||
tag(HOPPERS)
|
||||
}
|
||||
}
|
||||
|
||||
right(Blocks.FURNACE) {
|
||||
tag(FURNACES)
|
||||
|
||||
top {
|
||||
or {
|
||||
block(Blocks.HOPPER)
|
||||
block(Blocks.CHEST)
|
||||
block(BlockTags.PLANKS)
|
||||
}
|
||||
|
||||
tag(HOPPERS)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
619
src/main/kotlin/ru/dbotthepony/mc/otm/core/Multiblock.kt
Normal file
619
src/main/kotlin/ru/dbotthepony/mc/otm/core/Multiblock.kt
Normal file
@ -0,0 +1,619 @@
|
||||
package ru.dbotthepony.mc.otm.core
|
||||
|
||||
import com.google.common.collect.ImmutableList
|
||||
import com.google.common.collect.ImmutableSet
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
||||
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.core.Vec3i
|
||||
import net.minecraft.tags.TagKey
|
||||
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 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.Collections
|
||||
import java.util.function.Predicate
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
fun interface BlockPredicate {
|
||||
fun test(pos: BlockPos, access: LevelAccessor): Boolean
|
||||
|
||||
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 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.isEmptyBlock(pos)
|
||||
}
|
||||
}
|
||||
|
||||
object NotAir : BlockPredicate {
|
||||
override fun test(pos: BlockPos, access: LevelAccessor): Boolean {
|
||||
return !access.isEmptyBlock(pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
class MultiblockBuilder {
|
||||
private val nodes = Object2ObjectOpenHashMap<BlockPos, Node>()
|
||||
|
||||
// not data classes to allow having multiple tags with same target
|
||||
class EntityTag<T : BlockEntity>(val clazz: KClass<T>) : Predicate<BlockEntity> {
|
||||
override fun test(t: BlockEntity): Boolean {
|
||||
return clazz.isInstance(t)
|
||||
}
|
||||
}
|
||||
|
||||
enum class Strategy {
|
||||
OR, AND
|
||||
}
|
||||
|
||||
@Suppress("unchecked_cast")
|
||||
abstract class BlockPredicates<T : BlockPredicates<T>> {
|
||||
val predicates = ObjectArraySet<BlockPredicate>()
|
||||
val children = ObjectArraySet<BlockPredicates<*>>()
|
||||
|
||||
val blockStateTags = ObjectArraySet<Any>()
|
||||
val blockTags = ObjectArraySet<Any>()
|
||||
val blockEntityTags = ObjectArraySet<EntityTag<*>>()
|
||||
|
||||
fun tagBlockState(value: Any): T {
|
||||
blockStateTags.add(value)
|
||||
return this as T
|
||||
}
|
||||
|
||||
fun tagBlock(value: Any): T {
|
||||
blockTags.add(value)
|
||||
return this as T
|
||||
}
|
||||
|
||||
fun tag(value: EntityTag<*>): T {
|
||||
blockEntityTags.add(value)
|
||||
return this as T
|
||||
}
|
||||
|
||||
fun clear(): T {
|
||||
predicates.clear()
|
||||
return this as T
|
||||
}
|
||||
|
||||
fun predicate(predicate: BlockPredicate): T {
|
||||
predicates.add(predicate)
|
||||
return this as T
|
||||
}
|
||||
|
||||
fun block(block: Block): T {
|
||||
predicates.add { pos, access -> access.getBlockState(pos).`is`(block) }
|
||||
return this as T
|
||||
}
|
||||
|
||||
fun block(block: TagKey<Block>): T {
|
||||
predicates.add { pos, access -> access.getBlockState(pos).`is`(block) }
|
||||
return this as T
|
||||
}
|
||||
|
||||
fun air(): T {
|
||||
predicate(BlockPredicate.Air)
|
||||
return this as T
|
||||
}
|
||||
|
||||
fun block(state: BlockState): T {
|
||||
predicates.add { pos, access -> access.getBlockState(pos) == state }
|
||||
return this as T
|
||||
}
|
||||
|
||||
fun and(): And<T> {
|
||||
return And(this as T)
|
||||
}
|
||||
|
||||
fun or(): Or<T> {
|
||||
return Or(this as T)
|
||||
}
|
||||
|
||||
fun and(configurator: And<T>.() -> Unit): And<T> {
|
||||
return And(this as T).also(configurator)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
class And<P : BlockPredicates<P>>(val parent: P) : BlockPredicates<And<P>>() {
|
||||
init {
|
||||
parent.children.add(this)
|
||||
}
|
||||
|
||||
fun end() = parent
|
||||
|
||||
override val strategy: Strategy
|
||||
get() = Strategy.AND
|
||||
}
|
||||
|
||||
class Or<P : BlockPredicates<P>>(val parent: P) : BlockPredicates<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) : BlockPredicates<Node>() {
|
||||
init {
|
||||
check(nodes.put(pos, this) == null) { "Trying to create new node at $pos while already having one" }
|
||||
}
|
||||
|
||||
fun relative(diff: Vec3i): Node {
|
||||
return node(pos + diff)
|
||||
}
|
||||
|
||||
fun relative(dir: Direction): Node {
|
||||
return relative(dir.normal)
|
||||
}
|
||||
|
||||
fun relative(dir: RelativeSide): Node {
|
||||
return relative(dir.default)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
fun node(at: BlockPos): Node {
|
||||
return nodes[at] ?: Node(at)
|
||||
}
|
||||
|
||||
fun root() = node(BlockPos.ZERO)
|
||||
fun root(configurator: Node.() -> Unit) = node(BlockPos.ZERO).also(configurator)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
fun build(): MultiblockFactory {
|
||||
return MultiblockFactory(nodes.values.iterator().map { it.build() }.collect(ImmutableSet.toImmutableSet()))
|
||||
}
|
||||
}
|
||||
|
||||
class MultiblockFactory(val north: ImmutableSet<Part>) {
|
||||
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<*>>,
|
||||
)
|
||||
|
||||
fun create(pos: BlockPos): Multiblock {
|
||||
return Multiblock(
|
||||
pos,
|
||||
north,
|
||||
south,
|
||||
west,
|
||||
east,
|
||||
)
|
||||
}
|
||||
|
||||
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,
|
||||
north: Collection<MultiblockFactory.Part>,
|
||||
south: Collection<MultiblockFactory.Part>,
|
||||
west: Collection<MultiblockFactory.Part>,
|
||||
east: Collection<MultiblockFactory.Part>,
|
||||
) {
|
||||
var isValid = false
|
||||
private set
|
||||
|
||||
private val configurations = ArrayList<Config>()
|
||||
|
||||
private class BEList<T : BlockEntity>(val tag: MultiblockBuilder.EntityTag<T>) {
|
||||
val list = ArrayList<T>()
|
||||
val set = ObjectArraySet<T>()
|
||||
val setView: Set<T> = Collections.unmodifiableSet(set)
|
||||
|
||||
fun add(blockEntity: BlockEntity) {
|
||||
if (tag.test(blockEntity)) {
|
||||
set.add(blockEntity as T)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
list.clear()
|
||||
set.clear()
|
||||
}
|
||||
}
|
||||
|
||||
private inner class Config(val pos: BlockPos, parts: Collection<MultiblockFactory.Part>) {
|
||||
private inner class Part(val pos: BlockPos, val prototype: MultiblockFactory.Part) {
|
||||
private var blockEntity: BlockEntity? = null
|
||||
private var blockState: BlockState? = null
|
||||
private val assignedBlockEntityLists = ArrayList<BEList<*>>(prototype.blockEntityTags.size)
|
||||
private val assignedBlockStateLists = ArrayList<Reference2IntOpenHashMap<BlockState>>()
|
||||
private val assignedBlockLists = ArrayList<Object2IntOpenHashMap<Block>>()
|
||||
private val children: ImmutableList<Part> = prototype.children.stream().map { Part(pos, it) }.collect(ImmutableList.toImmutableList())
|
||||
|
||||
init {
|
||||
prototype.blockEntityTags.forEach {
|
||||
assignedBlockEntityLists.add(tag2BlockEntity.computeIfAbsent(it, Object2ObjectFunction { _ -> BEList(it as MultiblockBuilder.EntityTag<BlockEntity>).also { blockEntityLists.add(it) } }))
|
||||
}
|
||||
|
||||
prototype.blockStateTags.forEach {
|
||||
assignedBlockStateLists.add(tag2BlockState.computeIfAbsent(it, Object2ObjectFunction { Reference2IntOpenHashMap<BlockState>().also { blockStateLists.add(it) } }))
|
||||
}
|
||||
|
||||
prototype.blockTags.forEach {
|
||||
assignedBlockLists.add(tag2Block.computeIfAbsent(it, Object2ObjectFunction { Object2IntOpenHashMap<Block>().also { blockLists.add(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) { "Counter for block state $old turned negative" }
|
||||
}
|
||||
|
||||
assignedBlockLists.forEach {
|
||||
it[old.block] = it.getInt(old.block) - 1
|
||||
check(it.getInt(old.block) >= 0) { "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) { "Counter for block state $blockState turned negative" }
|
||||
}
|
||||
|
||||
assignedBlockLists.forEach {
|
||||
it[blockState.block] = it.getInt(blockState.block) - 1
|
||||
check(it.getInt(blockState.block) >= 0) { "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 val tag2BlockEntity = Object2ObjectOpenHashMap<MultiblockBuilder.EntityTag<*>, BEList<*>>()
|
||||
private val tag2BlockState = Object2ObjectOpenHashMap<Any, Reference2IntOpenHashMap<BlockState>>()
|
||||
private val tag2Block = Object2ObjectOpenHashMap<Any, Object2IntOpenHashMap<Block>>()
|
||||
|
||||
private val blockEntityLists = ArrayList<BEList<*>>()
|
||||
private val blockStateLists = ArrayList<Reference2IntOpenHashMap<BlockState>>()
|
||||
private val blockLists = ArrayList<Object2IntOpenHashMap<Block>>()
|
||||
|
||||
private val parts: ImmutableSet<Part> = parts.stream().map { Part(it.pos + pos, it) }.collect(ImmutableSet.toImmutableSet())
|
||||
|
||||
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>
|
||||
}
|
||||
}
|
||||
|
||||
private val north = Config(pos, north).also { configurations.add(it) }
|
||||
private val south = Config(pos, south).also { configurations.add(it) }
|
||||
private val west = Config(pos, west).also { configurations.add(it) }
|
||||
private val east = Config(pos, east).also { configurations.add(it) }
|
||||
|
||||
private var activeConfig: Config? = null
|
||||
|
||||
fun <T : BlockEntity> blockEntities(tag: MultiblockBuilder.EntityTag<T>): Set<T> {
|
||||
return activeConfig?.blockEntities(tag) ?: setOf()
|
||||
}
|
||||
|
||||
fun update(levelAccessor: LevelAccessor): Boolean {
|
||||
val activeConfig = activeConfig
|
||||
|
||||
if (activeConfig != null && activeConfig.update(levelAccessor)) {
|
||||
return true
|
||||
} else if (activeConfig != null) {
|
||||
for (config in configurations) {
|
||||
if (config !== activeConfig && config.update(levelAccessor)) {
|
||||
this.activeConfig = config
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
this.activeConfig = null
|
||||
this.isValid = false
|
||||
return false
|
||||
} else {
|
||||
for (config in configurations) {
|
||||
if (config.update(levelAccessor)) {
|
||||
this.activeConfig = config
|
||||
this.isValid = true
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
package ru.dbotthepony.mc.otm.core.math
|
||||
|
||||
import com.google.common.collect.ImmutableMap
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.core.Vec3i
|
||||
@ -27,8 +26,8 @@ operator fun BlockPos.plus(other: BlockRotation): BlockPos {
|
||||
return this + other.normal
|
||||
}
|
||||
|
||||
enum class RelativeSide {
|
||||
FRONT, BACK, LEFT, RIGHT, TOP, BOTTOM
|
||||
enum class RelativeSide(val default: Direction) {
|
||||
FRONT(Direction.NORTH), BACK(Direction.SOUTH), LEFT(Direction.WEST), RIGHT(Direction.EAST), TOP(Direction.UP), BOTTOM(Direction.DOWN)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -44,6 +44,10 @@ object MBlockEntities {
|
||||
return registry.register(name) { BlockEntityType.Builder.of(factory, *blocks.map { it.get() }.toTypedArray()).build(null) }
|
||||
}
|
||||
|
||||
private fun <T : BlockEntity> register(name: String, factory: BlockEntityType.BlockEntitySupplier<T>, blocks: Supplier<Block>): MDeferredRegister<*>.Entry<BlockEntityType<T>> {
|
||||
return registry.register(name) { BlockEntityType.Builder.of(factory, blocks.get()).build(null) }
|
||||
}
|
||||
|
||||
private fun <T : BlockEntity> register(name: String, factory: BlockEntityType.BlockEntitySupplier<T>, blocks: Map<*, Block>): MDeferredRegister<*>.Entry<BlockEntityType<T>> {
|
||||
return registry.register(name) { BlockEntityType.Builder.of(factory, *blocks.values.toTypedArray()).build(null) }
|
||||
}
|
||||
@ -87,6 +91,8 @@ object MBlockEntities {
|
||||
val POWERED_BLAST_FURNACE by register(MNames.POWERED_BLAST_FURNACE, ::PoweredBlastFurnaceBlockEntity, MBlocks.POWERED_BLAST_FURNACE)
|
||||
val POWERED_SMOKER by register(MNames.POWERED_SMOKER, ::PoweredSmokerBlockEntity, MBlocks.POWERED_SMOKER)
|
||||
|
||||
val MULTIBLOCK_TEST by register("multiblock_test", ::MultiblockTestBlockEntity, MBlocks::MULTIBLOCK_TEST)
|
||||
|
||||
val ENERGY_CABLES: Map<CablesConfig.E, BlockEntityType<*>> = SupplierMap(CablesConfig.E.entries.map { conf ->
|
||||
var selfFeed: Supplier<BlockEntityType<*>> = Supplier { TODO() }
|
||||
selfFeed = register("${conf.name.lowercase()}_energy_cable", { a, b -> SimpleEnergyCableBlockEntity(selfFeed.get(), a, b, conf) }) as Supplier<BlockEntityType<*>>
|
||||
|
@ -27,6 +27,7 @@ import ru.dbotthepony.mc.otm.block.BlockSphereDebugger
|
||||
import ru.dbotthepony.mc.otm.block.EnergyCableBlock
|
||||
import ru.dbotthepony.mc.otm.block.MatterCableBlock
|
||||
import ru.dbotthepony.mc.otm.block.MatteryBlock
|
||||
import ru.dbotthepony.mc.otm.block.MultiblockTestBlock
|
||||
import ru.dbotthepony.mc.otm.block.StorageCableBlock
|
||||
import ru.dbotthepony.mc.otm.block.addSimpleDescription
|
||||
import ru.dbotthepony.mc.otm.block.decorative.DevChestBlock
|
||||
@ -253,6 +254,8 @@ object MBlocks {
|
||||
.strength(3f)
|
||||
) }
|
||||
|
||||
val MULTIBLOCK_TEST by registry.register("multiblock_test") { MultiblockTestBlock() }
|
||||
|
||||
init {
|
||||
MRegistry.registerBlocks(registry)
|
||||
}
|
||||
|
@ -160,6 +160,7 @@ object MItems {
|
||||
|
||||
val DEBUG_EXPLOSION_SMALL: Item by registry.register(MNames.DEBUG_EXPLOSION_SMALL) { BlockItem(MBlocks.DEBUG_EXPLOSION_SMALL, Item.Properties().stacksTo(64)) }
|
||||
val DEBUG_SPHERE_POINTS: Item by registry.register(MNames.DEBUG_SPHERE_POINTS) { BlockItem(MBlocks.DEBUG_SPHERE_POINTS, Item.Properties().stacksTo(64)) }
|
||||
val MULTIBLOCK_TEST by registry.register("multiblock_test") { BlockItem(MBlocks.MULTIBLOCK_TEST, Properties().stacksTo(64)) }
|
||||
|
||||
val TRITANIUM_ANVIL: List<BlockItem>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user