diff --git a/src/main/java/ru/dbotthepony/mc/otm/capability/MatteryCapability.java b/src/main/java/ru/dbotthepony/mc/otm/capability/MatteryCapability.java index 6f30afc7e..77b2c9f5e 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/capability/MatteryCapability.java +++ b/src/main/java/ru/dbotthepony/mc/otm/capability/MatteryCapability.java @@ -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 UPGRADE = ItemCapability.createVoid(ResourceLocation.fromNamespaceAndPath(OverdriveThatMatters.MOD_ID, "machine_upgrade"), IMatteryUpgrade.class); + + @Nonnull + @NotNull + public static final BlockCapability QUICK_STACK_CONTAINER = BlockCapability.createVoid(ResourceLocation.fromNamespaceAndPath(OverdriveThatMatters.MOD_ID, "quick_stack_container"), IQuickStackContainer.class); } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/CargoCrateBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/CargoCrateBlockEntity.kt index 4530adaef..020a1d5b5 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/CargoCrateBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/CargoCrateBlockEntity.kt @@ -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) { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/IQuickStackContainer.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/IQuickStackContainer.kt new file mode 100644 index 000000000..cbf7209cc --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/IQuickStackContainer.kt @@ -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 + + class Simple(private val slots: Collection) : IQuickStackContainer { + override fun getSlotsFor(player: ServerPlayer): Collection { + return slots + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/ExopackInventoryScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/ExopackInventoryScreen.kt index d02636fa7..c6816f8e6 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/ExopackInventoryScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/ExopackInventoryScreen.kt @@ -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(menu, TranslatableComponent("otm.gui.exopack")) { @@ -283,6 +286,17 @@ class ExopackInventoryScreen(menu: ExopackInventoryMenu) : MatteryScreen>( return result } - private abstract inner class FoldableButtonPanel() : ButtonPanel(screen, this@DeviceControls, width = 18f, height = 18f) { + abstract inner class FoldableButtonPanel() : ButtonPanel(screen, this@DeviceControls, width = 18f, height = 18f) { init { addButton(this) } private var buttons: List>? = null - fun makeButtons() { if (buttons == null) { buttons = doMakeButtons().also { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/compat/vanilla/VanillaMenuTypes.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/compat/vanilla/VanillaMenuTypes.kt index d4b068d73..fbca57f62 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/compat/vanilla/VanillaMenuTypes.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/compat/vanilla/VanillaMenuTypes.kt @@ -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) { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt index 90966e14e..1c33b2955 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt @@ -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 > FriendlyByteBuf.readEnum(entries: EnumEntries): E { + return entries[readVarInt()] +} + fun FriendlyByteBuf.readType(registry: IdMap): T { val id = readInt() return registry.byId(id) ?: throw NoSuchElementException("No such entry with ID $id") diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/math/EuclidMath.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/math/EuclidMath.kt index 77d910401..574709d69 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/math/EuclidMath.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/math/EuclidMath.kt @@ -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()) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/entity/Ext.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/entity/Ext.kt index f9ec9cdbf..3071cfeaf 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/entity/Ext.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/entity/Ext.kt @@ -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(private val type: EntityDataAccessor, priv } fun SynchedEntityData.delegate(type: EntityDataAccessor) = SynchedEntityDataDelegate(type, this) + +fun Player.checkCanInteract(pos: BlockPos): Boolean { + return mayInteract(level(), pos) && canInteractWithBlock(pos, 1.0) +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MatteryMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MatteryMenu.kt index 37131c37e..64051b166 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MatteryMenu.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/MatteryMenu.kt @@ -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() { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/network/MatteryPlayerPackets.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/network/MatteryPlayerPackets.kt index 29bf837a9..c1ff400fc 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/network/MatteryPlayerPackets.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/network/MatteryPlayerPackets.kt @@ -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>() + + 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() + findCaps.forEach { (b, _) -> ignoreBlockstates.add(b.blockState) } + + val slots = ArrayList() + + 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 { + return TYPE + } + + fun write(buff: FriendlyByteBuf) { + buff.writeEnum(mode) + buff.writeBoolean(fromExopack) + } + + companion object { + val TYPE = CustomPacketPayload.Type(ResourceLocation(OverdriveThatMatters.MOD_ID, "quick_stack")) + val CODEC: StreamCodec = 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) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/network/NetworkPackets.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/network/NetworkPackets.kt index 07d715fc8..d23aad15e 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/network/NetworkPackets.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/network/NetworkPackets.kt @@ -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