Clean repo for open source release
Remove build artifacts, dev tool configs, unused dependencies, and third-party source dumps. Add proper README, update .gitignore, clean up Makefile.
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
package com.tiedup.remake.network.minigame;
|
||||
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.minigame.ContinuousStruggleMiniGameState;
|
||||
import com.tiedup.remake.minigame.StruggleSessionManager;
|
||||
import com.tiedup.remake.network.PacketRateLimiter;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Supplier;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
|
||||
/**
|
||||
* Client to Server packet for continuous struggle key hold state.
|
||||
*
|
||||
* Sent every 5 ticks (4 times per second) while the struggle screen is open.
|
||||
* Reports which direction key the player is holding (or -1 if none).
|
||||
*/
|
||||
public class PacketContinuousStruggleHold {
|
||||
|
||||
private final UUID sessionId;
|
||||
private final int heldDirection; // -1 if not holding any direction key
|
||||
private final boolean isHolding;
|
||||
|
||||
public PacketContinuousStruggleHold(
|
||||
UUID sessionId,
|
||||
int heldDirection,
|
||||
boolean isHolding
|
||||
) {
|
||||
this.sessionId = sessionId;
|
||||
this.heldDirection = heldDirection;
|
||||
this.isHolding = isHolding;
|
||||
}
|
||||
|
||||
public void encode(FriendlyByteBuf buf) {
|
||||
buf.writeUUID(sessionId);
|
||||
buf.writeVarInt(heldDirection);
|
||||
buf.writeBoolean(isHolding);
|
||||
}
|
||||
|
||||
public static PacketContinuousStruggleHold decode(FriendlyByteBuf buf) {
|
||||
UUID sessionId = buf.readUUID();
|
||||
int heldDirection = buf.readVarInt();
|
||||
boolean isHolding = buf.readBoolean();
|
||||
return new PacketContinuousStruggleHold(
|
||||
sessionId,
|
||||
heldDirection,
|
||||
isHolding
|
||||
);
|
||||
}
|
||||
|
||||
public void handle(Supplier<NetworkEvent.Context> ctx) {
|
||||
ctx
|
||||
.get()
|
||||
.enqueueWork(() -> {
|
||||
ServerPlayer player = ctx.get().getSender();
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
handleServer(player);
|
||||
});
|
||||
ctx.get().setPacketHandled(true);
|
||||
}
|
||||
|
||||
private void handleServer(ServerPlayer player) {
|
||||
// HIGH FIX #18: Rate limiting to prevent packet spam exploit
|
||||
// Designed for 4/sec, allow up to 12/sec with buffer
|
||||
if (!PacketRateLimiter.allowPacket(player, "minigame")) {
|
||||
return;
|
||||
}
|
||||
|
||||
StruggleSessionManager manager = StruggleSessionManager.getInstance();
|
||||
ContinuousStruggleMiniGameState session =
|
||||
manager.getContinuousStruggleSession(player.getUUID());
|
||||
|
||||
if (session == null) {
|
||||
TiedUpMod.LOGGER.debug(
|
||||
"[PacketContinuousStruggleHold] No active session for {}",
|
||||
player.getName().getString()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate session ID
|
||||
if (!session.getSessionId().equals(sessionId)) {
|
||||
TiedUpMod.LOGGER.debug(
|
||||
"[PacketContinuousStruggleHold] Session ID mismatch for {}",
|
||||
player.getName().getString()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update held direction
|
||||
session.updateHeldDirection(heldDirection, isHolding);
|
||||
}
|
||||
|
||||
// Getters
|
||||
public UUID getSessionId() {
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
public int getHeldDirection() {
|
||||
return heldDirection;
|
||||
}
|
||||
|
||||
public boolean isHolding() {
|
||||
return isHolding;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
package com.tiedup.remake.network.minigame;
|
||||
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.minigame.ContinuousStruggleMiniGameState.UpdateType;
|
||||
import com.tiedup.remake.network.base.AbstractClientPacket;
|
||||
import java.util.UUID;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
|
||||
/**
|
||||
* Server to Client packet for continuous struggle mini-game state updates.
|
||||
*
|
||||
* Sent when:
|
||||
* - START: Session begins (opens the GUI)
|
||||
* - DIRECTION_CHANGE: Direction changed
|
||||
* - RESISTANCE_UPDATE: Resistance reduced
|
||||
* - SHOCK: Shock collar triggered
|
||||
* - ESCAPE: Player escaped successfully
|
||||
* - END: Session ended (cancelled or completed)
|
||||
*/
|
||||
public class PacketContinuousStruggleState extends AbstractClientPacket {
|
||||
|
||||
private final UUID sessionId;
|
||||
private final UpdateType updateType;
|
||||
private final int currentDirection;
|
||||
private final int currentResistance;
|
||||
private final int maxResistance;
|
||||
private final boolean isLocked;
|
||||
|
||||
public PacketContinuousStruggleState(
|
||||
UUID sessionId,
|
||||
UpdateType updateType,
|
||||
int currentDirection,
|
||||
int currentResistance,
|
||||
int maxResistance,
|
||||
boolean isLocked
|
||||
) {
|
||||
this.sessionId = sessionId;
|
||||
this.updateType = updateType;
|
||||
this.currentDirection = currentDirection;
|
||||
this.currentResistance = currentResistance;
|
||||
this.maxResistance = maxResistance;
|
||||
this.isLocked = isLocked;
|
||||
}
|
||||
|
||||
public void encode(FriendlyByteBuf buf) {
|
||||
buf.writeUUID(sessionId);
|
||||
buf.writeVarInt(updateType.ordinal());
|
||||
buf.writeVarInt(currentDirection);
|
||||
buf.writeVarInt(currentResistance);
|
||||
buf.writeVarInt(maxResistance);
|
||||
buf.writeBoolean(isLocked);
|
||||
}
|
||||
|
||||
public static PacketContinuousStruggleState decode(FriendlyByteBuf buf) {
|
||||
UUID sessionId = buf.readUUID();
|
||||
UpdateType updateType = UpdateType.values()[buf.readVarInt()];
|
||||
int currentDirection = buf.readVarInt();
|
||||
int currentResistance = buf.readVarInt();
|
||||
int maxResistance = buf.readVarInt();
|
||||
boolean isLocked = buf.readBoolean();
|
||||
return new PacketContinuousStruggleState(
|
||||
sessionId,
|
||||
updateType,
|
||||
currentDirection,
|
||||
currentResistance,
|
||||
maxResistance,
|
||||
isLocked
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
protected void handleClientImpl() {
|
||||
ClientHandler.handle(this);
|
||||
}
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
private static class ClientHandler {
|
||||
private static void handle(PacketContinuousStruggleState pkt) {
|
||||
net.minecraft.client.Minecraft mc = net.minecraft.client.Minecraft.getInstance();
|
||||
if (mc.player == null) {
|
||||
TiedUpMod.LOGGER.warn(
|
||||
"[PacketContinuousStruggleState] Player is null, cannot handle packet"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
TiedUpMod.LOGGER.info(
|
||||
"[PacketContinuousStruggleState] Received update: type={}, direction={}, resistance={}/{}",
|
||||
pkt.updateType,
|
||||
pkt.currentDirection,
|
||||
pkt.currentResistance,
|
||||
pkt.maxResistance
|
||||
);
|
||||
|
||||
switch (pkt.updateType) {
|
||||
case START -> {
|
||||
// Open the continuous struggle screen
|
||||
TiedUpMod.LOGGER.info(
|
||||
"[PacketContinuousStruggleState] Opening ContinuousStruggleMiniGameScreen"
|
||||
);
|
||||
mc.setScreen(
|
||||
new com.tiedup.remake.client.gui.screens.ContinuousStruggleMiniGameScreen(
|
||||
pkt.sessionId,
|
||||
pkt.currentDirection,
|
||||
pkt.currentResistance,
|
||||
pkt.maxResistance,
|
||||
pkt.isLocked
|
||||
)
|
||||
);
|
||||
}
|
||||
case DIRECTION_CHANGE -> {
|
||||
if (
|
||||
mc.screen instanceof com.tiedup.remake.client.gui.screens.ContinuousStruggleMiniGameScreen screen
|
||||
) {
|
||||
screen.onDirectionChange(pkt.currentDirection);
|
||||
}
|
||||
}
|
||||
case RESISTANCE_UPDATE -> {
|
||||
if (
|
||||
mc.screen instanceof com.tiedup.remake.client.gui.screens.ContinuousStruggleMiniGameScreen screen
|
||||
) {
|
||||
screen.onResistanceUpdate(pkt.currentResistance);
|
||||
}
|
||||
}
|
||||
case SHOCK -> {
|
||||
if (
|
||||
mc.screen instanceof com.tiedup.remake.client.gui.screens.ContinuousStruggleMiniGameScreen screen
|
||||
) {
|
||||
screen.onShock();
|
||||
}
|
||||
}
|
||||
case ESCAPE -> {
|
||||
if (
|
||||
mc.screen instanceof com.tiedup.remake.client.gui.screens.ContinuousStruggleMiniGameScreen screen
|
||||
) {
|
||||
screen.onEscape();
|
||||
}
|
||||
}
|
||||
case END -> {
|
||||
if (
|
||||
mc.screen instanceof com.tiedup.remake.client.gui.screens.ContinuousStruggleMiniGameScreen screen
|
||||
) {
|
||||
screen.onEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Getters for potential external use
|
||||
public UUID getSessionId() {
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
public UpdateType getUpdateType() {
|
||||
return updateType;
|
||||
}
|
||||
|
||||
public int getCurrentDirection() {
|
||||
return currentDirection;
|
||||
}
|
||||
|
||||
public int getCurrentResistance() {
|
||||
return currentResistance;
|
||||
}
|
||||
|
||||
public int getMaxResistance() {
|
||||
return maxResistance;
|
||||
}
|
||||
|
||||
public boolean isLocked() {
|
||||
return isLocked;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.tiedup.remake.network.minigame;
|
||||
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.minigame.ContinuousStruggleMiniGameState;
|
||||
import com.tiedup.remake.minigame.StruggleSessionManager;
|
||||
import com.tiedup.remake.network.PacketRateLimiter;
|
||||
import com.tiedup.remake.network.sync.SyncManager;
|
||||
import com.tiedup.remake.state.PlayerBindState;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Supplier;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
|
||||
/**
|
||||
* Client to Server packet to stop the continuous struggle session.
|
||||
*
|
||||
* Sent when:
|
||||
* - Player presses ESC to close the screen
|
||||
* - Player takes damage (detected client-side)
|
||||
*/
|
||||
public class PacketContinuousStruggleStop {
|
||||
|
||||
private final UUID sessionId;
|
||||
|
||||
public PacketContinuousStruggleStop(UUID sessionId) {
|
||||
this.sessionId = sessionId;
|
||||
}
|
||||
|
||||
public void encode(FriendlyByteBuf buf) {
|
||||
buf.writeUUID(sessionId);
|
||||
}
|
||||
|
||||
public static PacketContinuousStruggleStop decode(FriendlyByteBuf buf) {
|
||||
UUID sessionId = buf.readUUID();
|
||||
return new PacketContinuousStruggleStop(sessionId);
|
||||
}
|
||||
|
||||
public void handle(Supplier<NetworkEvent.Context> ctx) {
|
||||
ctx
|
||||
.get()
|
||||
.enqueueWork(() -> {
|
||||
ServerPlayer player = ctx.get().getSender();
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
handleServer(player);
|
||||
});
|
||||
ctx.get().setPacketHandled(true);
|
||||
}
|
||||
|
||||
private void handleServer(ServerPlayer player) {
|
||||
if (!PacketRateLimiter.allowPacket(player, "minigame")) return;
|
||||
|
||||
StruggleSessionManager manager = StruggleSessionManager.getInstance();
|
||||
ContinuousStruggleMiniGameState session =
|
||||
manager.getContinuousStruggleSession(player.getUUID());
|
||||
|
||||
if (session == null) {
|
||||
TiedUpMod.LOGGER.debug(
|
||||
"[PacketContinuousStruggleStop] No active session for {}",
|
||||
player.getName().getString()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate session ID
|
||||
if (!session.getSessionId().equals(sessionId)) {
|
||||
TiedUpMod.LOGGER.debug(
|
||||
"[PacketContinuousStruggleStop] Session ID mismatch for {}",
|
||||
player.getName().getString()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
TiedUpMod.LOGGER.info(
|
||||
"[PacketContinuousStruggleStop] Player {} stopped struggle session",
|
||||
player.getName().getString()
|
||||
);
|
||||
|
||||
// Cancel the session
|
||||
session.cancel();
|
||||
|
||||
// Clear struggle animation state and sync
|
||||
PlayerBindState state = PlayerBindState.getInstance(player);
|
||||
if (state != null) {
|
||||
state.setStruggling(false, 0);
|
||||
SyncManager.syncStruggleState(player);
|
||||
}
|
||||
|
||||
// End the session in the manager
|
||||
manager.endContinuousStruggleSession(player.getUUID(), false);
|
||||
}
|
||||
|
||||
// Getter
|
||||
public UUID getSessionId() {
|
||||
return sessionId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,447 @@
|
||||
package com.tiedup.remake.network.minigame;
|
||||
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.items.ItemLockpick;
|
||||
import com.tiedup.remake.items.base.ILockable;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
import com.tiedup.remake.v2.bondage.capability.V2EquipmentHelper;
|
||||
import com.tiedup.remake.v2.furniture.EntityFurniture;
|
||||
import com.tiedup.remake.v2.furniture.FurnitureDefinition;
|
||||
import com.tiedup.remake.v2.furniture.network.PacketSyncFurnitureState;
|
||||
import com.tiedup.remake.minigame.LockpickMiniGameState;
|
||||
import com.tiedup.remake.minigame.LockpickMiniGameState.PickAttemptResult;
|
||||
import com.tiedup.remake.minigame.LockpickSessionManager;
|
||||
import com.tiedup.remake.network.ModNetwork;
|
||||
import com.tiedup.remake.network.PacketRateLimiter;
|
||||
import com.tiedup.remake.network.sync.SyncManager;
|
||||
import com.tiedup.remake.state.PlayerBindState;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Supplier;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
|
||||
/**
|
||||
* Packet for lockpick attempt at current position (Client to Server).
|
||||
*
|
||||
* Sent when player presses SPACE to attempt picking at current position.
|
||||
*/
|
||||
public class PacketLockpickAttempt {
|
||||
|
||||
private final UUID sessionId;
|
||||
|
||||
public PacketLockpickAttempt(UUID sessionId) {
|
||||
this.sessionId = sessionId;
|
||||
}
|
||||
|
||||
public void encode(FriendlyByteBuf buf) {
|
||||
buf.writeUUID(sessionId);
|
||||
}
|
||||
|
||||
public static PacketLockpickAttempt decode(FriendlyByteBuf buf) {
|
||||
UUID sessionId = buf.readUUID();
|
||||
return new PacketLockpickAttempt(sessionId);
|
||||
}
|
||||
|
||||
public void handle(Supplier<NetworkEvent.Context> ctx) {
|
||||
ctx
|
||||
.get()
|
||||
.enqueueWork(() -> {
|
||||
ServerPlayer player = ctx.get().getSender();
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
handleServer(player);
|
||||
});
|
||||
ctx.get().setPacketHandled(true);
|
||||
}
|
||||
|
||||
private void handleServer(ServerPlayer player) {
|
||||
// Rate limiting to prevent lockpick attempt spam exploit
|
||||
if (!PacketRateLimiter.allowPacket(player, "minigame")) {
|
||||
return;
|
||||
}
|
||||
|
||||
LockpickSessionManager manager = LockpickSessionManager.getInstance();
|
||||
|
||||
// Validate session
|
||||
if (!manager.validateLockpickSession(player.getUUID(), sessionId)) {
|
||||
TiedUpMod.LOGGER.warn(
|
||||
"[PacketLockpickAttempt] Invalid session {} for player {}",
|
||||
sessionId.toString().substring(0, 8),
|
||||
player.getName().getString()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
LockpickMiniGameState session = manager.getLockpickSession(
|
||||
player.getUUID()
|
||||
);
|
||||
if (session == null || session.isComplete()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Play sound and notify guards
|
||||
manager.onLockpickAttempt(player);
|
||||
|
||||
// Attempt to pick
|
||||
PickAttemptResult result = session.attemptPick();
|
||||
|
||||
TiedUpMod.LOGGER.debug(
|
||||
"[PacketLockpickAttempt] Player {} attempted pick, result: {}",
|
||||
player.getName().getString(),
|
||||
result
|
||||
);
|
||||
|
||||
// Handle result
|
||||
switch (result) {
|
||||
case SUCCESS -> handleSuccess(player, session);
|
||||
case OUT_OF_PICKS -> handleOutOfPicks(player, session);
|
||||
case MISSED -> handleMissed(player, session);
|
||||
default -> {
|
||||
// Session already complete
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSuccess(
|
||||
ServerPlayer player,
|
||||
LockpickMiniGameState session
|
||||
) {
|
||||
// Check for furniture lockpick context FIRST — if present, this is a
|
||||
// furniture seat lockpick, not a body item lockpick. The context tag is
|
||||
// written by PacketFurnitureEscape.handleLockpick() when starting the session.
|
||||
CompoundTag furnitureCtx = player.getPersistentData()
|
||||
.getCompound("tiedup_furniture_lockpick_ctx");
|
||||
if (furnitureCtx != null && furnitureCtx.contains("furniture_id")) {
|
||||
// H18: Distance check BEFORE ending session — prevents consuming session
|
||||
// without reward if player moved away (reviewer H18 RISK-001)
|
||||
int furnitureId = furnitureCtx.getInt("furniture_id");
|
||||
Entity furnitureEntity = player.level().getEntity(furnitureId);
|
||||
if (furnitureEntity == null || player.distanceTo(furnitureEntity) > 10.0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Session validated — now end it
|
||||
LockpickSessionManager.getInstance().endLockpickSession(
|
||||
player.getUUID(),
|
||||
true
|
||||
);
|
||||
handleFurnitureLockpickSuccess(player, furnitureCtx);
|
||||
player.getPersistentData().remove("tiedup_furniture_lockpick_ctx");
|
||||
damageLockpick(player);
|
||||
|
||||
// Send result to client
|
||||
ModNetwork.sendToPlayer(
|
||||
new PacketLockpickMiniGameResult(
|
||||
session.getSessionId(),
|
||||
PacketLockpickMiniGameResult.ResultType.SUCCESS,
|
||||
0
|
||||
),
|
||||
player
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Body item lockpick — self-targeting, no distance check needed
|
||||
LockpickSessionManager.getInstance().endLockpickSession(
|
||||
player.getUUID(),
|
||||
true
|
||||
);
|
||||
|
||||
// Body item lockpick path: targetSlot stores BodyRegionV2 ordinal
|
||||
BodyRegionV2 targetRegion =
|
||||
BodyRegionV2.values()[session.getTargetSlot()];
|
||||
ItemStack targetStack = V2EquipmentHelper.getInRegion(
|
||||
player,
|
||||
targetRegion
|
||||
);
|
||||
|
||||
if (
|
||||
!targetStack.isEmpty() &&
|
||||
targetStack.getItem() instanceof ILockable lockable
|
||||
) {
|
||||
// Get lock resistance BEFORE clearing it
|
||||
int lockResistance = lockable.getCurrentLockResistance(targetStack);
|
||||
|
||||
// Unlock the item
|
||||
lockable.setLockedByKeyUUID(targetStack, null);
|
||||
lockable.clearLockResistance(targetStack);
|
||||
|
||||
TiedUpMod.LOGGER.info(
|
||||
"[PacketLockpickAttempt] Player {} successfully picked lock on {} ({}, resistance was {})",
|
||||
player.getName().getString(),
|
||||
targetStack.getDisplayName().getString(),
|
||||
targetRegion,
|
||||
lockResistance
|
||||
);
|
||||
|
||||
// Deduct lock resistance from bind resistance
|
||||
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
|
||||
damageLockpick(player);
|
||||
|
||||
// Sync to all players so unlock is visible immediately
|
||||
SyncManager.syncInventory(player);
|
||||
|
||||
// Send result to client
|
||||
ModNetwork.sendToPlayer(
|
||||
new PacketLockpickMiniGameResult(
|
||||
session.getSessionId(),
|
||||
PacketLockpickMiniGameResult.ResultType.SUCCESS,
|
||||
0
|
||||
),
|
||||
player
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a successful furniture seat lockpick: unlock the seat, dismount
|
||||
* the passenger, play the unlock sound, and broadcast the updated state.
|
||||
*/
|
||||
private void handleFurnitureLockpickSuccess(
|
||||
ServerPlayer player,
|
||||
CompoundTag ctx
|
||||
) {
|
||||
int furnitureEntityId = ctx.getInt("furniture_id");
|
||||
String seatId = ctx.getString("seat_id");
|
||||
|
||||
Entity entity = player.level().getEntity(furnitureEntityId);
|
||||
if (!(entity instanceof EntityFurniture furniture)) {
|
||||
TiedUpMod.LOGGER.warn(
|
||||
"[PacketLockpickAttempt] Furniture entity {} not found or wrong type for lockpick success",
|
||||
furnitureEntityId
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Unlock the seat
|
||||
furniture.setSeatLocked(seatId, false);
|
||||
|
||||
// Dismount the passenger in that seat
|
||||
Entity passenger = furniture.findPassengerInSeat(seatId);
|
||||
if (passenger != null) {
|
||||
// Clear reconnection tag before dismount
|
||||
if (passenger instanceof ServerPlayer passengerPlayer) {
|
||||
passengerPlayer.getPersistentData().remove("tiedup_locked_furniture");
|
||||
}
|
||||
passenger.stopRiding();
|
||||
}
|
||||
|
||||
// Play unlock sound from the furniture definition
|
||||
FurnitureDefinition def = furniture.getDefinition();
|
||||
if (def != null && def.feedback().unlockSound() != null) {
|
||||
player.level().playSound(
|
||||
null,
|
||||
entity.getX(), entity.getY(), entity.getZ(),
|
||||
SoundEvent.createVariableRangeEvent(def.feedback().unlockSound()),
|
||||
SoundSource.BLOCKS, 1.0f, 1.0f
|
||||
);
|
||||
}
|
||||
|
||||
// Broadcast updated lock/anim state to all tracking clients
|
||||
PacketSyncFurnitureState.sendToTracking(furniture);
|
||||
|
||||
TiedUpMod.LOGGER.info(
|
||||
"[PacketLockpickAttempt] Player {} picked furniture lock on entity {} seat '{}'",
|
||||
player.getName().getString(), furnitureEntityId, seatId
|
||||
);
|
||||
}
|
||||
|
||||
private void handleOutOfPicks(
|
||||
ServerPlayer player,
|
||||
LockpickMiniGameState session
|
||||
) {
|
||||
LockpickSessionManager.getInstance().endLockpickSession(
|
||||
player.getUUID(),
|
||||
false
|
||||
);
|
||||
|
||||
// Destroy the lockpick
|
||||
ItemStack lockpickStack = ItemLockpick.findLockpickInInventory(player);
|
||||
if (!lockpickStack.isEmpty()) {
|
||||
lockpickStack.shrink(1);
|
||||
}
|
||||
|
||||
TiedUpMod.LOGGER.info(
|
||||
"[PacketLockpickAttempt] Player {} ran out of lockpicks",
|
||||
player.getName().getString()
|
||||
);
|
||||
|
||||
// Trigger shock if wearing shock collar
|
||||
triggerShockIfCollar(player);
|
||||
|
||||
// Send result to client
|
||||
ModNetwork.sendToPlayer(
|
||||
new PacketLockpickMiniGameResult(
|
||||
session.getSessionId(),
|
||||
PacketLockpickMiniGameResult.ResultType.OUT_OF_PICKS,
|
||||
0
|
||||
),
|
||||
player
|
||||
);
|
||||
}
|
||||
|
||||
private void handleMissed(
|
||||
ServerPlayer player,
|
||||
LockpickMiniGameState session
|
||||
) {
|
||||
// Calculate distance BEFORE damaging (for animation feedback)
|
||||
float distance = session.getDistanceToSweetSpot();
|
||||
|
||||
// Damage lockpick
|
||||
damageLockpick(player);
|
||||
|
||||
// Update remaining uses from actual lockpick
|
||||
int remainingUses = 0;
|
||||
ItemStack lockpickStack = ItemLockpick.findLockpickInInventory(player);
|
||||
if (!lockpickStack.isEmpty()) {
|
||||
remainingUses =
|
||||
lockpickStack.getMaxDamage() - lockpickStack.getDamageValue();
|
||||
}
|
||||
session.setRemainingUses(remainingUses);
|
||||
|
||||
// Check for JAM (5% chance on miss) — only applies to body item lockpick sessions.
|
||||
// Furniture seat locks do not have a jam mechanic (there is no ILockable item to jam).
|
||||
boolean jammed = false;
|
||||
boolean isFurnitureSession = player.getPersistentData()
|
||||
.getCompound("tiedup_furniture_lockpick_ctx")
|
||||
.contains("furniture_id");
|
||||
|
||||
if (!isFurnitureSession && player.getRandom().nextFloat() < 0.05f) {
|
||||
int targetSlot = session.getTargetSlot();
|
||||
if (targetSlot >= 0 && targetSlot < BodyRegionV2.values().length) {
|
||||
BodyRegionV2 targetRegion = BodyRegionV2.values()[targetSlot];
|
||||
ItemStack targetStack = V2EquipmentHelper.getInRegion(
|
||||
player,
|
||||
targetRegion
|
||||
);
|
||||
|
||||
if (
|
||||
!targetStack.isEmpty() &&
|
||||
targetStack.getItem() instanceof ILockable lockable
|
||||
) {
|
||||
lockable.setJammed(targetStack, true);
|
||||
jammed = true;
|
||||
|
||||
player.sendSystemMessage(
|
||||
Component.literal(
|
||||
"The lock jammed! Only struggle can open it now."
|
||||
).withStyle(ChatFormatting.RED)
|
||||
);
|
||||
|
||||
TiedUpMod.LOGGER.info(
|
||||
"[PacketLockpickAttempt] Player {} jammed the lock on {} ({})",
|
||||
player.getName().getString(),
|
||||
targetStack.getDisplayName().getString(),
|
||||
targetRegion
|
||||
);
|
||||
|
||||
// End session since lock is jammed
|
||||
LockpickSessionManager.getInstance().endLockpickSession(
|
||||
player.getUUID(),
|
||||
false
|
||||
);
|
||||
|
||||
// Sync jam state to all players
|
||||
SyncManager.syncInventory(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger shock if wearing shock collar
|
||||
triggerShockIfCollar(player);
|
||||
|
||||
// Send result to client with distance for animation
|
||||
if (jammed) {
|
||||
// Send cancelled result since lock is jammed
|
||||
ModNetwork.sendToPlayer(
|
||||
new PacketLockpickMiniGameResult(
|
||||
session.getSessionId(),
|
||||
PacketLockpickMiniGameResult.ResultType.CANCELLED,
|
||||
0,
|
||||
distance
|
||||
),
|
||||
player
|
||||
);
|
||||
} else {
|
||||
ModNetwork.sendToPlayer(
|
||||
new PacketLockpickMiniGameResult(
|
||||
session.getSessionId(),
|
||||
PacketLockpickMiniGameResult.ResultType.MISSED,
|
||||
remainingUses,
|
||||
distance
|
||||
),
|
||||
player
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Damage the player's lockpick by 1 durability. If durability is exhausted,
|
||||
* the lockpick item is consumed (shrunk). Shared by handleSuccess, handleMissed,
|
||||
* and handleFurnitureLockpickSuccess paths.
|
||||
*/
|
||||
private void damageLockpick(ServerPlayer player) {
|
||||
ItemStack lockpickStack = ItemLockpick.findLockpickInInventory(player);
|
||||
if (!lockpickStack.isEmpty()) {
|
||||
lockpickStack.setDamageValue(lockpickStack.getDamageValue() + 1);
|
||||
if (lockpickStack.getDamageValue() >= lockpickStack.getMaxDamage()) {
|
||||
lockpickStack.shrink(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void triggerShockIfCollar(ServerPlayer player) {
|
||||
PlayerBindState state = PlayerBindState.getInstance(player);
|
||||
if (state == null) return;
|
||||
|
||||
ItemStack collar = V2EquipmentHelper.getInRegion(
|
||||
player, BodyRegionV2.NECK
|
||||
);
|
||||
if (collar.isEmpty()) return;
|
||||
|
||||
if (
|
||||
collar.getItem() instanceof com.tiedup.remake.items.ItemShockCollar
|
||||
) {
|
||||
state.shockKidnapped(" (Failed lockpick attempt)", 2.0f);
|
||||
TiedUpMod.LOGGER.info(
|
||||
"[PacketLockpickAttempt] Player {} shocked for failed lockpick",
|
||||
player.getName().getString()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.tiedup.remake.network.minigame;
|
||||
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.minigame.LockpickMiniGameState;
|
||||
import com.tiedup.remake.minigame.LockpickSessionManager;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Supplier;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
|
||||
/**
|
||||
* Phase 2: Packet for player position update during lockpick mini-game (Client to Server).
|
||||
*
|
||||
* Contains:
|
||||
* - Session UUID
|
||||
* - New position (0.0 to 1.0)
|
||||
*/
|
||||
public class PacketLockpickMiniGameMove {
|
||||
|
||||
private final UUID sessionId;
|
||||
private final float newPosition;
|
||||
|
||||
public PacketLockpickMiniGameMove(UUID sessionId, float newPosition) {
|
||||
this.sessionId = sessionId;
|
||||
this.newPosition = newPosition;
|
||||
}
|
||||
|
||||
public void encode(FriendlyByteBuf buf) {
|
||||
buf.writeUUID(sessionId);
|
||||
buf.writeFloat(newPosition);
|
||||
}
|
||||
|
||||
public static PacketLockpickMiniGameMove decode(FriendlyByteBuf buf) {
|
||||
UUID sessionId = buf.readUUID();
|
||||
float newPosition = buf.readFloat();
|
||||
return new PacketLockpickMiniGameMove(sessionId, newPosition);
|
||||
}
|
||||
|
||||
public void handle(Supplier<NetworkEvent.Context> ctx) {
|
||||
ctx
|
||||
.get()
|
||||
.enqueueWork(() -> {
|
||||
ServerPlayer player = ctx.get().getSender();
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Rate limiting: Prevent lockpick spam
|
||||
if (
|
||||
!com.tiedup.remake.network.PacketRateLimiter.allowPacket(
|
||||
player,
|
||||
"minigame"
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
handleServer(player);
|
||||
});
|
||||
ctx.get().setPacketHandled(true);
|
||||
}
|
||||
|
||||
private void handleServer(ServerPlayer player) {
|
||||
LockpickSessionManager manager = LockpickSessionManager.getInstance();
|
||||
|
||||
// Validate session
|
||||
if (!manager.validateLockpickSession(player.getUUID(), sessionId)) {
|
||||
TiedUpMod.LOGGER.warn(
|
||||
"[PacketLockpickMiniGameMove] Invalid session {} for player {}",
|
||||
sessionId.toString().substring(0, 8),
|
||||
player.getName().getString()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
LockpickMiniGameState session = manager.getLockpickSession(
|
||||
player.getUUID()
|
||||
);
|
||||
if (session == null || session.isComplete()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update position (server-side validation: clamp to 0-1)
|
||||
session.setCurrentPosition(Math.max(0.0f, Math.min(1.0f, newPosition)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
package com.tiedup.remake.network.minigame;
|
||||
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.network.base.AbstractClientPacket;
|
||||
import java.util.UUID;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
|
||||
/**
|
||||
* Phase 2: Packet to send lockpick result to client (Server to Client).
|
||||
*
|
||||
* Contains:
|
||||
* - Session UUID
|
||||
* - Result type (SUCCESS, MISSED, OUT_OF_PICKS)
|
||||
* - Remaining uses
|
||||
*/
|
||||
public class PacketLockpickMiniGameResult extends AbstractClientPacket {
|
||||
|
||||
public enum ResultType {
|
||||
/**
|
||||
* Successfully picked the lock
|
||||
*/
|
||||
SUCCESS(0),
|
||||
|
||||
/**
|
||||
* Missed the sweet spot
|
||||
*/
|
||||
MISSED(1),
|
||||
|
||||
/**
|
||||
* Ran out of lockpick uses
|
||||
*/
|
||||
OUT_OF_PICKS(2),
|
||||
|
||||
/**
|
||||
* Session cancelled
|
||||
*/
|
||||
CANCELLED(3);
|
||||
|
||||
private final int id;
|
||||
|
||||
ResultType(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public static ResultType fromId(int id) {
|
||||
return switch (id) {
|
||||
case 0 -> SUCCESS;
|
||||
case 1 -> MISSED;
|
||||
case 2 -> OUT_OF_PICKS;
|
||||
case 3 -> CANCELLED;
|
||||
default -> MISSED;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private final UUID sessionId;
|
||||
private final ResultType resultType;
|
||||
private final int remainingUses;
|
||||
private final float distance; // Distance to sweet spot (0.0-1.0), used for MISSED animation
|
||||
|
||||
public PacketLockpickMiniGameResult(
|
||||
UUID sessionId,
|
||||
ResultType resultType,
|
||||
int remainingUses
|
||||
) {
|
||||
this(sessionId, resultType, remainingUses, 0f);
|
||||
}
|
||||
|
||||
public PacketLockpickMiniGameResult(
|
||||
UUID sessionId,
|
||||
ResultType resultType,
|
||||
int remainingUses,
|
||||
float distance
|
||||
) {
|
||||
this.sessionId = sessionId;
|
||||
this.resultType = resultType;
|
||||
this.remainingUses = remainingUses;
|
||||
this.distance = distance;
|
||||
}
|
||||
|
||||
public void encode(FriendlyByteBuf buf) {
|
||||
buf.writeUUID(sessionId);
|
||||
buf.writeVarInt(resultType.getId());
|
||||
buf.writeVarInt(remainingUses);
|
||||
buf.writeFloat(distance);
|
||||
}
|
||||
|
||||
public static PacketLockpickMiniGameResult decode(FriendlyByteBuf buf) {
|
||||
UUID sessionId = buf.readUUID();
|
||||
ResultType resultType = ResultType.fromId(buf.readVarInt());
|
||||
int remainingUses = buf.readVarInt();
|
||||
float distance = buf.readFloat();
|
||||
return new PacketLockpickMiniGameResult(
|
||||
sessionId,
|
||||
resultType,
|
||||
remainingUses,
|
||||
distance
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
protected void handleClientImpl() {
|
||||
ClientHandler.handle(this);
|
||||
}
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
private static class ClientHandler {
|
||||
private static void handle(PacketLockpickMiniGameResult pkt) {
|
||||
net.minecraft.client.Minecraft mc = net.minecraft.client.Minecraft.getInstance();
|
||||
if (mc.player == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
TiedUpMod.LOGGER.debug(
|
||||
"[PacketLockpickMiniGameResult] Received result: type={}, uses={}",
|
||||
pkt.resultType,
|
||||
pkt.remainingUses
|
||||
);
|
||||
|
||||
if (mc.screen instanceof com.tiedup.remake.client.gui.screens.LockpickMiniGameScreen screen) {
|
||||
switch (pkt.resultType) {
|
||||
case SUCCESS -> screen.onSuccess();
|
||||
case MISSED -> screen.onMissed(pkt.remainingUses, pkt.distance);
|
||||
case OUT_OF_PICKS -> screen.onOutOfPicks();
|
||||
case CANCELLED -> screen.onCancelled();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Getters
|
||||
public UUID getSessionId() {
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
public ResultType getResultType() {
|
||||
return resultType;
|
||||
}
|
||||
|
||||
public int getRemainingUses() {
|
||||
return remainingUses;
|
||||
}
|
||||
|
||||
public float getDistance() {
|
||||
return distance;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
package com.tiedup.remake.network.minigame;
|
||||
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.items.ItemLockpick;
|
||||
import com.tiedup.remake.items.base.ILockable;
|
||||
import com.tiedup.remake.minigame.LockpickMiniGameState;
|
||||
import com.tiedup.remake.minigame.LockpickSessionManager;
|
||||
import com.tiedup.remake.network.ModNetwork;
|
||||
import com.tiedup.remake.network.PacketRateLimiter;
|
||||
import com.tiedup.remake.state.PlayerBindState;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
import com.tiedup.remake.v2.bondage.capability.V2EquipmentHelper;
|
||||
import java.util.function.Supplier;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
|
||||
/**
|
||||
* Phase 2: Packet to start a Lockpick mini-game session (Client to Server).
|
||||
*
|
||||
* Sent when player clicks "Lockpick" on a locked item.
|
||||
*/
|
||||
public class PacketLockpickMiniGameStart {
|
||||
|
||||
private final BodyRegionV2 targetRegion;
|
||||
|
||||
public PacketLockpickMiniGameStart(BodyRegionV2 targetRegion) {
|
||||
this.targetRegion = targetRegion;
|
||||
}
|
||||
|
||||
public void encode(FriendlyByteBuf buf) {
|
||||
buf.writeEnum(targetRegion);
|
||||
}
|
||||
|
||||
public static PacketLockpickMiniGameStart decode(FriendlyByteBuf buf) {
|
||||
return new PacketLockpickMiniGameStart(buf.readEnum(BodyRegionV2.class));
|
||||
}
|
||||
|
||||
public void handle(Supplier<NetworkEvent.Context> ctx) {
|
||||
ctx
|
||||
.get()
|
||||
.enqueueWork(() -> {
|
||||
ServerPlayer player = ctx.get().getSender();
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
handleServer(player);
|
||||
});
|
||||
ctx.get().setPacketHandled(true);
|
||||
}
|
||||
|
||||
private void handleServer(ServerPlayer player) {
|
||||
if (!PacketRateLimiter.allowPacket(player, "minigame")) return;
|
||||
|
||||
PlayerBindState state = PlayerBindState.getInstance(player);
|
||||
if (state == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for mittens
|
||||
if (state.hasMittens()) {
|
||||
TiedUpMod.LOGGER.debug(
|
||||
"[PacketLockpickMiniGameStart] Player {} has mittens, cannot lockpick",
|
||||
player.getName().getString()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Find lockpick in inventory
|
||||
ItemStack lockpickStack = ItemLockpick.findLockpickInInventory(player);
|
||||
if (lockpickStack.isEmpty()) {
|
||||
TiedUpMod.LOGGER.debug(
|
||||
"[PacketLockpickMiniGameStart] Player {} has no lockpick",
|
||||
player.getName().getString()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get target item via V2 equipment system
|
||||
ItemStack targetStack = V2EquipmentHelper.getInRegion(
|
||||
player,
|
||||
targetRegion
|
||||
);
|
||||
|
||||
if (
|
||||
targetStack.isEmpty() ||
|
||||
!(targetStack.getItem() instanceof ILockable lockable)
|
||||
) {
|
||||
TiedUpMod.LOGGER.debug(
|
||||
"[PacketLockpickMiniGameStart] Target region {} is not lockable",
|
||||
targetRegion
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!lockable.isLocked(targetStack)) {
|
||||
TiedUpMod.LOGGER.debug(
|
||||
"[PacketLockpickMiniGameStart] Target region {} is not locked",
|
||||
targetRegion
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (lockable.isJammed(targetStack)) {
|
||||
TiedUpMod.LOGGER.debug(
|
||||
"[PacketLockpickMiniGameStart] Target region {} is jammed",
|
||||
targetRegion
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine sweet spot width based on lockpick type
|
||||
float sweetSpotWidth = 0.03f; // 3% - Very difficult sweet spot (Skyrim-style)
|
||||
|
||||
// Get remaining uses
|
||||
int remainingUses =
|
||||
lockpickStack.getMaxDamage() - lockpickStack.getDamageValue();
|
||||
|
||||
// Start session
|
||||
LockpickSessionManager manager = LockpickSessionManager.getInstance();
|
||||
LockpickMiniGameState session = manager.startLockpickSession(
|
||||
player,
|
||||
targetRegion.ordinal(),
|
||||
sweetSpotWidth
|
||||
);
|
||||
|
||||
if (session == null) {
|
||||
TiedUpMod.LOGGER.warn(
|
||||
"[PacketLockpickMiniGameStart] Failed to create lockpick session for {}",
|
||||
player.getName().getString()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
session.setRemainingUses(remainingUses);
|
||||
|
||||
TiedUpMod.LOGGER.info(
|
||||
"[PacketLockpickMiniGameStart] Started lockpick session for {} (region: {}, uses: {})",
|
||||
player.getName().getString(),
|
||||
targetRegion,
|
||||
remainingUses
|
||||
);
|
||||
|
||||
// Send initial state to client
|
||||
ModNetwork.sendToPlayer(
|
||||
new PacketLockpickMiniGameState(
|
||||
session.getSessionId(),
|
||||
session.getSweetSpotCenter(),
|
||||
session.getSweetSpotWidth(),
|
||||
session.getCurrentPosition(),
|
||||
session.getRemainingUses()
|
||||
),
|
||||
player
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package com.tiedup.remake.network.minigame;
|
||||
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.network.base.AbstractClientPacket;
|
||||
import java.util.UUID;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
|
||||
/**
|
||||
* Phase 2: Packet to send lockpick initial state to client (Server to Client).
|
||||
*
|
||||
* Contains:
|
||||
* - Session UUID
|
||||
* - Sweet spot center and width (server-authoritative)
|
||||
* - Current position
|
||||
* - Remaining lockpick uses
|
||||
*/
|
||||
public class PacketLockpickMiniGameState extends AbstractClientPacket {
|
||||
|
||||
private final UUID sessionId;
|
||||
private final float sweetSpotCenter;
|
||||
private final float sweetSpotWidth;
|
||||
private final float currentPosition;
|
||||
private final int remainingUses;
|
||||
|
||||
public PacketLockpickMiniGameState(
|
||||
UUID sessionId,
|
||||
float sweetSpotCenter,
|
||||
float sweetSpotWidth,
|
||||
float currentPosition,
|
||||
int remainingUses
|
||||
) {
|
||||
this.sessionId = sessionId;
|
||||
this.sweetSpotCenter = sweetSpotCenter;
|
||||
this.sweetSpotWidth = sweetSpotWidth;
|
||||
this.currentPosition = currentPosition;
|
||||
this.remainingUses = remainingUses;
|
||||
}
|
||||
|
||||
public void encode(FriendlyByteBuf buf) {
|
||||
buf.writeUUID(sessionId);
|
||||
buf.writeFloat(sweetSpotCenter);
|
||||
buf.writeFloat(sweetSpotWidth);
|
||||
buf.writeFloat(currentPosition);
|
||||
buf.writeVarInt(remainingUses);
|
||||
}
|
||||
|
||||
public static PacketLockpickMiniGameState decode(FriendlyByteBuf buf) {
|
||||
UUID sessionId = buf.readUUID();
|
||||
float sweetSpotCenter = buf.readFloat();
|
||||
float sweetSpotWidth = buf.readFloat();
|
||||
float currentPosition = buf.readFloat();
|
||||
int remainingUses = buf.readVarInt();
|
||||
return new PacketLockpickMiniGameState(
|
||||
sessionId,
|
||||
sweetSpotCenter,
|
||||
sweetSpotWidth,
|
||||
currentPosition,
|
||||
remainingUses
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
protected void handleClientImpl() {
|
||||
ClientHandler.handle(this);
|
||||
}
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
private static class ClientHandler {
|
||||
private static void handle(PacketLockpickMiniGameState pkt) {
|
||||
net.minecraft.client.Minecraft mc = net.minecraft.client.Minecraft.getInstance();
|
||||
if (mc.player == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
TiedUpMod.LOGGER.debug(
|
||||
"[PacketLockpickMiniGameState] Received state: sweet={}(w={}), pos={}, uses={}",
|
||||
pkt.sweetSpotCenter,
|
||||
pkt.sweetSpotWidth,
|
||||
pkt.currentPosition,
|
||||
pkt.remainingUses
|
||||
);
|
||||
|
||||
// Open the lockpick mini-game screen
|
||||
mc.setScreen(
|
||||
new com.tiedup.remake.client.gui.screens.LockpickMiniGameScreen(
|
||||
pkt.sessionId,
|
||||
pkt.sweetSpotCenter,
|
||||
pkt.sweetSpotWidth,
|
||||
pkt.currentPosition,
|
||||
pkt.remainingUses
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Getters
|
||||
public UUID getSessionId() {
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
public float getSweetSpotCenter() {
|
||||
return sweetSpotCenter;
|
||||
}
|
||||
|
||||
public float getSweetSpotWidth() {
|
||||
return sweetSpotWidth;
|
||||
}
|
||||
|
||||
public float getCurrentPosition() {
|
||||
return currentPosition;
|
||||
}
|
||||
|
||||
public int getRemainingUses() {
|
||||
return remainingUses;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user