From 0ae1cd21fef85688bcd4cbf1d05eba49facb5893 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Sun, 14 Jan 2024 00:52:38 +0700 Subject: [PATCH 01/11] Fix secondary furnace recipes not using proper config values --- .../block/entity/tech/AbstractPoweredFurnaceBlockEntity.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/AbstractPoweredFurnaceBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/AbstractPoweredFurnaceBlockEntity.kt index ed41c96f8..404a0fa31 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/AbstractPoweredFurnaceBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/AbstractPoweredFurnaceBlockEntity.kt @@ -142,8 +142,8 @@ sealed class AbstractPoweredFurnaceBlockEntity

Date: Sun, 14 Jan 2024 10:51:15 +0700 Subject: [PATCH 02/11] How did i mess up left/right sprites of machine side controls again --- .../kotlin/ru/dbotthepony/mc/otm/client/render/Widgets18.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/Widgets18.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/Widgets18.kt index 8999e8f91..1835e22a0 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/Widgets18.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/Widgets18.kt @@ -157,8 +157,8 @@ object Widgets18 { } } - val LEFT_CONTROLS = SideControls() val RIGHT_CONTROLS = SideControls() + val LEFT_CONTROLS = SideControls() val TOP_CONTROLS = SideControls() val BOTTOM_CONTROLS = SideControls() val FRONT_CONTROLS = SideControls() From f97ad565d48144629380396853a1e22aaf55d6a4 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Wed, 17 Jan 2024 13:15:01 +0700 Subject: [PATCH 03/11] Multiblock test --- .../mc/otm/block/MultiblockTestBlock.kt | 23 + .../block/entity/MultiblockTestBlockEntity.kt | 75 +++ .../ru/dbotthepony/mc/otm/core/Multiblock.kt | 619 ++++++++++++++++++ .../mc/otm/core/math/BlockRotation.kt | 5 +- .../mc/otm/registry/MBlockEntities.kt | 6 + .../ru/dbotthepony/mc/otm/registry/MBlocks.kt | 3 + .../ru/dbotthepony/mc/otm/registry/MItems.kt | 1 + 7 files changed, 729 insertions(+), 3 deletions(-) create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/block/MultiblockTestBlock.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MultiblockTestBlockEntity.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/core/Multiblock.kt diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/MultiblockTestBlock.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/MultiblockTestBlock.kt new file mode 100644 index 000000000..20997844e --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/MultiblockTestBlock.kt @@ -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 getTicker(level: Level, blockState: BlockState, blockEntityType: BlockEntityType): BlockEntityTicker? { + if (level.isClientSide) + return null + + return BlockEntityTicker { _, _, _, tile -> if (tile is MultiblockTestBlockEntity) tile.tick() } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MultiblockTestBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MultiblockTestBlockEntity.kt new file mode 100644 index 000000000..a5bdd18f7 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MultiblockTestBlockEntity.kt @@ -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() + private val FURNACES = multiblockEntity() + + 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) + } + } + } + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Multiblock.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Multiblock.kt new file mode 100644 index 000000000..b2b571c68 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Multiblock.kt @@ -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 { + constructor(vararg nodes: BlockPredicate) : this(ImmutableSet.copyOf(nodes)) + constructor(nodes: Set) : 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 { + constructor(vararg nodes: BlockPredicate) : this(ImmutableSet.copyOf(nodes)) + constructor(nodes: Set) : 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 multiblockEntity(): MultiblockBuilder.EntityTag { + return MultiblockBuilder.EntityTag(T::class) +} + +class MultiblockBuilder { + private val nodes = Object2ObjectOpenHashMap() + + // not data classes to allow having multiple tags with same target + class EntityTag(val clazz: KClass) : Predicate { + override fun test(t: BlockEntity): Boolean { + return clazz.isInstance(t) + } + } + + enum class Strategy { + OR, AND + } + + @Suppress("unchecked_cast") + abstract class BlockPredicates> { + val predicates = ObjectArraySet() + val children = ObjectArraySet>() + + val blockStateTags = ObjectArraySet() + val blockTags = ObjectArraySet() + val blockEntityTags = ObjectArraySet>() + + 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): 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 { + return And(this as T) + } + + fun or(): Or { + return Or(this as T) + } + + fun and(configurator: And.() -> Unit): And { + return And(this as T).also(configurator) + } + + fun or(configurator: Or.() -> Unit): Or { + 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

>(val parent: P) : BlockPredicates>() { + init { + parent.children.add(this) + } + + fun end() = parent + + override val strategy: Strategy + get() = Strategy.AND + } + + class Or

>(val parent: P) : BlockPredicates>() { + 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() { + 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) { + data class Part( + val pos: BlockPos, + val strategy: MultiblockBuilder.Strategy, + val predicate: ImmutableList, + val children: ImmutableList, + val blockStateTags: ImmutableSet, + val blockTags: ImmutableSet, + val blockEntityTags: ImmutableSet>, + ) + + fun create(pos: BlockPos): Multiblock { + return Multiblock( + pos, + north, + south, + west, + east, + ) + } + + val south: ImmutableSet = north.iterator().map { it.copy(pos = it.pos.rotate(Rotation.CLOCKWISE_180)) }.collect(ImmutableSet.toImmutableSet()) + val west: ImmutableSet = north.iterator().map { it.copy(pos = it.pos.rotate(Rotation.COUNTERCLOCKWISE_90)) }.collect(ImmutableSet.toImmutableSet()) + val east: ImmutableSet = north.iterator().map { it.copy(pos = it.pos.rotate(Rotation.CLOCKWISE_90)) }.collect(ImmutableSet.toImmutableSet()) +} + +class Multiblock( + pos: BlockPos, + north: Collection, + south: Collection, + west: Collection, + east: Collection, +) { + var isValid = false + private set + + private val configurations = ArrayList() + + private class BEList(val tag: MultiblockBuilder.EntityTag) { + val list = ArrayList() + val set = ObjectArraySet() + val setView: Set = 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) { + 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>(prototype.blockEntityTags.size) + private val assignedBlockStateLists = ArrayList>() + private val assignedBlockLists = ArrayList>() + private val children: ImmutableList = 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).also { blockEntityLists.add(it) } })) + } + + prototype.blockStateTags.forEach { + assignedBlockStateLists.add(tag2BlockState.computeIfAbsent(it, Object2ObjectFunction { Reference2IntOpenHashMap().also { blockStateLists.add(it) } })) + } + + prototype.blockTags.forEach { + assignedBlockLists.add(tag2Block.computeIfAbsent(it, Object2ObjectFunction { Object2IntOpenHashMap().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, BEList<*>>() + private val tag2BlockState = Object2ObjectOpenHashMap>() + private val tag2Block = Object2ObjectOpenHashMap>() + + private val blockEntityLists = ArrayList>() + private val blockStateLists = ArrayList>() + private val blockLists = ArrayList>() + + private val parts: ImmutableSet = 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 blockEntities(tag: MultiblockBuilder.EntityTag): Set { + return (tag2BlockEntity[tag]?.setView ?: setOf()) as Set + } + } + + 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 blockEntities(tag: MultiblockBuilder.EntityTag): Set { + 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 + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/math/BlockRotation.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/math/BlockRotation.kt index 1985f0925..fee43c2be 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/math/BlockRotation.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/math/BlockRotation.kt @@ -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) } /** diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlockEntities.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlockEntities.kt index fea6ebb36..4db354925 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlockEntities.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlockEntities.kt @@ -44,6 +44,10 @@ object MBlockEntities { return registry.register(name) { BlockEntityType.Builder.of(factory, *blocks.map { it.get() }.toTypedArray()).build(null) } } + private fun register(name: String, factory: BlockEntityType.BlockEntitySupplier, blocks: Supplier): MDeferredRegister<*>.Entry> { + return registry.register(name) { BlockEntityType.Builder.of(factory, blocks.get()).build(null) } + } + private fun register(name: String, factory: BlockEntityType.BlockEntitySupplier, blocks: Map<*, Block>): MDeferredRegister<*>.Entry> { 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> = SupplierMap(CablesConfig.E.entries.map { conf -> var selfFeed: Supplier> = Supplier { TODO() } selfFeed = register("${conf.name.lowercase()}_energy_cable", { a, b -> SimpleEnergyCableBlockEntity(selfFeed.get(), a, b, conf) }) as Supplier> diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlocks.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlocks.kt index f384631cf..b2539b7b2 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlocks.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlocks.kt @@ -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) } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MItems.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MItems.kt index 04411e1ea..96dc11e0a 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MItems.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MItems.kt @@ -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 From c356cd703e3e1afafbe67d59aeaf51287856a652 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Wed, 17 Jan 2024 17:30:50 +0700 Subject: [PATCH 04/11] :mind_blown: More multiblock code --- .../mc/otm/mixin/BlockEntityMixin.java | 23 ++++++ .../ru/dbotthepony/mc/otm/core/Multiblock.kt | 74 +++++++++++++++++-- .../overdrive_that_matters.mixins.json | 1 + 3 files changed, 92 insertions(+), 6 deletions(-) create mode 100644 src/main/java/ru/dbotthepony/mc/otm/mixin/BlockEntityMixin.java diff --git a/src/main/java/ru/dbotthepony/mc/otm/mixin/BlockEntityMixin.java b/src/main/java/ru/dbotthepony/mc/otm/mixin/BlockEntityMixin.java new file mode 100644 index 000000000..f537e2951 --- /dev/null +++ b/src/main/java/ru/dbotthepony/mc/otm/mixin/BlockEntityMixin.java @@ -0,0 +1,23 @@ +package ru.dbotthepony.mc.otm.mixin; + +import net.minecraft.world.level.block.entity.BlockEntity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import ru.dbotthepony.mc.otm.core.MultiblockKt; + +// because i know +// someone +// somewhere +// will try to break the system +// either on purpose or accidentally because of other mod +@Mixin(BlockEntity.class) +public abstract class BlockEntityMixin { + @Inject( + at = @At("TAIL"), + method = "setRemoved()V" + ) + public void setRemovedListener() { + MultiblockKt.onBlockEntityInvalidated((BlockEntity) (Object) this); + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Multiblock.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Multiblock.kt index b2b571c68..6637b2fdf 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Multiblock.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Multiblock.kt @@ -11,19 +11,29 @@ 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.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 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.Collections +import java.util.WeakHashMap import java.util.function.Predicate import kotlin.reflect.KClass +private val blockEntityListeners = WeakHashMap>>() + +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 @@ -358,14 +368,20 @@ class Multiblock( private val configurations = ArrayList() - private class BEList(val tag: MultiblockBuilder.EntityTag) { + private inner class BEList(val tag: MultiblockBuilder.EntityTag) { val list = ArrayList() val set = ObjectArraySet() val setView: Set = Collections.unmodifiableSet(set) fun add(blockEntity: BlockEntity) { if (tag.test(blockEntity)) { - set.add(blockEntity as T) + if (set.add(blockEntity as T)) { + blockEntityListeners + .computeIfAbsent(blockEntity.level) { WeakHashMap() } + .computeIfAbsent(blockEntity) { WeakHashSet() } + .add(this@Multiblock) + } + list.add(blockEntity) } } @@ -376,11 +392,42 @@ class Multiblock( 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() } @@ -471,12 +518,12 @@ class Multiblock( if (old != null) { assignedBlockStateLists.forEach { it[old] = it.getInt(old) - 1 - check(it.getInt(old) >= 0) { "Counter for block state $old turned negative" } + 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) { "Counter for block ${old.block.registryName} turned negative" } + check(it.getInt(old.block) >= 0) { "Consistency check failed: Counter for block ${old.block.registryName} turned negative" } } } @@ -510,12 +557,12 @@ class Multiblock( if (blockState != null) { assignedBlockStateLists.forEach { it[blockState] = it.getInt(blockState) - 1 - check(it.getInt(blockState) >= 0) { "Counter for block state $blockState turned negative" } + 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) { "Counter for block ${blockState.block.registryName} turned negative" } + check(it.getInt(blockState.block) >= 0) { "Consistency check failed: Counter for block ${blockState.block.registryName} turned negative" } } } @@ -575,6 +622,16 @@ class Multiblock( fun blockEntities(tag: MultiblockBuilder.EntityTag): Set { return (tag2BlockEntity[tag]?.setView ?: setOf()) as Set } + + fun blockEntityRemoved(blockEntity: BlockEntity): Boolean { + var any = false + + blockEntityLists.forEach { + any = it.blockEntityRemoved(blockEntity) || any + } + + return any + } } private val north = Config(pos, north).also { configurations.add(it) } @@ -585,9 +642,14 @@ class Multiblock( private var activeConfig: Config? = null fun blockEntities(tag: MultiblockBuilder.EntityTag): Set { + if (!isValid) return emptySet() return activeConfig?.blockEntities(tag) ?: setOf() } + fun blockEntityRemoved(blockEntity: BlockEntity) { + activeConfig?.blockEntityRemoved(blockEntity) + } + fun update(levelAccessor: LevelAccessor): Boolean { val activeConfig = activeConfig diff --git a/src/main/resources/overdrive_that_matters.mixins.json b/src/main/resources/overdrive_that_matters.mixins.json index aa7271058..37f1c45b8 100644 --- a/src/main/resources/overdrive_that_matters.mixins.json +++ b/src/main/resources/overdrive_that_matters.mixins.json @@ -6,6 +6,7 @@ "minVersion": "0.8", "refmap": "overdrive_that_matters.refmap.json", "mixins": [ + "BlockEntityMixin", "EnchantmentHelperMixin", "FoodDataMixin", "MixinPatchProjectileFinder", From 6999b42b1f33a32c70a621db9b47328937c7a9ff Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Wed, 17 Jan 2024 20:02:14 +0700 Subject: [PATCH 05/11] Since OTM does not specifically modify vanilla behavior when it's structures are in "default" state, accept connections to vanilla servers or server with OTM missing --- .../ru/dbotthepony/mc/otm/network/MatteryNetworkChannel.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/network/MatteryNetworkChannel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/network/MatteryNetworkChannel.kt index 7b69da821..ff1e4cf11 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/network/MatteryNetworkChannel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/network/MatteryNetworkChannel.kt @@ -38,7 +38,8 @@ interface MatteryPacket { abstract class MatteryNetworkChannel(val version: Int, val name: String) { val channel: SimpleChannel = ChannelBuilder .named(ResourceLocation(OverdriveThatMatters.MOD_ID, name)) - .acceptedVersions(Channel.VersionTest.exact(version)) + .clientAcceptedVersions { status, version -> (status == Channel.VersionTest.Status.MISSING || status == Channel.VersionTest.Status.VANILLA) || version == this.version } + .serverAcceptedVersions { status, version -> status == Channel.VersionTest.Status.PRESENT || version == this.version } .networkProtocolVersion(version) .simpleChannel() From 604029dcd500479c04d5b0387a6487da05d27fd3 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Wed, 17 Jan 2024 20:11:02 +0700 Subject: [PATCH 06/11] Add missing callback info --- .../java/ru/dbotthepony/mc/otm/mixin/BlockEntityMixin.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/ru/dbotthepony/mc/otm/mixin/BlockEntityMixin.java b/src/main/java/ru/dbotthepony/mc/otm/mixin/BlockEntityMixin.java index f537e2951..860683f11 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/mixin/BlockEntityMixin.java +++ b/src/main/java/ru/dbotthepony/mc/otm/mixin/BlockEntityMixin.java @@ -4,6 +4,7 @@ import net.minecraft.world.level.block.entity.BlockEntity; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import ru.dbotthepony.mc.otm.core.MultiblockKt; // because i know @@ -17,7 +18,7 @@ public abstract class BlockEntityMixin { at = @At("TAIL"), method = "setRemoved()V" ) - public void setRemovedListener() { + public void setRemovedListener(CallbackInfo ci) { MultiblockKt.onBlockEntityInvalidated((BlockEntity) (Object) this); } } From d540cbef3c1387a98425b21c40c95eab4003fec6 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Thu, 18 Jan 2024 19:28:04 +0700 Subject: [PATCH 07/11] Overflow prevention inside painter block entity --- .../mc/otm/block/entity/decorative/PainterBlockEntity.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/PainterBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/PainterBlockEntity.kt index 7a65e1450..5b594f8de 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/PainterBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/PainterBlockEntity.kt @@ -66,8 +66,8 @@ class PainterBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryDe } override fun fill(resource: FluidStack, action: IFluidHandler.FluidAction): Int { - if (resource.isEmpty || resource.fluid != Fluids.WATER || waterStored() >= MAX_WATER_STORAGE) return 0 - val diff = (waterStored() + resource.amount).coerceAtMost(MAX_WATER_STORAGE) - waterStored() + if (resource.amount <= 0 || resource.fluid != Fluids.WATER || waterStored() >= MAX_WATER_STORAGE) return 0 + val diff = ((waterStored().toLong() + resource.amount.toLong()).coerceAtMost(MAX_WATER_STORAGE.toLong()) - waterStored().toLong()).toInt() if (action.simulate() || diff == 0) return diff dyeStored[null] = waterStored() + diff return diff From 26382d96019438f3a84cfe8fcb6f7da995394bbf Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Thu, 18 Jan 2024 23:22:31 +0700 Subject: [PATCH 08/11] Fix wrong blast resistance of tritanium striped block --- src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlocks.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlocks.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlocks.kt index b2539b7b2..14c471d08 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlocks.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlocks.kt @@ -229,7 +229,7 @@ object MBlocks { .sound(SoundType.BASALT) .requiresCorrectToolForDrops() .explosionResistance(80f) - .strength(4f) + .destroyTime(2.5f) ) } val TRITANIUM_STRIPED_STAIRS: Block by registry.register(MNames.TRITANIUM_STRIPED_STAIRS) { StairBlock( @@ -251,7 +251,7 @@ object MBlocks { .sound(SoundType.BASALT) .requiresCorrectToolForDrops() .explosionResistance(40f) - .strength(3f) + .destroyTime(1.5f) ) } val MULTIBLOCK_TEST by registry.register("multiblock_test") { MultiblockTestBlock() } From 8ebc611c7cbc27970c525b8e8a8626e71ec61c8b Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Sat, 20 Jan 2024 18:31:39 +0700 Subject: [PATCH 09/11] Custom splashes --- .../mc/otm/mixin/SplashManagerMixin.java | 40 +++++++++++++++++++ .../overdrive_that_matters.mixins.json | 3 +- 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 src/main/java/ru/dbotthepony/mc/otm/mixin/SplashManagerMixin.java diff --git a/src/main/java/ru/dbotthepony/mc/otm/mixin/SplashManagerMixin.java b/src/main/java/ru/dbotthepony/mc/otm/mixin/SplashManagerMixin.java new file mode 100644 index 000000000..99e17c70e --- /dev/null +++ b/src/main/java/ru/dbotthepony/mc/otm/mixin/SplashManagerMixin.java @@ -0,0 +1,40 @@ +package ru.dbotthepony.mc.otm.mixin; + +import net.minecraft.client.resources.SplashManager; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.util.profiling.ProfilerFiller; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.List; + +@Mixin(SplashManager.class) +public abstract class SplashManagerMixin { + @Shadow + public List splashes; + + @Inject( + at = @At("TAIL"), + method = "apply(Ljava/util/List;Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/util/profiling/ProfilerFiller;)V" + ) + public void otmSplashes(List splashes, ResourceManager p_118879_, ProfilerFiller p_118880_, CallbackInfo ci) { + this.splashes.add("42 is the answer!"); + this.splashes.add("Now with matter!"); + this.splashes.add("Now with four dimensions!"); + this.splashes.add("Now with more ways to slice Creepers!"); + this.splashes.add("Batteries not included!"); + this.splashes.add("As it was 13 nanoseconds ago!"); + this.splashes.add("Smart AI is smart enough to fail Turing test!"); + this.splashes.add("Neural computing!"); + this.splashes.add("Swimming through quantum field!"); + this.splashes.add("For biological and digital alike!"); + this.splashes.add("Digital is the next form of Biological!"); + + this.splashes.add("Also try Starbound!"); + this.splashes.add("Also try No Man's Sky!"); + this.splashes.add("Also try Factorio!"); + } +} diff --git a/src/main/resources/overdrive_that_matters.mixins.json b/src/main/resources/overdrive_that_matters.mixins.json index 37f1c45b8..95ba2b017 100644 --- a/src/main/resources/overdrive_that_matters.mixins.json +++ b/src/main/resources/overdrive_that_matters.mixins.json @@ -23,6 +23,7 @@ ], "client": [ "MixinGameRenderer", - "MixinMinecraft" + "MixinMinecraft", + "SplashManagerMixin" ] } From 9ad1c8e82a379c483aaf154a7d32be0318e97640 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Sun, 21 Jan 2024 23:32:40 +0700 Subject: [PATCH 10/11] Micro optimize combinedcontainer stack iterator for best case scenario --- .../ru/dbotthepony/mc/otm/container/CombinedContainer.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/container/CombinedContainer.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/container/CombinedContainer.kt index 36e2aa9d8..992139a7a 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/container/CombinedContainer.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/container/CombinedContainer.kt @@ -16,6 +16,7 @@ import net.minecraft.world.item.ItemStack import ru.dbotthepony.mc.otm.container.util.IContainerSlot import ru.dbotthepony.mc.otm.container.util.containerSlot import ru.dbotthepony.mc.otm.container.util.iterator +import ru.dbotthepony.mc.otm.container.util.slotIterator import ru.dbotthepony.mc.otm.core.collect.concatIterators import ru.dbotthepony.mc.otm.core.collect.filter import ru.dbotthepony.mc.otm.core.collect.flatMap @@ -154,6 +155,9 @@ class CombinedContainer(containers: Stream>>) : IM } override fun iterator(nonEmpty: Boolean): Iterator { + if (notFullCoverage.isEmpty()) + return fullCoverage.iterator().flatMap { it.iterator(nonEmpty) } + return concatIterators( fullCoverage.iterator().flatMap { it.iterator(nonEmpty) }, notFullCoverage.values.iterator().flatMap { it.iterator() }.map { it.item }.let { if (nonEmpty) it.filter { it.isNotEmpty } else it } From d18399795bd9869e9bfeff9b0f51355988ee8b8b Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Sun, 21 Jan 2024 23:33:03 +0700 Subject: [PATCH 11/11] Void/Unit subscripable --- .../dbotthepony/mc/otm/core/ISubscripable.kt | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/ISubscripable.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/ISubscripable.kt index 5bbd7b52e..75f487e45 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/ISubscripable.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/ISubscripable.kt @@ -66,6 +66,44 @@ interface ISubscriptable { } } +interface IUnitSubscripable : ISubscriptable { + fun addListener(listener: Runnable): ISubscriptable.L + + override fun addListener(listener: Consumer): ISubscriptable.L { + return addListener(Runnable { listener.accept(Unit) }) + } + + class Impl : IUnitSubscripable, Runnable { + private inner class L(val callback: Runnable) : ISubscriptable.L { + private var isRemoved = false + + init { + subscribers.add(this) + } + + override fun remove() { + if (!isRemoved) { + isRemoved = true + queue.add(this) + } + } + } + + private val subscribers = ReferenceLinkedOpenHashSet(0) + private val queue = ReferenceArraySet(0) + + override fun addListener(listener: Runnable): ISubscriptable.L { + return L(listener) + } + + override fun run() { + queue.forEach { subscribers.remove(it) } + queue.clear() + subscribers.forEach { it.callback.run() } + } + } +} + interface IFloatSubcripable : ISubscriptable { @Deprecated("Use type specific listener") override fun addListener(listener: Consumer): ISubscriptable.L {