"Quick stack to nearby chests" prototype

This commit is contained in:
DBotThePony 2025-03-18 22:37:44 +07:00
parent 091ffc8c9f
commit d6f53946d9
Signed by: DBot
GPG Key ID: DCC23B5715498507
12 changed files with 189 additions and 9 deletions

View File

@ -15,7 +15,6 @@ import ru.dbotthepony.mc.otm.capability.matter.IReplicationTaskProvider;
import ru.dbotthepony.mc.otm.capability.matter.IPatternStorage;
import ru.dbotthepony.mc.otm.graph.matter.MatterNode;
import ru.dbotthepony.mc.otm.graph.storage.StorageNode;
import ru.dbotthepony.mc.otm.storage.StorageStack;
import javax.annotation.Nonnull;
@ -70,4 +69,8 @@ public class MatteryCapability {
@Nonnull
@NotNull
public static final ItemCapability<IMatteryUpgrade, Void> UPGRADE = ItemCapability.createVoid(ResourceLocation.fromNamespaceAndPath(OverdriveThatMatters.MOD_ID, "machine_upgrade"), IMatteryUpgrade.class);
@Nonnull
@NotNull
public static final BlockCapability<IQuickStackContainer, Void> QUICK_STACK_CONTAINER = BlockCapability.createVoid(ResourceLocation.fromNamespaceAndPath(OverdriveThatMatters.MOD_ID, "quick_stack_container"), IQuickStackContainer.class);
}

View File

@ -29,11 +29,15 @@ import net.minecraft.world.phys.Vec3
import net.neoforged.neoforge.capabilities.Capabilities
import ru.dbotthepony.mc.otm.block.decorative.CargoCrateBlock
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.capability.IQuickStackContainer
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.container.slotted.FilteredContainerSlot
import ru.dbotthepony.mc.otm.container.slotted.SlottedContainer
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.menu.MatteryMenuSlot
import ru.dbotthepony.mc.otm.menu.decorative.CargoCrateMenu
import ru.dbotthepony.mc.otm.menu.makeSlots
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
import ru.dbotthepony.mc.otm.registry.game.MSoundEvents
@ -57,6 +61,10 @@ class CargoCrateBlockEntity(
.build()
.also(::addDroppableContainer)
init {
exposeSideless(MatteryCapability.QUICK_STACK_CONTAINER, IQuickStackContainer.Simple(makeSlots(container, ::MatteryMenuSlot)))
}
private var interactingPlayers = 0
override fun beforeDroppingItems(oldBlockState: BlockState, level: Level, blockPos: BlockPos, newBlockState: BlockState, movedByPiston: Boolean) {

View File

@ -0,0 +1,14 @@
package ru.dbotthepony.mc.otm.capability
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.inventory.Slot
interface IQuickStackContainer {
fun getSlotsFor(player: ServerPlayer): Collection<Slot>
class Simple(private val slots: Collection<Slot>) : IQuickStackContainer {
override fun getSlotsFor(player: ServerPlayer): Collection<Slot> {
return slots
}
}
}

View File

@ -25,8 +25,11 @@ import ru.dbotthepony.mc.otm.client.setMousePos
import ru.dbotthepony.mc.otm.client.shouldOpenVanillaInventory
import ru.dbotthepony.mc.otm.core.math.integerDivisionDown
import ru.dbotthepony.mc.otm.menu.ExopackInventoryMenu
import ru.dbotthepony.mc.otm.menu.QuickMoveInput
import ru.dbotthepony.mc.otm.network.ExopackMenuOpen
import ru.dbotthepony.mc.otm.network.QuickStackPacket
import yalter.mousetweaks.api.MouseTweaksDisableWheelTweak
import java.util.function.IntConsumer
@MouseTweaksDisableWheelTweak
class ExopackInventoryScreen(menu: ExopackInventoryMenu) : MatteryScreen<ExopackInventoryMenu>(menu, TranslatableComponent("otm.gui.exopack")) {
@ -283,6 +286,17 @@ class ExopackInventoryScreen(menu: ExopackInventoryMenu) : MatteryScreen<Exopack
EffectListPanel(this, frame, menu.player, x - 56f, gridHeight = 6).tick()
val leftControls = DeviceControls(this, frame)
leftControls.dockOnLeft = true
leftControls.dockTop = 96f
leftControls.addButton(ButtonPanel.square18(
this, leftControls,
icon = Widgets18.RESTOCK_WITH_MOVE_TO_STORAGE,
onPress = IntConsumer { PacketDistributor.sendToServer(QuickStackPacket(QuickMoveInput.Mode.RESTOCK_WITH_MOVE, true)) }).also {
it.tooltips.add(QuickMoveInput.Mode.RESTOCK_WITH_MOVE.textToStorage)
})
return frame
}

View File

@ -457,14 +457,13 @@ class DeviceControls<out S : MatteryScreen<*>>(
return result
}
private abstract inner class FoldableButtonPanel() : ButtonPanel<S>(screen, this@DeviceControls, width = 18f, height = 18f) {
abstract inner class FoldableButtonPanel() : ButtonPanel<S>(screen, this@DeviceControls, width = 18f, height = 18f) {
init {
addButton(this)
}
private var buttons: List<EditablePanel<S>>? = null
fun makeButtons() {
if (buttons == null) {
buttons = doMakeButtons().also {

View File

@ -1,11 +1,24 @@
package ru.dbotthepony.mc.otm.compat.vanilla
import net.minecraft.core.BlockPos
import net.minecraft.core.registries.Registries
import net.minecraft.world.Container
import net.minecraft.world.flag.FeatureFlags
import net.minecraft.world.inventory.MenuType
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Blocks
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.entity.ChestBlockEntity
import net.minecraft.world.level.block.state.BlockState
import net.neoforged.bus.api.IEventBus
import net.neoforged.neoforge.capabilities.IBlockCapabilityProvider
import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent
import net.neoforged.neoforge.client.event.RegisterMenuScreensEvent
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.capability.IQuickStackContainer
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.menu.MatteryMenuSlot
import ru.dbotthepony.mc.otm.menu.makeSlots
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
object VanillaMenuTypes {
@ -22,9 +35,26 @@ object VanillaMenuTypes {
val SHULKER_BOX by registrar.register("shulker_box") { MenuType(::MatteryShulkerBoxMenu, FeatureFlags.VANILLA_SET) }
private fun provider(level: Level, pos: BlockPos, state: BlockState, blockEntity: BlockEntity?, context: Void?): IQuickStackContainer? {
val container = blockEntity as? Container ?: return null
return IQuickStackContainer.Simple(makeSlots(container, ::MatteryMenuSlot))
}
fun registerCapabilities(event: RegisterCapabilitiesEvent) {
event.registerBlock(
MatteryCapability.QUICK_STACK_CONTAINER,
::provider,
Blocks.CHEST,
Blocks.TRAPPED_CHEST,
Blocks.SHULKER_BOX,
Blocks.BARREL,
)
}
internal fun register(bus: IEventBus) {
registrar.register(bus)
bus.addListener(this::registerScreens)
bus.addListener(this::registerCapabilities)
}
private fun registerScreens(event: RegisterMenuScreensEvent) {

View File

@ -72,6 +72,7 @@ import java.util.random.RandomGenerator
import java.util.stream.Stream
import java.util.stream.StreamSupport
import kotlin.NoSuchElementException
import kotlin.enums.EnumEntries
import kotlin.jvm.optionals.getOrNull
import kotlin.math.ln
import kotlin.math.sqrt
@ -301,6 +302,10 @@ fun OutputStream.writeItemType(value: Item) {
writeVarIntLE(BuiltInRegistries.ITEM.getId(value))
}
fun <E : Enum<E>> FriendlyByteBuf.readEnum(entries: EnumEntries<E>): E {
return entries[readVarInt()]
}
fun <T : Any> FriendlyByteBuf.readType(registry: IdMap<T>): T {
val id = readInt()
return registry.byId(id) ?: throw NoSuchElementException("No such entry with ID $id")

View File

@ -435,6 +435,14 @@ operator fun Vec3i.times(int: Int): Vec3i = this.multiply(int)
operator fun Direction.times(int: Int): Vec3i = this.normal.multiply(int)
operator fun Vec3i.times(double: Double): Vector = Vector(x * double, y * double, z * double)
operator fun Vector.plus(direction: Vec3i): Vector = Vector(x + direction.x, y + direction.y, z + direction.z)
operator fun Vector.plus(direction: Direction): Vector = plus(direction.normal)
operator fun Vector.minus(direction: Vec3i): Vector = Vector(x - direction.x, y - direction.y, z - direction.z)
operator fun Vector.minus(direction: Direction): Vector = minus(direction.normal)
operator fun Vec3i.plus(direction: Vector): Vector = Vector(x + direction.x, y + direction.y, z + direction.z)
operator fun Vec3i.minus(direction: Vector): Vector = Vector(x - direction.x, y - direction.y, z - direction.z)
fun Vec3.toIntVector() = Vec3i(x.toInt(), y.toInt(), z.toInt())
fun Vec3.roundToIntVector() = Vec3i(x.roundToInt(), y.roundToInt(), z.roundToInt())

View File

@ -1,8 +1,10 @@
package ru.dbotthepony.mc.otm.entity
import net.minecraft.core.BlockPos
import net.minecraft.network.syncher.EntityDataAccessor
import net.minecraft.network.syncher.SynchedEntityData
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.player.Player
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
@ -17,3 +19,7 @@ class SynchedEntityDataDelegate<T>(private val type: EntityDataAccessor<T>, priv
}
fun <T> SynchedEntityData.delegate(type: EntityDataAccessor<T>) = SynchedEntityDataDelegate(type, this)
fun Player.checkCanInteract(pos: BlockPos): Boolean {
return mayInteract(level(), pos) && canInteractWithBlock(pos, 1.0)
}

View File

@ -42,6 +42,7 @@ import ru.dbotthepony.mc.otm.container.util.containerSlotOrNull
import ru.dbotthepony.mc.otm.core.ResourceLocation
import ru.dbotthepony.mc.otm.core.collect.ConditionalSet
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.entity.checkCanInteract
import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
import ru.dbotthepony.mc.otm.network.MatteryStreamCodec
@ -346,13 +347,10 @@ abstract class MatteryMenu(
override fun stillValid(player: Player): Boolean {
if (tile == null) return true
if (player.level().getBlockEntity(tile.blockPos) !== tile) {
if (player.level().getBlockEntity(tile.blockPos) !== tile)
return false
}
val pos = tile.blockPos
return player.distanceToSqr(pos.x.toDouble() + 0.5, pos.y.toDouble() + 0.5, pos.z.toDouble() + 0.5) <= 64.0
return player.checkCanInteract(tile.blockPos)
}
fun syncCarried() {

View File

@ -1,6 +1,7 @@
package ru.dbotthepony.mc.otm.network
import it.unimi.dsi.fastutil.bytes.ByteArrayList
import net.minecraft.core.BlockPos
import net.minecraft.core.particles.ParticleTypes
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
@ -8,28 +9,47 @@ import net.minecraft.network.codec.StreamCodec
import net.minecraft.network.protocol.common.custom.CustomPacketPayload
import net.minecraft.network.protocol.game.ClientboundSetCarriedItemPacket
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.ai.attributes.Attributes
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.inventory.Slot
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.ChunkPos
import net.minecraft.world.level.ClipBlockStateContext
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.phys.Vec3
import net.neoforged.neoforge.common.NeoForgeMod
import net.neoforged.neoforge.network.handling.IPayloadContext
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.kommons.math.RGBAColor
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.capability.IQuickStackContainer
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.player.MatteryPlayer
import ru.dbotthepony.mc.otm.player.matteryPlayer
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.container.get
import ru.dbotthepony.mc.otm.container.set
import ru.dbotthepony.mc.otm.core.ResourceLocation
import ru.dbotthepony.mc.otm.core.getChunkNow
import ru.dbotthepony.mc.otm.core.math.Vector
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.math.plus
import ru.dbotthepony.mc.otm.core.math.toRadians
import ru.dbotthepony.mc.otm.core.otmRandom
import ru.dbotthepony.mc.otm.core.position
import ru.dbotthepony.mc.otm.core.readEnum
import ru.dbotthepony.mc.otm.core.readItem
import ru.dbotthepony.mc.otm.core.writeItem
import ru.dbotthepony.mc.otm.entity.checkCanInteract
import ru.dbotthepony.mc.otm.menu.ExopackInventoryMenu
import ru.dbotthepony.mc.otm.menu.QuickMoveInput
import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.HashSet
class MatteryPlayerDataPacket(val bytes: ByteArrayList, val isPublic: Boolean, val target: UUID? = null) : CustomPacketPayload {
fun write(buff: FriendlyByteBuf) {
@ -414,6 +434,80 @@ object ResetExopackColorPacket : CustomPacketPayload {
)
}
// as separate packet so it can be put into non-mattery menus
class QuickStackPacket(
val mode: QuickMoveInput.Mode,
val fromExopack: Boolean
) : CustomPacketPayload {
fun play(context: IPayloadContext) {
val player = context.player() as ServerPlayer
if (player.isSpectator) return
val radius = player.blockInteractionRange()
val minChunkPos = ChunkPos(BlockPos.containing(player.position - Vector(radius, 0.0, radius)))
val maxChunkPos = ChunkPos(BlockPos.containing(player.position + Vector(radius, 0.0, radius)))
val findCaps = ArrayList<Pair<BlockEntity, IQuickStackContainer>>()
for (x in minChunkPos.x .. maxChunkPos.x) {
for (z in minChunkPos.z .. maxChunkPos.z) {
val chunk = player.serverLevel().chunkSource.getChunkNow(x, z) ?: continue
for (blockEntity in chunk.blockEntities.values) {
if (player.checkCanInteract(blockEntity.blockPos)) {
val cap = player.level().getCapability(MatteryCapability.QUICK_STACK_CONTAINER, blockEntity.blockPos) ?: continue
findCaps.add(blockEntity to cap)
}
}
}
}
if (findCaps.isEmpty()) return
val eyes = player.eyePosition
val ignoreBlockstates = HashSet<BlockState>()
findCaps.forEach { (b, _) -> ignoreBlockstates.add(b.blockState) }
val slots = ArrayList<Slot>()
for ((blockEntity, cap) in findCaps) {
// don't interact through walls
val trace = player.serverLevel().isBlockInLine(ClipBlockStateContext(eyes, Vector.atCenterOf(blockEntity.blockPos)) {
!it.isAir && it !in ignoreBlockstates
})
if (trace.blockPos == blockEntity.blockPos) {
slots.addAll(cap.getSlotsFor(player))
}
}
if (slots.isEmpty()) return
if (fromExopack)
mode.move(player.matteryPlayer.exoPackMenu.playerCombinedInventorySlots, slots, player)
else
mode.move(slots, player.matteryPlayer.exoPackMenu.playerInventorySlots, player)
}
override fun type(): CustomPacketPayload.Type<out CustomPacketPayload> {
return TYPE
}
fun write(buff: FriendlyByteBuf) {
buff.writeEnum(mode)
buff.writeBoolean(fromExopack)
}
companion object {
val TYPE = CustomPacketPayload.Type<QuickStackPacket>(ResourceLocation(OverdriveThatMatters.MOD_ID, "quick_stack"))
val CODEC: StreamCodec<FriendlyByteBuf, QuickStackPacket> = StreamCodec.ofMember(QuickStackPacket::write, ::read)
fun read(buff: FriendlyByteBuf): QuickStackPacket {
return QuickStackPacket(buff.readEnum(QuickMoveInput.Mode.entries), buff.readBoolean())
}
}
}
class ExopackSmokePacket(val player: UUID) : CustomPacketPayload {
fun write(buff: FriendlyByteBuf) {
buff.writeUUID(player)

View File

@ -20,7 +20,7 @@ import ru.dbotthepony.mc.otm.menu.matter.ReplicationRequestPacket
import ru.dbotthepony.mc.otm.menu.matter.TasksChangePacket
internal fun registerNetworkPackets(event: RegisterPayloadHandlersEvent) {
val r = event.registrar("1.5.0")
val r = event.registrar("1.5.1")
// world
r
@ -54,6 +54,7 @@ internal fun registerNetworkPackets(event: RegisterPayloadHandlersEvent) {
.playToServer(DisableExopackGlowPacket.TYPE, DisableExopackGlowPacket, DisableExopackGlowPacket::play)
.playToServer(ResetExopackColorPacket.TYPE, ResetExopackColorPacket, ResetExopackColorPacket::play)
.playToServer(SetExopackColorPacket.TYPE, SetExopackColorPacket.CODEC, SetExopackColorPacket::play)
.playToServer(QuickStackPacket.TYPE, QuickStackPacket.CODEC, QuickStackPacket::play)
// otm player general
r