Add UUID Locking (per unique item locking that allows movement)

This commit is contained in:
nea
2023-09-29 22:44:13 +02:00
parent 7a71e77b4c
commit f89135db7e
7 changed files with 97 additions and 20 deletions

View File

@@ -10,6 +10,7 @@ import moe.nea.firmament.events.IsSlotProtectedEvent;
import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.screen.slot.Slot; import net.minecraft.screen.slot.Slot;
import net.minecraft.screen.slot.SlotActionType;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
@@ -24,7 +25,7 @@ public abstract class MixinClientPlayerEntity extends PlayerEntity {
@Inject(method = "dropSelectedItem", at = @At("HEAD"), cancellable = true) @Inject(method = "dropSelectedItem", at = @At("HEAD"), cancellable = true)
public void onDropSelectedItem(boolean entireStack, CallbackInfoReturnable<Boolean> cir) { public void onDropSelectedItem(boolean entireStack, CallbackInfoReturnable<Boolean> cir) {
Slot fakeSlot = new Slot(getInventory(), getInventory().selectedSlot, 0, 0); Slot fakeSlot = new Slot(getInventory(), getInventory().selectedSlot, 0, 0);
if (IsSlotProtectedEvent.shouldBlockInteraction(fakeSlot)) { if (IsSlotProtectedEvent.shouldBlockInteraction(fakeSlot, SlotActionType.THROW)) {
cir.setReturnValue(false); cir.setReturnValue(false);
} }
} }

View File

@@ -10,6 +10,7 @@ import moe.nea.firmament.events.*;
import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.ingame.HandledScreen; import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.entity.player.PlayerInventory; import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.screen.ScreenHandler; import net.minecraft.screen.ScreenHandler;
import net.minecraft.screen.slot.Slot; import net.minecraft.screen.slot.Slot;
import net.minecraft.screen.slot.SlotActionType; import net.minecraft.screen.slot.SlotActionType;
@@ -63,11 +64,19 @@ public abstract class MixinHandledScreen<T extends ScreenHandler> {
@Inject(method = "onMouseClick(Lnet/minecraft/screen/slot/Slot;IILnet/minecraft/screen/slot/SlotActionType;)V", at = @At("HEAD"), cancellable = true) @Inject(method = "onMouseClick(Lnet/minecraft/screen/slot/Slot;IILnet/minecraft/screen/slot/SlotActionType;)V", at = @At("HEAD"), cancellable = true)
public void onMouseClickedSlot(Slot slot, int slotId, int button, SlotActionType actionType, CallbackInfo ci) { public void onMouseClickedSlot(Slot slot, int slotId, int button, SlotActionType actionType, CallbackInfo ci) {
if (IsSlotProtectedEvent.shouldBlockInteraction(slot)) { if (slotId == -999 && getScreenHandler() != null && actionType == SlotActionType.PICKUP) { // -999 is code for "clicked outside the main window"
ItemStack cursorStack = getScreenHandler().getCursorStack();
if (cursorStack != null && IsSlotProtectedEvent.shouldBlockInteraction(slot, SlotActionType.THROW, cursorStack)) {
ci.cancel();
return;
}
}
if (IsSlotProtectedEvent.shouldBlockInteraction(slot, actionType)) {
ci.cancel(); ci.cancel();
return;
} }
if (actionType == SlotActionType.SWAP && 0 <= button && button < 9) { if (actionType == SlotActionType.SWAP && 0 <= button && button < 9) {
if (IsSlotProtectedEvent.shouldBlockInteraction(new Slot(playerInventory, button, 0, 0))) { if (IsSlotProtectedEvent.shouldBlockInteraction(new Slot(playerInventory, button, 0, 0), actionType)) {
ci.cancel(); ci.cancel();
} }
} }

View File

@@ -6,28 +6,37 @@
package moe.nea.firmament.events package moe.nea.firmament.events
import net.minecraft.item.ItemStack
import net.minecraft.screen.slot.Slot import net.minecraft.screen.slot.Slot
import net.minecraft.screen.slot.SlotActionType
import net.minecraft.text.Text import net.minecraft.text.Text
import moe.nea.firmament.util.CommonSoundEffects import moe.nea.firmament.util.CommonSoundEffects
import moe.nea.firmament.util.MC import moe.nea.firmament.util.MC
data class IsSlotProtectedEvent( data class IsSlotProtectedEvent(
val slot: Slot, var isProtected: Boolean = false val slot: Slot?,
val actionType: SlotActionType,
var isProtected: Boolean,
val itemStackOverride: ItemStack?,
) : FirmamentEvent() { ) : FirmamentEvent() {
val itemStack get() = itemStackOverride ?: slot!!.stack
fun protect() {
isProtected = true
}
companion object : FirmamentEventBus<IsSlotProtectedEvent>() { companion object : FirmamentEventBus<IsSlotProtectedEvent>() {
@JvmStatic @JvmStatic
fun shouldBlockInteraction(slot: Slot?): Boolean { @JvmOverloads
if (slot == null) return false fun shouldBlockInteraction(slot: Slot?, action: SlotActionType, itemStackOverride: ItemStack? = null): Boolean {
return publish(IsSlotProtectedEvent(slot)).isProtected.also { if (slot == null && itemStackOverride == null) return false
val event = IsSlotProtectedEvent(slot, action, false, itemStackOverride)
return publish(event).isProtected.also {
if (it) { if (it) {
MC.player?.sendMessage(Text.translatable("firmament.protectitem").append(slot.stack.name)) MC.player?.sendMessage(Text.translatable("firmament.protectitem").append(event.itemStack.name))
CommonSoundEffects.playFailure() CommonSoundEffects.playFailure()
} }
} }
} }
} }
fun protect() {
isProtected = true
}
} }

View File

@@ -3,23 +3,30 @@
* *
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
*/ */
@file:UseSerializers(DashlessUUIDSerializer::class)
package moe.nea.firmament.features.inventory package moe.nea.firmament.features.inventory
import java.util.*
import org.lwjgl.glfw.GLFW import org.lwjgl.glfw.GLFW
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers
import kotlinx.serialization.serializer import kotlinx.serialization.serializer
import net.minecraft.entity.player.PlayerInventory import net.minecraft.entity.player.PlayerInventory
import net.minecraft.screen.slot.SlotActionType
import moe.nea.firmament.events.HandledScreenKeyPressedEvent import moe.nea.firmament.events.HandledScreenKeyPressedEvent
import moe.nea.firmament.events.IsSlotProtectedEvent import moe.nea.firmament.events.IsSlotProtectedEvent
import moe.nea.firmament.events.SlotRenderEvents import moe.nea.firmament.events.SlotRenderEvents
import moe.nea.firmament.features.FirmamentFeature import moe.nea.firmament.features.FirmamentFeature
import moe.nea.firmament.gui.config.ManagedConfig import moe.nea.firmament.gui.config.ManagedConfig
import moe.nea.firmament.keybindings.SavedKeyBinding
import moe.nea.firmament.mixins.accessor.AccessorHandledScreen import moe.nea.firmament.mixins.accessor.AccessorHandledScreen
import moe.nea.firmament.util.CommonSoundEffects import moe.nea.firmament.util.CommonSoundEffects
import moe.nea.firmament.util.MC import moe.nea.firmament.util.MC
import moe.nea.firmament.util.SBData import moe.nea.firmament.util.SBData
import moe.nea.firmament.util.data.ProfileSpecificDataHolder import moe.nea.firmament.util.data.ProfileSpecificDataHolder
import moe.nea.firmament.util.json.DashlessUUIDSerializer
import moe.nea.firmament.util.skyblockUUID
object SlotLocking : FirmamentFeature { object SlotLocking : FirmamentFeature {
override val identifier: String override val identifier: String
@@ -29,10 +36,15 @@ object SlotLocking : FirmamentFeature {
data class Data( data class Data(
val lockedSlots: MutableSet<Int> = mutableSetOf(), val lockedSlots: MutableSet<Int> = mutableSetOf(),
val lockedSlotsRift: MutableSet<Int> = mutableSetOf(), val lockedSlotsRift: MutableSet<Int> = mutableSetOf(),
val lockedUUIDs: MutableSet<UUID> = mutableSetOf(),
) )
object TConfig : ManagedConfig(identifier) { object TConfig : ManagedConfig(identifier) {
val lock by keyBinding("lock") { GLFW.GLFW_KEY_L } val lockSlot by keyBinding("lock") { GLFW.GLFW_KEY_L }
val lockUUID by keyBindingWithOutDefaultModifiers("lock-uuid") {
SavedKeyBinding(GLFW.GLFW_KEY_L, shift = true)
}
} }
override val config: TConfig override val config: TConfig
@@ -40,6 +52,8 @@ object SlotLocking : FirmamentFeature {
object DConfig : ProfileSpecificDataHolder<Data>(serializer(), "locked-slots", ::Data) object DConfig : ProfileSpecificDataHolder<Data>(serializer(), "locked-slots", ::Data)
val lockedUUIDs get() = DConfig.data?.lockedUUIDs
val lockedSlots val lockedSlots
get() = when (SBData.skyblockLocation) { get() = when (SBData.skyblockLocation) {
"rift" -> DConfig.data?.lockedSlotsRift "rift" -> DConfig.data?.lockedSlotsRift
@@ -49,7 +63,7 @@ object SlotLocking : FirmamentFeature {
override fun onLoad() { override fun onLoad() {
HandledScreenKeyPressedEvent.subscribe { HandledScreenKeyPressedEvent.subscribe {
if (!it.matches(TConfig.lock)) return@subscribe if (!it.matches(TConfig.lockSlot)) return@subscribe
val inventory = MC.handledScreen ?: return@subscribe val inventory = MC.handledScreen ?: return@subscribe
inventory as AccessorHandledScreen inventory as AccessorHandledScreen
@@ -65,19 +79,56 @@ object SlotLocking : FirmamentFeature {
CommonSoundEffects.playSuccess() CommonSoundEffects.playSuccess()
} }
} }
HandledScreenKeyPressedEvent.subscribe {
if (!it.matches(TConfig.lockUUID)) return@subscribe
val inventory = MC.handledScreen ?: return@subscribe
inventory as AccessorHandledScreen
val slot = inventory.focusedSlot_Firmament ?: return@subscribe
val stack = slot.stack ?: return@subscribe
val uuid = stack.skyblockUUID ?: return@subscribe
val lockedUUIDs = lockedUUIDs ?: return@subscribe
if (uuid in lockedUUIDs) {
lockedUUIDs.remove(uuid)
} else {
lockedUUIDs.add(uuid)
}
DConfig.markDirty()
CommonSoundEffects.playSuccess()
}
IsSlotProtectedEvent.subscribe { IsSlotProtectedEvent.subscribe {
if (it.slot.inventory is PlayerInventory && it.slot.index in (lockedSlots ?: setOf())) { if (it.slot != null && it.slot.inventory is PlayerInventory && it.slot.index in (lockedSlots ?: setOf())) {
it.protect()
}
}
IsSlotProtectedEvent.subscribe {
if (it.actionType == SlotActionType.SWAP
|| it.actionType == SlotActionType.PICKUP
|| it.actionType == SlotActionType.QUICK_MOVE
|| it.actionType == SlotActionType.QUICK_CRAFT
|| it.actionType == SlotActionType.CLONE
|| it.actionType == SlotActionType.PICKUP_ALL
) return@subscribe
val stack = it.itemStack ?: return@subscribe
val uuid = stack.skyblockUUID ?: return@subscribe
if (uuid in (lockedUUIDs ?: return@subscribe)) {
it.protect() it.protect()
} }
} }
SlotRenderEvents.Before.subscribe { SlotRenderEvents.Before.subscribe {
if (it.slot.inventory is PlayerInventory && it.slot.index in (lockedSlots ?: setOf())) { val isSlotLocked = it.slot.inventory is PlayerInventory && it.slot.index in (lockedSlots ?: setOf())
val isUUIDLocked = (it.slot.stack?.skyblockUUID ?: return@subscribe) in (lockedUUIDs ?: setOf())
if (isSlotLocked || isUUIDLocked) {
it.context.fill( it.context.fill(
it.slot.x, it.slot.x,
it.slot.y, it.slot.y,
it.slot.x + 16, it.slot.x + 16,
it.slot.y + 16, it.slot.y + 16,
0xFFFF0000.toInt() when {
isSlotLocked -> 0xFFFF0000.toInt()
isUUIDLocked -> 0xFF00FF00.toInt()
else -> error("Slot is locked, but not by slot or uuid")
}
) )
} }
} }

View File

@@ -13,6 +13,7 @@ import net.minecraft.client.gui.screen.ingame.HandledScreen
import net.minecraft.text.Text import net.minecraft.text.Text
import net.minecraft.util.math.BlockPos import net.minecraft.util.math.BlockPos
import moe.nea.firmament.events.TickEvent import moe.nea.firmament.events.TickEvent
import moe.nea.firmament.mixins.accessor.AccessorHandledScreen
object MC { object MC {

View File

@@ -5,11 +5,12 @@
*/ */
@file:UseSerializers(DashlessUUIDSerializer::class) @file:UseSerializers(DashlessUUIDSerializer::class)
package moe.nea.firmament.util package moe.nea.firmament.util
import io.github.moulberry.repo.data.NEUItem import io.github.moulberry.repo.data.NEUItem
import io.github.moulberry.repo.data.Rarity import io.github.moulberry.repo.data.Rarity
import java.util.UUID import java.util.*
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers import kotlinx.serialization.UseSerializers
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
@@ -73,6 +74,12 @@ private val jsonparser = Json { ignoreUnknownKeys = true }
val ItemStack.extraAttributes: NbtCompound val ItemStack.extraAttributes: NbtCompound
get() = getOrCreateSubNbt("ExtraAttributes") get() = getOrCreateSubNbt("ExtraAttributes")
val ItemStack.skyblockUUIDString: String?
get() = extraAttributes.getString("uuid")?.takeIf { it.isNotBlank() }
val ItemStack.skyblockUUID: UUID?
get() = skyblockUUIDString?.let { UUID.fromString(it) }
val ItemStack.petData: HypixelPetInfo? val ItemStack.petData: HypixelPetInfo?
get() { get() {
val jsonString = extraAttributes.getString("petInfo") val jsonString = extraAttributes.getString("petInfo")

View File

@@ -57,8 +57,6 @@
"firmament.config.fishing-warning": "Fishing Warning", "firmament.config.fishing-warning": "Fishing Warning",
"firmament.config.fishing-warning.display-warning": "Display a warning when you are about to hook a fish", "firmament.config.fishing-warning.display-warning": "Display a warning when you are about to hook a fish",
"firmament.config.fishing-warning.highlight-wake-chain": "Highlight fishing particles", "firmament.config.fishing-warning.highlight-wake-chain": "Highlight fishing particles",
"firmament.key.slotlocking": "Lock Slot / Slot Binding",
"firmament.key.category": "Firmament",
"firmament.protectitem": "Firmament protected your item: ", "firmament.protectitem": "Firmament protected your item: ",
"firmament.recipe.forge.time": "Forging Time: %s", "firmament.recipe.forge.time": "Forging Time: %s",
"firmament.pv.skills": "Skills", "firmament.pv.skills": "Skills",
@@ -97,6 +95,7 @@
"firmament.keybinding.external": "External", "firmament.keybinding.external": "External",
"firmament.config.slot-locking": "Slot Locking", "firmament.config.slot-locking": "Slot Locking",
"firmament.config.slot-locking.lock": "Lock Slot", "firmament.config.slot-locking.lock": "Lock Slot",
"firmament.config.slot-locking.lock-uuid": "Lock UUID (Lock Item)",
"firmament.config.fixes.auto-sprint": "Auto Sprint", "firmament.config.fixes.auto-sprint": "Auto Sprint",
"firmament.config.fixes.auto-sprint-keybinding": "Auto Sprint KeyBinding", "firmament.config.fixes.auto-sprint-keybinding": "Auto Sprint KeyBinding",
"firmament.config.fixes.auto-sprint-hud": "Sprint State Hud", "firmament.config.fixes.auto-sprint-hud": "Sprint State Hud",