feat(D-01/E): resistance & lock system rework (E1-E7)

E1: Initialize currentResistance in NBT at equip time from
    ResistanceComponent — eliminates MAX-scan fallback bug

E2: BuiltInLockComponent for organic items (already committed)

E3: canStruggle refactor — new model:
    - ARMS: always struggle-able (no lock gating)
    - Non-ARMS: only if locked OR built-in lock
    - Removed dead isItemLocked() from StruggleState + overrides

E4: canUnequip already handled by BuiltInLockComponent.blocksUnequip()
    via ComponentHolder delegation

E5: Help/assist mechanic deferred (needs UI design)

E6: Removed lock resistance from ILockable (5 methods + NBT key deleted)
    - GenericKnife: new knifeCutProgress NBT for cutting locks
    - StruggleAccessory: accessoryStruggleResistance NBT replaces lock resistance
    - PacketV2StruggleStart: uses config-based padlock resistance
    - All lock/unlock packets cleaned of initializeLockResistance/clearLockResistance

E7: Fixed 3 pre-existing bugs:
    - B2: DataDrivenItemRegistry.clear() synchronized on RELOAD_LOCK
    - B3: V2TyingPlayerTask validates heldStack before equip (prevents duplication)
    - B5: EntityKidnapperMerchant.remove() cleans playerToMerchant map (memory leak)
This commit is contained in:
NotEvil
2026-04-15 03:23:49 +02:00
parent 199bf00aef
commit d6bb030ad7
17 changed files with 76 additions and 228 deletions

View File

@@ -889,6 +889,10 @@ public class EntityKidnapperMerchant extends EntityKidnapperElite {
// Clear trading players to prevent dangling references // Clear trading players to prevent dangling references
if (!this.level().isClientSide) { if (!this.level().isClientSide) {
int count = tradingPlayers.size(); int count = tradingPlayers.size();
// Clean up reverse-lookup map BEFORE clearing to prevent memory leak
for (UUID playerUuid : tradingPlayers) {
playerToMerchant.remove(playerUuid);
}
this.tradingPlayers.clear(); this.tradingPlayers.clear();
if (count > 0) { if (count > 0) {
TiedUpMod.LOGGER.debug( TiedUpMod.LOGGER.debug(

View File

@@ -384,26 +384,27 @@ public class GenericKnife extends Item implements IKnife {
return; return;
} }
// Accessory IS locked - reduce lock resistance // Accessory IS locked - reduce lock resistance via knife cut progress
ILockable lockable = (ILockable) accessory.getItem(); ILockable lockable = (ILockable) accessory.getItem();
int currentRes = lockable.getCurrentLockResistance(accessory); int cutProgress = com.tiedup.remake.util.ItemNBTHelper.getInt(accessory, "knifeCutProgress");
int newRes = Math.max(0, currentRes - speed); int newProgress = cutProgress + speed;
lockable.setCurrentLockResistance(accessory, newRes); int lockResistance = com.tiedup.remake.core.SettingsAccessor.getPadlockResistance(null);
com.tiedup.remake.util.ItemNBTHelper.setInt(accessory, "knifeCutProgress", newProgress);
TiedUpMod.LOGGER.debug( TiedUpMod.LOGGER.debug(
"[GenericKnife] {} cutting {} lock: resistance {} -> {}", "[GenericKnife] {} cutting {} lock: progress {} / {}",
player.getName().getString(), player.getName().getString(),
target, target,
currentRes, newProgress,
newRes lockResistance
); );
// Check if lock is destroyed // Check if lock is destroyed
if (newRes <= 0) { if (newProgress >= lockResistance) {
// Destroy the lock (remove padlock, clear lock state) // Destroy the lock (remove padlock, clear lock state)
lockable.setLockedByKeyUUID(accessory, null); // Unlocks and clears locked state lockable.setLockedByKeyUUID(accessory, null); // Unlocks and clears locked state
lockable.setLockable(accessory, false); // Remove padlock entirely lockable.setLockable(accessory, false); // Remove padlock entirely
lockable.clearLockResistance(accessory); com.tiedup.remake.util.ItemNBTHelper.remove(accessory, "knifeCutProgress");
lockable.setJammed(accessory, false); lockable.setJammed(accessory, false);
state.clearKnifeCutTarget(); state.clearKnifeCutTarget();

View File

@@ -214,7 +214,6 @@ public class ItemLockpick extends Item {
if (success) { if (success) {
// SUCCESS: Unlock the item, PRESERVE the padlock // SUCCESS: Unlock the item, PRESERVE the padlock
lockable.setLockedByKeyUUID(targetStack, null); // Unlock lockable.setLockedByKeyUUID(targetStack, null); // Unlock
lockable.clearLockResistance(targetStack); // Clear struggle progress
// lockable stays true - padlock preserved! // lockable stays true - padlock preserved!
SystemMessageManager.sendToPlayer( SystemMessageManager.sendToPlayer(

View File

@@ -222,20 +222,6 @@ public interface ILockable {
*/ */
String NBT_JAMMED = "jammed"; String NBT_JAMMED = "jammed";
/**
* Get the resistance added by the lock for struggle mechanics.
*
* <p>When locked, this value is added to the item's base resistance.
* Configurable via server config and GameRule.</p>
*
* @return Lock resistance value (default: 250, configurable)
*/
default int getLockResistance() {
return com.tiedup.remake.core.SettingsAccessor.getPadlockResistance(
null
);
}
/** /**
* Check if the lock is jammed (lockpick failed critically). * Check if the lock is jammed (lockpick failed critically).
* *
@@ -266,63 +252,6 @@ public interface ILockable {
} }
} }
// ========== LOCK RESISTANCE (for struggle) ==========
/**
* NBT key for current lock resistance during struggle.
*/
String NBT_LOCK_RESISTANCE = "lockResistance";
/**
* Get the current lock resistance remaining for struggle.
* Initialized to getLockResistance() (configurable, default 250) when first locked.
*
* @param stack The ItemStack to check
* @return Current lock resistance (0 if not locked or fully struggled)
*/
default int getCurrentLockResistance(ItemStack stack) {
if (stack.isEmpty()) return 0;
// If locked but no resistance stored yet, initialize it
if (
isLocked(stack) &&
!ItemNBTHelper.contains(stack, NBT_LOCK_RESISTANCE)
) {
return getLockResistance(); // Configurable via ModConfig
}
return ItemNBTHelper.getInt(stack, NBT_LOCK_RESISTANCE);
}
/**
* Set the current lock resistance remaining for struggle.
*
* @param stack The ItemStack to modify
* @param resistance The new resistance value
*/
default void setCurrentLockResistance(ItemStack stack, int resistance) {
ItemNBTHelper.setInt(stack, NBT_LOCK_RESISTANCE, resistance);
}
/**
* Initialize lock resistance when item is locked.
* Called when setLockedByKeyUUID is called with a non-null UUID.
*
* @param stack The ItemStack to initialize
*/
default void initializeLockResistance(ItemStack stack) {
setCurrentLockResistance(stack, getLockResistance());
}
/**
* Clear lock resistance when item is unlocked.
*
* @param stack The ItemStack to clear
*/
default void clearLockResistance(ItemStack stack) {
ItemNBTHelper.remove(stack, NBT_LOCK_RESISTANCE);
}
// ========== LOCK BREAKING (struggle/force) ========== // ========== LOCK BREAKING (struggle/force) ==========
/** /**
@@ -334,7 +263,6 @@ public interface ILockable {
* <li>Unlocks the item (lockedByKeyUUID = null)</li> * <li>Unlocks the item (lockedByKeyUUID = null)</li>
* <li>Removes the lockable flag (no more padlock slot)</li> * <li>Removes the lockable flag (no more padlock slot)</li>
* <li>Clears any jam state</li> * <li>Clears any jam state</li>
* <li>Clears stored lock resistance</li>
* </ul> * </ul>
* *
* @param stack The ItemStack to break the lock on * @param stack The ItemStack to break the lock on
@@ -344,6 +272,5 @@ public interface ILockable {
setLockedByKeyUUID(stack, null); setLockedByKeyUUID(stack, null);
setLockable(stack, false); setLockable(stack, false);
setJammed(stack, false); setJammed(stack, false);
clearLockResistance(stack);
} }
} }

View File

@@ -394,7 +394,6 @@ public class GenericClothes extends Item implements ILockable, IV2BondageItem {
stack.getOrCreateTag().putBoolean(NBT_LOCKED, state); stack.getOrCreateTag().putBoolean(NBT_LOCKED, state);
if (!state) { if (!state) {
// When unlocking, clear lock-related data // When unlocking, clear lock-related data
clearLockResistance(stack);
setJammed(stack, false); setJammed(stack, false);
} }
return stack; return stack;
@@ -439,7 +438,6 @@ public class GenericClothes extends Item implements ILockable, IV2BondageItem {
if (keyUUID != null) { if (keyUUID != null) {
tag.putUUID(NBT_LOCKED_BY_KEY_UUID, keyUUID); tag.putUUID(NBT_LOCKED_BY_KEY_UUID, keyUUID);
setLocked(stack, true); setLocked(stack, true);
initializeLockResistance(stack);
} else { } else {
tag.remove(NBT_LOCKED_BY_KEY_UUID); tag.remove(NBT_LOCKED_BY_KEY_UUID);
setLocked(stack, false); setLocked(stack, false);

View File

@@ -622,18 +622,14 @@ public class StruggleSessionManager {
); );
if (accessoryStack.isEmpty()) return; if (accessoryStack.isEmpty()) return;
if ( // Update accessory struggle resistance in dedicated NBT key
accessoryStack.getItem() instanceof com.tiedup.remake.util.ItemNBTHelper.setInt(
com.tiedup.remake.items.base.ILockable lockable accessoryStack,
) { "accessoryStruggleResistance",
// Update the lock resistance to match session state session.getCurrentResistance()
lockable.setCurrentLockResistance( );
accessoryStack, // Sync V2 equipment state
session.getCurrentResistance() V2EquipmentHelper.sync(player);
);
// Sync V2 equipment state
V2EquipmentHelper.sync(player);
}
return; return;
} }

View File

@@ -172,47 +172,17 @@ public class PacketLockpickAttempt {
!targetStack.isEmpty() && !targetStack.isEmpty() &&
targetStack.getItem() instanceof ILockable lockable targetStack.getItem() instanceof ILockable lockable
) { ) {
// Get lock resistance BEFORE clearing it
int lockResistance = lockable.getCurrentLockResistance(targetStack);
// Unlock the item // Unlock the item
lockable.setLockedByKeyUUID(targetStack, null); lockable.setLockedByKeyUUID(targetStack, null);
lockable.clearLockResistance(targetStack);
TiedUpMod.LOGGER.info( TiedUpMod.LOGGER.info(
"[PacketLockpickAttempt] Player {} successfully picked lock on {} ({}, resistance was {})", "[PacketLockpickAttempt] Player {} successfully picked lock on {} ({})",
player.getName().getString(), player.getName().getString(),
targetStack.getDisplayName().getString(), targetStack.getDisplayName().getString(),
targetRegion, targetRegion
lockResistance
); );
// Deduct lock resistance from bind resistance // Lock is now unlocked -- bind resistance is tracked separately via IHasResistance
PlayerBindState state = PlayerBindState.getInstance(player);
if (state != null && state.isTiedUp() && lockResistance > 0) {
int currentBindResistance = state.getCurrentBindResistance();
int newBindResistance = Math.max(
0,
currentBindResistance - lockResistance
);
state.setCurrentBindResistance(newBindResistance);
TiedUpMod.LOGGER.info(
"[PacketLockpickAttempt] Deducted {} from bind resistance: {} -> {}",
lockResistance,
currentBindResistance,
newBindResistance
);
// Check if player escaped (resistance = 0)
if (newBindResistance <= 0) {
state.getStruggleBinds().successActionExternal(state);
TiedUpMod.LOGGER.info(
"[PacketLockpickAttempt] Player {} escaped via lockpick!",
player.getName().getString()
);
}
}
} }
// Damage lockpick // Damage lockpick

View File

@@ -432,26 +432,6 @@ public class PacketSlaveItemManage {
// Lock with keyUUID // Lock with keyUUID
lockable.setLockedByKeyUUID(itemStack, keyUUID); lockable.setLockedByKeyUUID(itemStack, keyUUID);
if (
region == BodyRegionV2.ARMS &&
itemStack.getItem() instanceof com.tiedup.remake.items.base.IHasResistance resistanceItem
) {
int currentResistance = resistanceItem.getCurrentResistance(
itemStack,
target
);
int lockResistance = lockable.getLockResistance(); // Configurable via ModConfig
resistanceItem.setCurrentResistance(
itemStack,
currentResistance + lockResistance
);
TiedUpMod.LOGGER.info(
"[PacketSlaveItemManage] Added {} lock resistance to bind (total: {})",
lockResistance,
currentResistance + lockResistance
);
}
TiedUpMod.LOGGER.info( TiedUpMod.LOGGER.info(
"[PacketSlaveItemManage] {} locked {}'s {} with key {}", "[PacketSlaveItemManage] {} locked {}'s {} with key {}",
sender.getName().getString(), sender.getName().getString(),
@@ -528,7 +508,6 @@ public class PacketSlaveItemManage {
// Unlock the item - just unlock, keep padlock attached (can be re-locked) // Unlock the item - just unlock, keep padlock attached (can be re-locked)
lockable.setLockedByKeyUUID(itemStack, null); lockable.setLockedByKeyUUID(itemStack, null);
lockable.clearLockResistance(itemStack); // Clear any struggle progress
// Note: Padlock is only dropped on REMOVE, not on UNLOCK // Note: Padlock is only dropped on REMOVE, not on UNLOCK
TiedUpMod.LOGGER.info( TiedUpMod.LOGGER.info(

View File

@@ -1,12 +1,15 @@
package com.tiedup.remake.state.struggle; package com.tiedup.remake.state.struggle;
import com.tiedup.remake.core.SettingsAccessor;
import com.tiedup.remake.core.SystemMessageManager; import com.tiedup.remake.core.SystemMessageManager;
import com.tiedup.remake.core.SystemMessageManager.MessageCategory; import com.tiedup.remake.core.SystemMessageManager.MessageCategory;
import com.tiedup.remake.core.TiedUpMod; import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.items.base.ILockable; import com.tiedup.remake.items.base.ILockable;
import com.tiedup.remake.state.PlayerBindState; import com.tiedup.remake.state.PlayerBindState;
import com.tiedup.remake.util.ItemNBTHelper;
import com.tiedup.remake.v2.BodyRegionV2; import com.tiedup.remake.v2.BodyRegionV2;
import com.tiedup.remake.v2.bondage.capability.V2EquipmentHelper; import com.tiedup.remake.v2.bondage.capability.V2EquipmentHelper;
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
@@ -32,28 +35,36 @@ public class StruggleAccessory extends StruggleState {
this.accessoryRegion = accessoryRegion; this.accessoryRegion = accessoryRegion;
} }
/** NBT key for accessory struggle progress (replaces removed ILockable lock resistance). */
private static final String NBT_ACCESSORY_STRUGGLE = "accessoryStruggleResistance";
/** /**
* Get the current resistance for this accessory. * Get the current resistance for this accessory.
* Accessories have 0 base resistance - only lock resistance exists. * Accessories have 0 base resistance -- when locked, resistance comes from
* Resistance is stored in the item's NBT to persist between attempts. * {@link SettingsAccessor#getPadlockResistance}. Stored in a dedicated NBT
* key on the accessory stack to persist between attempts.
* *
* @param state The player's bind state * @param state The player's bind state
* @return Current resistance value (0 if not locked, 250 if locked and not yet struggled) * @return Current resistance value (0 if not locked)
*/ */
@Override @Override
protected int getResistanceState(PlayerBindState state) { protected int getResistanceState(PlayerBindState state) {
ItemStack stack = getAccessoryStack(state); ItemStack stack = getAccessoryStack(state);
if (stack.isEmpty()) return 0; if (stack.isEmpty()) return 0;
if (stack.getItem() instanceof ILockable lockable) { if (stack.getItem() instanceof ILockable lockable && lockable.isLocked(stack)) {
return lockable.getCurrentLockResistance(stack); // If no struggle progress stored yet, initialize from config
if (!ItemNBTHelper.contains(stack, NBT_ACCESSORY_STRUGGLE)) {
return SettingsAccessor.getPadlockResistance(null);
}
return ItemNBTHelper.getInt(stack, NBT_ACCESSORY_STRUGGLE);
} }
return 0; return 0;
} }
/** /**
* Set the current resistance for this accessory struggle. * Set the current resistance for this accessory struggle.
* Stored in the item's NBT to persist between attempts. * Stored in a dedicated NBT key on the accessory stack.
* *
* @param state The player's bind state * @param state The player's bind state
* @param resistance The new resistance value * @param resistance The new resistance value
@@ -63,9 +74,7 @@ public class StruggleAccessory extends StruggleState {
ItemStack stack = getAccessoryStack(state); ItemStack stack = getAccessoryStack(state);
if (stack.isEmpty()) return; if (stack.isEmpty()) return;
if (stack.getItem() instanceof ILockable lockable) { ItemNBTHelper.setInt(stack, NBT_ACCESSORY_STRUGGLE, resistance);
lockable.setCurrentLockResistance(stack, resistance);
}
} }
/** /**
@@ -87,29 +96,15 @@ public class StruggleAccessory extends StruggleState {
return false; return false;
} }
// Only locked accessories can be struggled against // Only require struggle if LOCKED or has BUILT_IN_LOCK.
if (!(stack.getItem() instanceof ILockable lockable)) { // Unlocked accessories without built-in lock can be freely removed (no struggle needed).
boolean isLocked = stack.getItem() instanceof ILockable lockable && lockable.isLocked(stack);
boolean hasBuiltInLock = DataDrivenBondageItem.hasBuiltInLock(stack);
if (!isLocked && !hasBuiltInLock) {
return false; return false;
} }
return lockable.isLocked(stack); return true;
}
/**
* Check if the accessory is locked.
*
* @param state The player's bind state
* @return true if the accessory is locked
*/
@Override
protected boolean isItemLocked(PlayerBindState state) {
ItemStack stack = getAccessoryStack(state);
if (
stack.isEmpty() || !(stack.getItem() instanceof ILockable lockable)
) {
return false;
}
return lockable.isLocked(stack);
} }
/** /**

View File

@@ -1,6 +1,5 @@
package com.tiedup.remake.state.struggle; package com.tiedup.remake.state.struggle;
import com.tiedup.remake.core.SettingsAccessor;
import com.tiedup.remake.core.SystemMessageManager; import com.tiedup.remake.core.SystemMessageManager;
import com.tiedup.remake.core.SystemMessageManager.MessageCategory; import com.tiedup.remake.core.SystemMessageManager.MessageCategory;
import com.tiedup.remake.core.TiedUpMod; import com.tiedup.remake.core.TiedUpMod;
@@ -93,31 +92,6 @@ public class StruggleBinds extends StruggleState {
return true; return true;
} }
/**
* Check if the bind item is locked.
* Used by StruggleState to apply x10 resistance penalty.
*
* @param state The player's bind state
* @return true if the bind is locked
*/
@Override
protected boolean isItemLocked(PlayerBindState state) {
Player player = state.getPlayer();
if (player == null) return false;
ItemStack bindStack = V2EquipmentHelper.getInRegion(
player,
BodyRegionV2.ARMS
);
if (bindStack.isEmpty()) return false;
// Works for both V1 (ItemBind) and V2 (DataDrivenBondageItem) via ILockable
if (bindStack.getItem() instanceof ILockable lockable) {
return lockable.isLocked(bindStack);
}
return false;
}
/** /**
* Called when the player successfully escapes from their binds. * Called when the player successfully escapes from their binds.
* Drops all bondage items and completely unties the player. * Drops all bondage items and completely unties the player.

View File

@@ -107,13 +107,12 @@ public class StruggleCollar extends StruggleState {
return false; return false;
} }
// Check if locked (works for V1 and V2 via ILockable) // Only require struggle if LOCKED or has BUILT_IN_LOCK.
if (collar.getItem() instanceof ILockable lockable) { // Unlocked collars without built-in lock can be freely removed (no struggle needed).
if (!lockable.isLocked(collar)) { boolean isLocked = collar.getItem() instanceof ILockable lockable && lockable.isLocked(collar);
TiedUpMod.LOGGER.debug("[StruggleCollar] Collar is not locked"); boolean hasBuiltInLock = DataDrivenBondageItem.hasBuiltInLock(collar);
return false; if (!isLocked && !hasBuiltInLock) {
} TiedUpMod.LOGGER.debug("[StruggleCollar] Collar is not locked and has no built-in lock — no struggle needed");
} else {
return false; return false;
} }

View File

@@ -212,16 +212,6 @@ public abstract class StruggleState {
*/ */
protected abstract boolean canStruggle(PlayerBindState state); protected abstract boolean canStruggle(PlayerBindState state);
/**
* Check if the item being struggled against is locked.
*
* @param state The player's bind state
* @return true if the item is locked
*/
protected boolean isItemLocked(PlayerBindState state) {
return false; // Default: not locked, subclasses override
}
/** /**
* Check if debug logging is enabled. * Check if debug logging is enabled.
* *

View File

@@ -70,6 +70,13 @@ public class V2TyingPlayerTask extends TyingPlayerTask {
targetEntity.getName().getString() targetEntity.getName().getString()
); );
// Validate heldStack hasn't changed during tying (e.g. player swapped items)
if (heldStack.isEmpty() || !ItemStack.isSameItemSameTags(heldStack, bind)) {
TiedUpMod.LOGGER.warn("[V2TyingPlayerTask] heldStack changed during tying — aborting");
stop();
return;
}
// Equip via V2 system // Equip via V2 system
V2EquipResult result = V2EquipmentHelper.equipItem(targetEntity, bind); V2EquipResult result = V2EquipmentHelper.equipItem(targetEntity, bind);
if (result.isSuccess()) { if (result.isSuccess()) {

View File

@@ -356,6 +356,16 @@ public class DataDrivenBondageItem extends AbstractV2BondageItem {
holder.onEquipped(stack, entity); holder.onEquipped(stack, entity);
} }
// Initialize currentResistance in NBT from ResistanceComponent at equip time.
// This avoids the MAX-scan fallback in getBaseResistance(entity) which returns
// the highest resistance across ALL equipped data-driven items.
ResistanceComponent resistComp = getComponent(stack, ComponentType.RESISTANCE, ResistanceComponent.class);
if (resistComp != null) {
com.tiedup.remake.items.base.IHasResistance resistanceItem =
(com.tiedup.remake.items.base.IHasResistance) stack.getItem();
resistanceItem.setCurrentResistance(stack, resistComp.getBaseResistance());
}
// NPC speed reduction (players use MovementStyleManager, not this legacy path) // NPC speed reduction (players use MovementStyleManager, not this legacy path)
if (!(entity instanceof Player)) { if (!(entity instanceof Player)) {
Set<BodyRegionV2> regions = getOccupiedRegions(stack); Set<BodyRegionV2> regions = getOccupiedRegions(stack);

View File

@@ -139,7 +139,9 @@ public final class DataDrivenItemRegistry {
* Clear all definitions. Called on world unload or for testing. * Clear all definitions. Called on world unload or for testing.
*/ */
public static void clear() { public static void clear() {
SNAPSHOT = RegistrySnapshot.EMPTY; synchronized (RELOAD_LOCK) {
SNAPSHOT = RegistrySnapshot.EMPTY;
}
} }
// ===== COMPONENT HOLDERS ===== // ===== COMPONENT HOLDERS =====

View File

@@ -129,7 +129,6 @@ public class PacketV2LockToggle {
UUID keyUUID = heldKey.getKeyUUID(heldKeyStack); UUID keyUUID = heldKey.getKeyUUID(heldKeyStack);
lockable.setLockedByKeyUUID(stack, keyUUID); lockable.setLockedByKeyUUID(stack, keyUUID);
lockable.initializeLockResistance(stack);
TiedUpMod.LOGGER.debug( TiedUpMod.LOGGER.debug(
"[V2LockToggle] Locked region {} on entity {}", "[V2LockToggle] Locked region {} on entity {}",
@@ -142,12 +141,10 @@ public class PacketV2LockToggle {
if (hasMasterKey) { if (hasMasterKey) {
lockable.setLockedByKeyUUID(stack, null); lockable.setLockedByKeyUUID(stack, null);
lockable.clearLockResistance(stack);
} else if (heldKey != null) { } else if (heldKey != null) {
UUID keyUUID = heldKey.getKeyUUID(heldKeyStack); UUID keyUUID = heldKey.getKeyUUID(heldKeyStack);
if (!lockable.matchesKey(stack, keyUUID)) return; if (!lockable.matchesKey(stack, keyUUID)) return;
lockable.setLockedByKeyUUID(stack, null); lockable.setLockedByKeyUUID(stack, null);
lockable.clearLockResistance(stack);
} else { } else {
return; return;
} }

View File

@@ -84,7 +84,7 @@ public class PacketV2StruggleStart {
if (stack.getItem() instanceof ILockable lockable) { if (stack.getItem() instanceof ILockable lockable) {
isLocked = lockable.isLocked(stack); isLocked = lockable.isLocked(stack);
if (isLocked) { if (isLocked) {
resistance += lockable.getCurrentLockResistance(stack); resistance += com.tiedup.remake.core.SettingsAccessor.getPadlockResistance(null);
} }
} }