Remove internal phase comments and format code

Strip all Phase references, TODO/FUTURE roadmap notes, and internal
planning comments from the codebase. Run Prettier for consistent
formatting across all Java files.
This commit is contained in:
NotEvil
2026-04-12 01:24:49 +02:00
parent 73d70e212d
commit a71093ba9c
482 changed files with 8500 additions and 5155 deletions

View File

@@ -19,11 +19,13 @@ import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.Entity;
@@ -33,8 +35,6 @@ import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Pose;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.entity.IEntityAdditionalSpawnData;
@@ -59,25 +59,36 @@ import org.jetbrains.annotations.Nullable;
* Forge to include the {@code IEntityAdditionalSpawnData} buffer in the spawn
* packet. Without this override, the entity would be invisible on clients.</p>
*/
public class EntityFurniture extends Entity
implements ISeatProvider, IEntityAdditionalSpawnData {
public class EntityFurniture
extends Entity
implements ISeatProvider, IEntityAdditionalSpawnData
{
// ========== SynchedEntityData Accessors ==========
/** The furniture definition ID (ResourceLocation string form). */
private static final EntityDataAccessor<String> FURNITURE_ID =
SynchedEntityData.defineId(EntityFurniture.class, EntityDataSerializers.STRING);
SynchedEntityData.defineId(
EntityFurniture.class,
EntityDataSerializers.STRING
);
/**
* Bitmask of locked seats. Bit N corresponds to seat index N
* in {@link FurnitureDefinition#seats()}. Max 8 seats per furniture.
*/
private static final EntityDataAccessor<Byte> SEAT_LOCK_BITS =
SynchedEntityData.defineId(EntityFurniture.class, EntityDataSerializers.BYTE);
SynchedEntityData.defineId(
EntityFurniture.class,
EntityDataSerializers.BYTE
);
/** Current animation state (idle, occupied, locking, struggle). */
private static final EntityDataAccessor<Byte> ANIM_STATE =
SynchedEntityData.defineId(EntityFurniture.class, EntityDataSerializers.BYTE);
SynchedEntityData.defineId(
EntityFurniture.class,
EntityDataSerializers.BYTE
);
// ========== Animation State Constants ==========
@@ -166,7 +177,10 @@ public class EntityFurniture extends Entity
public EntityDimensions getDimensions(Pose pose) {
FurnitureDefinition def = getDefinition();
if (def != null) {
return EntityDimensions.fixed(def.hitboxWidth(), def.hitboxHeight());
return EntityDimensions.fixed(
def.hitboxWidth(),
def.hitboxHeight()
);
}
return EntityDimensions.fixed(1.0f, 1.0f);
}
@@ -228,7 +242,9 @@ public class EntityFurniture extends Entity
if (locked) {
writeFurnitureReconnectionTag(serverPlayer, seatId);
} else {
serverPlayer.getPersistentData().remove("tiedup_locked_furniture");
serverPlayer
.getPersistentData()
.remove("tiedup_locked_furniture");
}
}
}
@@ -258,7 +274,10 @@ public class EntityFurniture extends Entity
* @param player the server player locked in this furniture
* @param seatId the seat ID the player is locked in
*/
private void writeFurnitureReconnectionTag(ServerPlayer player, String seatId) {
private void writeFurnitureReconnectionTag(
ServerPlayer player,
String seatId
) {
CompoundTag tag = new CompoundTag();
BlockPos pos = this.blockPosition();
tag.putInt("x", pos.getX());
@@ -311,13 +330,21 @@ public class EntityFurniture extends Entity
* by spacing seats evenly along the furniture's local right axis.</p>
*/
@Override
protected void positionRider(Entity passenger, Entity.MoveFunction moveFunction) {
protected void positionRider(
Entity passenger,
Entity.MoveFunction moveFunction
) {
if (!this.hasPassenger(passenger)) return;
SeatDefinition seat = getSeatForPassenger(passenger);
if (seat == null) {
// Fallback: center of entity at base height
moveFunction.accept(passenger, this.getX(), this.getY(), this.getZ());
moveFunction.accept(
passenger,
this.getX(),
this.getY(),
this.getZ()
);
return;
}
@@ -326,10 +353,13 @@ public class EntityFurniture extends Entity
if (this.level().isClientSide) {
FurnitureDefinition def = getDefinition();
if (def != null) {
double[] pos = com.tiedup.remake.v2.furniture.client.FurnitureSeatPositionHelper
.getSeatWorldPosition(
def, seat.id(),
this.getX(), this.getY(), this.getZ(),
double[] pos =
com.tiedup.remake.v2.furniture.client.FurnitureSeatPositionHelper.getSeatWorldPosition(
def,
seat.id(),
this.getX(),
this.getY(),
this.getZ(),
this.getYRot()
);
if (pos != null) {
@@ -349,7 +379,8 @@ public class EntityFurniture extends Entity
float yawRad = (float) Math.toRadians(this.getYRot());
double rightX = -Math.sin(yawRad + Math.PI / 2.0);
double rightZ = Math.cos(yawRad + Math.PI / 2.0);
double offset = seatCount == 1 ? 0.0 : (seatIdx - (seatCount - 1) / 2.0);
double offset =
seatCount == 1 ? 0.0 : (seatIdx - (seatCount - 1) / 2.0);
moveFunction.accept(
passenger,
@@ -464,7 +495,10 @@ public class EntityFurniture extends Entity
* @return the best matching seat, or null if no occupied lockable seats exist
*/
@Nullable
private SeatDefinition findNearestOccupiedLockableSeat(Player player, FurnitureDefinition def) {
private SeatDefinition findNearestOccupiedLockableSeat(
Player player,
FurnitureDefinition def
) {
Vec3 playerPos = player.getEyePosition();
Vec3 lookDir = player.getLookAngle();
float yawRad = (float) Math.toRadians(this.getYRot());
@@ -482,7 +516,9 @@ public class EntityFurniture extends Entity
SeatDefinition seat = seats.get(i);
// Only consider occupied, lockable seats
if (!seat.lockable() || !seatAssignments.containsValue(seat.id())) continue;
if (
!seat.lockable() || !seatAssignments.containsValue(seat.id())
) continue;
double offset = seatCount == 1 ? 0.0 : (i - (seatCount - 1) / 2.0);
Vec3 seatWorldPos = new Vec3(
@@ -533,9 +569,20 @@ public class EntityFurniture extends Entity
if (passenger instanceof ServerPlayer sp) {
FurnitureDefinition def = getDefinition();
if (def != null && def.feedback().deniedSound() != null) {
sp.level().playSound(null, sp.getX(), sp.getY(), sp.getZ(),
SoundEvent.createVariableRangeEvent(def.feedback().deniedSound()),
SoundSource.PLAYERS, 0.5f, 1.0f);
sp
.level()
.playSound(
null,
sp.getX(),
sp.getY(),
sp.getZ(),
SoundEvent.createVariableRangeEvent(
def.feedback().deniedSound()
),
SoundSource.PLAYERS,
0.5f,
1.0f
);
}
}
@@ -546,7 +593,11 @@ public class EntityFurniture extends Entity
String lockedSeatId = seat.id();
Entity self = this;
this.getServer().execute(() -> {
if (passenger.isAlive() && self.isAlive() && !self.isRemoved()) {
if (
passenger.isAlive() &&
self.isAlive() &&
!self.isRemoved()
) {
passenger.startRiding(self, true);
// Re-assign the specific seat (not just first available)
assignSeat(passenger, lockedSeatId);
@@ -575,7 +626,9 @@ public class EntityFurniture extends Entity
if (!this.level().isClientSide) {
this.entityData.set(ANIM_STATE, STATE_EXITING);
this.transitionTicksLeft = 20;
this.transitionTargetState = this.getPassengers().isEmpty() ? STATE_IDLE : STATE_OCCUPIED;
this.transitionTargetState = this.getPassengers().isEmpty()
? STATE_IDLE
: STATE_OCCUPIED;
}
// Client-side: immediately stop the furniture pose animation on the dismounting player.
@@ -583,8 +636,9 @@ public class EntityFurniture extends Entity
// The safety tick in AnimationTickHandler also catches this after a 3-tick grace period,
// but stopping immediately avoids a visible animation glitch during dismount.
if (this.level().isClientSide && passenger instanceof Player) {
com.tiedup.remake.client.animation.BondageAnimationManager
.stopFurniture((Player) passenger);
com.tiedup.remake.client.animation.BondageAnimationManager.stopFurniture(
(Player) passenger
);
}
}
@@ -598,7 +652,9 @@ public class EntityFurniture extends Entity
// Don't clobber active transitions — let the tick timer handle the reset
if (this.transitionTicksLeft > 0) return;
byte state = this.getPassengers().isEmpty() ? STATE_IDLE : STATE_OCCUPIED;
byte state = this.getPassengers().isEmpty()
? STATE_IDLE
: STATE_OCCUPIED;
this.entityData.set(ANIM_STATE, state);
}
@@ -628,11 +684,16 @@ public class EntityFurniture extends Entity
// Priority 1: Force-mount a leashed captive onto an available seat.
// The interacting player must be a captor with at least one leashed captive,
// and the captive must wear a collar owned by this player (or player is OP).
if (player instanceof ServerPlayer serverPlayer
&& this.getPassengers().size() < def.seats().size()) {
PlayerBindState ownerState = PlayerBindState.getInstance(serverPlayer);
if (
player instanceof ServerPlayer serverPlayer &&
this.getPassengers().size() < def.seats().size()
) {
PlayerBindState ownerState = PlayerBindState.getInstance(
serverPlayer
);
if (ownerState != null) {
PlayerCaptorManager captorManager = ownerState.getCaptorManager();
PlayerCaptorManager captorManager =
ownerState.getCaptorManager();
if (captorManager != null && captorManager.hasCaptives()) {
for (IBondageState captive : captorManager.getCaptives()) {
LivingEntity captiveEntity = captive.asLivingEntity();
@@ -647,11 +708,18 @@ public class EntityFurniture extends Entity
// Verify collar ownership
if (!captive.hasCollar()) continue;
ItemStack collarStack = captive.getEquipment(BodyRegionV2.NECK);
if (collarStack.isEmpty()
|| !(collarStack.getItem() instanceof ItemCollar collar)) continue;
if (!collar.isOwner(collarStack, serverPlayer)
&& !serverPlayer.hasPermissions(2)) continue;
ItemStack collarStack = captive.getEquipment(
BodyRegionV2.NECK
);
if (
collarStack.isEmpty() ||
!(collarStack.getItem() instanceof
ItemCollar collar)
) continue;
if (
!collar.isOwner(collarStack, serverPlayer) &&
!serverPlayer.hasPermissions(2)
) continue;
// Detach leash only (drop the lead, keep tied-up status)
captive.free(false);
@@ -662,10 +730,18 @@ public class EntityFurniture extends Entity
// Play mount sound
FurnitureFeedback feedback = def.feedback();
if (feedback.mountSound() != null) {
this.level().playSound(null,
this.getX(), this.getY(), this.getZ(),
SoundEvent.createVariableRangeEvent(feedback.mountSound()),
SoundSource.BLOCKS, 1.0f, 1.0f);
this.level().playSound(
null,
this.getX(),
this.getY(),
this.getZ(),
SoundEvent.createVariableRangeEvent(
feedback.mountSound()
),
SoundSource.BLOCKS,
1.0f,
1.0f
);
}
// Broadcast updated state to tracking clients
@@ -688,7 +764,10 @@ public class EntityFurniture extends Entity
// Use look direction to pick the nearest occupied, lockable seat.
ItemStack heldItem = player.getItemInHand(hand);
if (isKeyItem(heldItem) && !this.getPassengers().isEmpty()) {
SeatDefinition targetSeat = findNearestOccupiedLockableSeat(player, def);
SeatDefinition targetSeat = findNearestOccupiedLockableSeat(
player,
def
);
if (targetSeat != null) {
boolean wasLocked = isSeatLocked(targetSeat.id());
setSeatLocked(targetSeat.id(), !wasLocked);
@@ -699,15 +778,24 @@ public class EntityFurniture extends Entity
? feedback.unlockSound()
: feedback.lockSound();
if (soundRL != null) {
this.level().playSound(null, this.getX(), this.getY(), this.getZ(),
this.level().playSound(
null,
this.getX(),
this.getY(),
this.getZ(),
SoundEvent.createVariableRangeEvent(soundRL),
SoundSource.BLOCKS, 1.0f, 1.0f);
SoundSource.BLOCKS,
1.0f,
1.0f
);
}
// Set lock/unlock animation with transition timer
boolean nowLocked = !wasLocked;
this.entityData.set(ANIM_STATE,
nowLocked ? STATE_LOCKING : STATE_UNLOCKING);
this.entityData.set(
ANIM_STATE,
nowLocked ? STATE_LOCKING : STATE_UNLOCKING
);
this.transitionTicksLeft = 20;
this.transitionTargetState = STATE_OCCUPIED;
@@ -723,7 +811,10 @@ public class EntityFurniture extends Entity
}
// Priority 3: Empty hand + available seat -> self mount
if (heldItem.isEmpty() && this.getPassengers().size() < def.seats().size()) {
if (
heldItem.isEmpty() &&
this.getPassengers().size() < def.seats().size()
) {
player.startRiding(this);
return InteractionResult.CONSUME;
}
@@ -731,9 +822,16 @@ public class EntityFurniture extends Entity
// No valid action — play denied sound if configured
FurnitureFeedback feedback = def.feedback();
if (feedback != null && feedback.deniedSound() != null) {
this.level().playSound(null, this.getX(), this.getY(), this.getZ(),
this.level().playSound(
null,
this.getX(),
this.getY(),
this.getZ(),
SoundEvent.createVariableRangeEvent(feedback.deniedSound()),
SoundSource.BLOCKS, 0.5f, 1.0f);
SoundSource.BLOCKS,
0.5f,
1.0f
);
}
return InteractionResult.PASS;
@@ -775,10 +873,22 @@ public class EntityFurniture extends Entity
// Creative mode bypasses this check (handled above with instant discard).
if (!this.getPassengers().isEmpty()) {
FurnitureDefinition occupiedDef = getDefinition();
if (occupiedDef != null && occupiedDef.feedback().deniedSound() != null) {
this.level().playSound(null, this.getX(), this.getY(), this.getZ(),
SoundEvent.createVariableRangeEvent(occupiedDef.feedback().deniedSound()),
SoundSource.BLOCKS, 0.5f, 1.0f);
if (
occupiedDef != null &&
occupiedDef.feedback().deniedSound() != null
) {
this.level().playSound(
null,
this.getX(),
this.getY(),
this.getZ(),
SoundEvent.createVariableRangeEvent(
occupiedDef.feedback().deniedSound()
),
SoundSource.BLOCKS,
0.5f,
1.0f
);
}
return true; // Consume the attack but accumulate no damage
}
@@ -790,7 +900,9 @@ public class EntityFurniture extends Entity
if (this.currentDamage >= resistance) {
this.ejectPassengers();
if (def != null && def.dropOnBreak()) {
ItemStack dropStack = FurniturePlacerItem.createStack(def.id());
ItemStack dropStack = FurniturePlacerItem.createStack(
def.id()
);
if (!dropStack.isEmpty()) {
this.spawnAtLocation(dropStack);
}
@@ -850,9 +962,9 @@ public class EntityFurniture extends Entity
passengerUuids.add(passenger.getUUID());
}
boolean removed = seatAssignments.keySet().removeIf(
uuid -> !passengerUuids.contains(uuid)
);
boolean removed = seatAssignments
.keySet()
.removeIf(uuid -> !passengerUuids.contains(uuid));
if (removed) {
TiedUpMod.LOGGER.debug(
@@ -888,7 +1000,8 @@ public class EntityFurniture extends Entity
@Override
protected void readAdditionalSaveData(CompoundTag tag) {
if (tag.contains(FurnitureRegistry.NBT_FURNITURE_ID, 8)) { // 8 = TAG_String
if (tag.contains(FurnitureRegistry.NBT_FURNITURE_ID, 8)) {
// 8 = TAG_String
setFurnitureId(tag.getString(FurnitureRegistry.NBT_FURNITURE_ID));
}
if (tag.contains("facing")) {
@@ -909,7 +1022,8 @@ public class EntityFurniture extends Entity
// Restore seat assignments
seatAssignments.clear();
if (tag.contains("seat_assignments", 10)) { // 10 = TAG_Compound
if (tag.contains("seat_assignments", 10)) {
// 10 = TAG_Compound
CompoundTag assignments = tag.getCompound("seat_assignments");
for (String key : assignments.getAllKeys()) {
try {

View File

@@ -31,6 +31,11 @@ public record FurnitureFeedback(
) {
/** Empty feedback -- all sounds null (use defaults). */
public static final FurnitureFeedback EMPTY = new FurnitureFeedback(
null, null, null, null, null, null
null,
null,
null,
null,
null,
null
);
}

View File

@@ -32,11 +32,15 @@ import org.jetbrains.annotations.Nullable;
*/
public final class FurnitureParser {
private static final Logger LOGGER = LogManager.getLogger("TiedUpFurniture");
private static final Logger LOGGER = LogManager.getLogger(
"TiedUpFurniture"
);
private static final String TAG = "[FurnitureParser]";
/** Strict hex color pattern: # followed by exactly 6 hex digits. */
private static final Pattern HEX_COLOR = Pattern.compile("^#[0-9A-Fa-f]{6}$");
private static final Pattern HEX_COLOR = Pattern.compile(
"^#[0-9A-Fa-f]{6}$"
);
/** Maximum number of seats per furniture (bitmask limit: 8 bits). */
private static final int MAX_SEATS = 8;
@@ -51,7 +55,10 @@ public final class FurnitureParser {
* @return the parsed definition, or null if the file is invalid
*/
@Nullable
public static FurnitureDefinition parse(InputStream input, ResourceLocation fileId) {
public static FurnitureDefinition parse(
InputStream input,
ResourceLocation fileId
) {
try {
JsonObject root = JsonParser.parseReader(
new InputStreamReader(input, StandardCharsets.UTF_8)
@@ -59,7 +66,12 @@ public final class FurnitureParser {
return parseObject(root, fileId);
} catch (Exception e) {
LOGGER.error("{} Failed to parse JSON {}: {}", TAG, fileId, e.getMessage());
LOGGER.error(
"{} Failed to parse JSON {}: {}",
TAG,
fileId,
e.getMessage()
);
return null;
}
}
@@ -72,7 +84,10 @@ public final class FurnitureParser {
* @return the parsed definition, or null if validation fails
*/
@Nullable
public static FurnitureDefinition parseObject(JsonObject root, ResourceLocation fileId) {
public static FurnitureDefinition parseObject(
JsonObject root,
ResourceLocation fileId
) {
// --- Required: id ---
String idStr = getStringOrNull(root, "id");
if (idStr == null || idStr.isEmpty()) {
@@ -81,7 +96,12 @@ public final class FurnitureParser {
}
ResourceLocation id = ResourceLocation.tryParse(idStr);
if (id == null) {
LOGGER.error("{} Skipping {}: invalid id ResourceLocation '{}'", TAG, fileId, idStr);
LOGGER.error(
"{} Skipping {}: invalid id ResourceLocation '{}'",
TAG,
fileId,
idStr
);
return null;
}
@@ -103,7 +123,12 @@ public final class FurnitureParser {
}
ResourceLocation modelLocation = ResourceLocation.tryParse(modelStr);
if (modelLocation == null) {
LOGGER.error("{} Skipping {}: invalid model ResourceLocation '{}'", TAG, fileId, modelStr);
LOGGER.error(
"{} Skipping {}: invalid model ResourceLocation '{}'",
TAG,
fileId,
modelStr
);
return null;
}
@@ -115,15 +140,27 @@ public final class FurnitureParser {
}
// --- Optional: supports_color (default false) ---
boolean supportsColor = getBooleanOrDefault(root, "supports_color", false);
boolean supportsColor = getBooleanOrDefault(
root,
"supports_color",
false
);
// --- Optional: hitbox (defaults: 1.0 x 1.0, clamped [0.1, 5.0]) ---
float hitboxWidth = 1.0f;
float hitboxHeight = 1.0f;
if (root.has("hitbox") && root.get("hitbox").isJsonObject()) {
JsonObject hitbox = root.getAsJsonObject("hitbox");
hitboxWidth = clamp(getFloatOrDefault(hitbox, "width", 1.0f), 0.1f, 5.0f);
hitboxHeight = clamp(getFloatOrDefault(hitbox, "height", 1.0f), 0.1f, 5.0f);
hitboxWidth = clamp(
getFloatOrDefault(hitbox, "width", 1.0f),
0.1f,
5.0f
);
hitboxHeight = clamp(
getFloatOrDefault(hitbox, "height", 1.0f),
0.1f,
5.0f
);
}
// --- Optional: placement ---
@@ -139,14 +176,22 @@ public final class FurnitureParser {
boolean lockable = getBooleanOrDefault(root, "lockable", false);
// --- Optional: break_resistance (default 100, clamped [1, 10000]) ---
float breakResistance = clamp(getFloatOrDefault(root, "break_resistance", 100.0f), 1.0f, 10000.0f);
float breakResistance = clamp(
getFloatOrDefault(root, "break_resistance", 100.0f),
1.0f,
10000.0f
);
// --- Optional: drop_on_break (default true) ---
boolean dropOnBreak = getBooleanOrDefault(root, "drop_on_break", true);
// --- Required: seats (non-empty array, size [1, 8]) ---
if (!root.has("seats") || !root.get("seats").isJsonArray()) {
LOGGER.error("{} Skipping {}: missing or invalid 'seats' array", TAG, fileId);
LOGGER.error(
"{} Skipping {}: missing or invalid 'seats' array",
TAG,
fileId
);
return null;
}
JsonArray seatsArray = root.getAsJsonArray("seats");
@@ -155,18 +200,33 @@ public final class FurnitureParser {
return null;
}
if (seatsArray.size() > MAX_SEATS) {
LOGGER.error("{} Skipping {}: 'seats' array has {} entries (max {})",
TAG, fileId, seatsArray.size(), MAX_SEATS);
LOGGER.error(
"{} Skipping {}: 'seats' array has {} entries (max {})",
TAG,
fileId,
seatsArray.size(),
MAX_SEATS
);
return null;
}
List<SeatDefinition> seats = new ArrayList<>(seatsArray.size());
for (int i = 0; i < seatsArray.size(); i++) {
if (!seatsArray.get(i).isJsonObject()) {
LOGGER.error("{} Skipping {}: seats[{}] is not a JSON object", TAG, fileId, i);
LOGGER.error(
"{} Skipping {}: seats[{}] is not a JSON object",
TAG,
fileId,
i
);
return null;
}
SeatDefinition seat = parseSeat(seatsArray.get(i).getAsJsonObject(), i, lockable, fileId);
SeatDefinition seat = parseSeat(
seatsArray.get(i).getAsJsonObject(),
i,
lockable,
fileId
);
if (seat == null) {
// parseSeat already logged the error
return null;
@@ -184,15 +244,30 @@ public final class FurnitureParser {
String category = getStringOrDefault(root, "category", "furniture");
// --- Optional: icon (item model ResourceLocation for inventory sprite) ---
ResourceLocation icon = parseOptionalResourceLocation(root, "icon", fileId);
ResourceLocation icon = parseOptionalResourceLocation(
root,
"icon",
fileId
);
return new FurnitureDefinition(
id, displayName, translationKey, modelLocation,
tintChannels, supportsColor,
hitboxWidth, hitboxHeight,
snapToWall, floorOnly,
lockable, breakResistance, dropOnBreak,
seats, feedback, category, icon
id,
displayName,
translationKey,
modelLocation,
tintChannels,
supportsColor,
hitboxWidth,
hitboxHeight,
snapToWall,
floorOnly,
lockable,
breakResistance,
dropOnBreak,
seats,
feedback,
category,
icon
);
}
@@ -208,47 +283,85 @@ public final class FurnitureParser {
* @return the parsed seat, or null on validation failure
*/
@Nullable
private static SeatDefinition parseSeat(JsonObject obj, int index,
boolean parentLockable,
ResourceLocation fileId) {
private static SeatDefinition parseSeat(
JsonObject obj,
int index,
boolean parentLockable,
ResourceLocation fileId
) {
// Required: id (must not contain ':')
String seatId = getStringOrNull(obj, "id");
if (seatId == null || seatId.isEmpty()) {
LOGGER.error("{} Skipping {}: seats[{}] missing 'id'", TAG, fileId, index);
LOGGER.error(
"{} Skipping {}: seats[{}] missing 'id'",
TAG,
fileId,
index
);
return null;
}
if (seatId.contains(":")) {
LOGGER.error("{} Skipping {}: seats[{}] id '{}' must not contain ':'",
TAG, fileId, index, seatId);
LOGGER.error(
"{} Skipping {}: seats[{}] id '{}' must not contain ':'",
TAG,
fileId,
index,
seatId
);
return null;
}
// Required: armature
String armature = getStringOrNull(obj, "armature");
if (armature == null || armature.isEmpty()) {
LOGGER.error("{} Skipping {}: seats[{}] missing 'armature'", TAG, fileId, index);
LOGGER.error(
"{} Skipping {}: seats[{}] missing 'armature'",
TAG,
fileId,
index
);
return null;
}
// Optional: blocked_regions (unknown region = fatal for entire furniture)
Set<BodyRegionV2> blockedRegions = parseBlockedRegions(obj, index, fileId);
Set<BodyRegionV2> blockedRegions = parseBlockedRegions(
obj,
index,
fileId
);
if (blockedRegions == null) {
// parseBlockedRegions returns null ONLY on unknown region name (fatal)
return null;
}
// Optional: lockable (inherits from top-level)
boolean seatLockable = getBooleanOrDefault(obj, "lockable", parentLockable);
boolean seatLockable = getBooleanOrDefault(
obj,
"lockable",
parentLockable
);
// Optional: locked_difficulty (clamped [1, 10000], default 1)
int lockedDifficulty = clampInt(getIntOrDefault(obj, "locked_difficulty", 1), 1, 10000);
int lockedDifficulty = clampInt(
getIntOrDefault(obj, "locked_difficulty", 1),
1,
10000
);
// Optional: item_difficulty_bonus (default false)
boolean itemDifficultyBonus = getBooleanOrDefault(obj, "item_difficulty_bonus", false);
boolean itemDifficultyBonus = getBooleanOrDefault(
obj,
"item_difficulty_bonus",
false
);
return new SeatDefinition(
seatId, armature, blockedRegions,
seatLockable, lockedDifficulty, itemDifficultyBonus
seatId,
armature,
blockedRegions,
seatLockable,
lockedDifficulty,
itemDifficultyBonus
);
}
@@ -257,15 +370,25 @@ public final class FurnitureParser {
* Returns null (fatal) if any region name is unknown.
*/
@Nullable
private static Set<BodyRegionV2> parseBlockedRegions(JsonObject obj, int seatIndex,
ResourceLocation fileId) {
if (!obj.has("blocked_regions") || !obj.get("blocked_regions").isJsonArray()) {
return Collections.unmodifiableSet(EnumSet.noneOf(BodyRegionV2.class));
private static Set<BodyRegionV2> parseBlockedRegions(
JsonObject obj,
int seatIndex,
ResourceLocation fileId
) {
if (
!obj.has("blocked_regions") ||
!obj.get("blocked_regions").isJsonArray()
) {
return Collections.unmodifiableSet(
EnumSet.noneOf(BodyRegionV2.class)
);
}
JsonArray arr = obj.getAsJsonArray("blocked_regions");
if (arr.isEmpty()) {
return Collections.unmodifiableSet(EnumSet.noneOf(BodyRegionV2.class));
return Collections.unmodifiableSet(
EnumSet.noneOf(BodyRegionV2.class)
);
}
EnumSet<BodyRegionV2> regions = EnumSet.noneOf(BodyRegionV2.class);
@@ -274,15 +397,25 @@ public final class FurnitureParser {
try {
name = elem.getAsString().toUpperCase();
} catch (Exception e) {
LOGGER.error("{} Skipping {}: seats[{}] invalid element in 'blocked_regions': {}",
TAG, fileId, seatIndex, e.getMessage());
LOGGER.error(
"{} Skipping {}: seats[{}] invalid element in 'blocked_regions': {}",
TAG,
fileId,
seatIndex,
e.getMessage()
);
return null;
}
BodyRegionV2 region = BodyRegionV2.fromName(name);
if (region == null) {
LOGGER.error("{} Skipping {}: seats[{}] unknown body region '{}'",
TAG, fileId, seatIndex, name);
LOGGER.error(
"{} Skipping {}: seats[{}] unknown body region '{}'",
TAG,
fileId,
seatIndex,
name
);
return null;
}
regions.add(region);
@@ -293,7 +426,10 @@ public final class FurnitureParser {
// ===== Feedback Parsing =====
private static FurnitureFeedback parseFeedback(JsonObject obj, ResourceLocation fileId) {
private static FurnitureFeedback parseFeedback(
JsonObject obj,
ResourceLocation fileId
) {
return new FurnitureFeedback(
parseOptionalResourceLocation(obj, "mount_sound", fileId),
parseOptionalResourceLocation(obj, "lock_sound", fileId),
@@ -311,8 +447,14 @@ public final class FurnitureParser {
* Returns empty map if field is absent. Returns null if any value is invalid hex.
*/
@Nullable
private static Map<String, Integer> parseTintChannels(JsonObject root, ResourceLocation fileId) {
if (!root.has("tint_channels") || !root.get("tint_channels").isJsonObject()) {
private static Map<String, Integer> parseTintChannels(
JsonObject root,
ResourceLocation fileId
) {
if (
!root.has("tint_channels") ||
!root.get("tint_channels").isJsonObject()
) {
return Map.of();
}
@@ -324,15 +466,24 @@ public final class FurnitureParser {
try {
hex = entry.getValue().getAsString();
} catch (Exception e) {
LOGGER.error("{} Skipping {}: tint_channels '{}' value is not a string",
TAG, fileId, entry.getKey());
LOGGER.error(
"{} Skipping {}: tint_channels '{}' value is not a string",
TAG,
fileId,
entry.getKey()
);
return null;
}
if (!HEX_COLOR.matcher(hex).matches()) {
LOGGER.error("{} Skipping {}: tint_channels '{}' has invalid hex color '{}' "
+ "(expected '#' followed by 6 hex digits)",
TAG, fileId, entry.getKey(), hex);
LOGGER.error(
"{} Skipping {}: tint_channels '{}' has invalid hex color '{}' " +
"(expected '#' followed by 6 hex digits)",
TAG,
fileId,
entry.getKey(),
hex
);
return null;
}
@@ -355,12 +506,20 @@ public final class FurnitureParser {
}
}
private static String getStringOrDefault(JsonObject obj, String key, String defaultValue) {
private static String getStringOrDefault(
JsonObject obj,
String key,
String defaultValue
) {
String value = getStringOrNull(obj, key);
return (value != null && !value.isEmpty()) ? value : defaultValue;
}
private static int getIntOrDefault(JsonObject obj, String key, int defaultValue) {
private static int getIntOrDefault(
JsonObject obj,
String key,
int defaultValue
) {
if (!obj.has(key) || obj.get(key).isJsonNull()) return defaultValue;
try {
return obj.get(key).getAsInt();
@@ -369,7 +528,11 @@ public final class FurnitureParser {
}
}
private static float getFloatOrDefault(JsonObject obj, String key, float defaultValue) {
private static float getFloatOrDefault(
JsonObject obj,
String key,
float defaultValue
) {
if (!obj.has(key) || obj.get(key).isJsonNull()) return defaultValue;
try {
return obj.get(key).getAsFloat();
@@ -378,7 +541,11 @@ public final class FurnitureParser {
}
}
private static boolean getBooleanOrDefault(JsonObject obj, String key, boolean defaultValue) {
private static boolean getBooleanOrDefault(
JsonObject obj,
String key,
boolean defaultValue
) {
if (!obj.has(key) || obj.get(key).isJsonNull()) return defaultValue;
try {
return obj.get(key).getAsBoolean();
@@ -389,13 +556,21 @@ public final class FurnitureParser {
@Nullable
private static ResourceLocation parseOptionalResourceLocation(
JsonObject obj, String key, ResourceLocation fileId
JsonObject obj,
String key,
ResourceLocation fileId
) {
String value = getStringOrNull(obj, key);
if (value == null || value.isEmpty()) return null;
ResourceLocation loc = ResourceLocation.tryParse(value);
if (loc == null) {
LOGGER.warn("{} In {}: invalid ResourceLocation for '{}': '{}'", TAG, fileId, key, value);
LOGGER.warn(
"{} In {}: invalid ResourceLocation for '{}': '{}'",
TAG,
fileId,
key,
value
);
}
return loc;
}

View File

@@ -86,7 +86,8 @@ public class FurniturePlacerItem extends Item {
// Spawn the furniture entity
EntityFurniture furniture = new EntityFurniture(
ModEntities.FURNITURE.get(), level
ModEntities.FURNITURE.get(),
level
);
furniture.setFurnitureId(furnitureIdStr);
furniture.moveTo(spawnPos.x, spawnPos.y, spawnPos.z);
@@ -101,10 +102,19 @@ public class FurniturePlacerItem extends Item {
// Snap to wall: if enabled, check 4 cardinal directions for an adjacent wall
// and rotate the furniture to face it (back against wall), overriding player yaw.
if (def.snapToWall()) {
Direction[] directions = {Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST};
Direction[] directions = {
Direction.NORTH,
Direction.SOUTH,
Direction.EAST,
Direction.WEST,
};
for (Direction dir : directions) {
BlockPos wallPos = spawnBlockPos.relative(dir);
if (level.getBlockState(wallPos).isFaceSturdy(level, wallPos, dir.getOpposite())) {
if (
level
.getBlockState(wallPos)
.isFaceSturdy(level, wallPos, dir.getOpposite())
) {
yaw = dir.toYRot();
break;
}
@@ -155,9 +165,12 @@ public class FurniturePlacerItem extends Item {
public static ItemStack createStack(ResourceLocation furnitureId) {
if (V2BondageItems.FURNITURE_PLACER == null) return ItemStack.EMPTY;
ItemStack stack = new ItemStack(V2BondageItems.FURNITURE_PLACER.get());
stack.getOrCreateTag().putString(
FurnitureRegistry.NBT_FURNITURE_ID, furnitureId.toString()
);
stack
.getOrCreateTag()
.putString(
FurnitureRegistry.NBT_FURNITURE_ID,
furnitureId.toString()
);
return stack;
}
@@ -173,8 +186,12 @@ public class FurniturePlacerItem extends Item {
public static String getFurnitureIdFromStack(ItemStack stack) {
if (stack.isEmpty() || !stack.hasTag()) return null;
// noinspection DataFlowIssue -- hasTag() guarantees non-null
if (!stack.getTag().contains(FurnitureRegistry.NBT_FURNITURE_ID, 8)) return null;
String id = stack.getTag().getString(FurnitureRegistry.NBT_FURNITURE_ID);
if (
!stack.getTag().contains(FurnitureRegistry.NBT_FURNITURE_ID, 8)
) return null;
String id = stack
.getTag()
.getString(FurnitureRegistry.NBT_FURNITURE_ID);
return id.isEmpty() ? null : id;
}
}

View File

@@ -30,7 +30,10 @@ public final class FurnitureRegistry {
* Volatile reference to an unmodifiable map. {@link #reload} builds a new map
* and swaps atomically; consumer threads always see a consistent snapshot.
*/
private static volatile Map<ResourceLocation, FurnitureDefinition> DEFINITIONS = Map.of();
private static volatile Map<
ResourceLocation,
FurnitureDefinition
> DEFINITIONS = Map.of();
private FurnitureRegistry() {}
@@ -41,7 +44,9 @@ public final class FurnitureRegistry {
*
* @param newDefs the new definitions map (will be defensively copied)
*/
public static void reload(Map<ResourceLocation, FurnitureDefinition> newDefs) {
public static void reload(
Map<ResourceLocation, FurnitureDefinition> newDefs
) {
DEFINITIONS = Collections.unmodifiableMap(new HashMap<>(newDefs));
}

View File

@@ -22,30 +22,45 @@ import org.apache.logging.log4j.Logger;
* <p>Registered via {@link net.minecraftforge.event.AddReloadListenerEvent} in
* {@link com.tiedup.remake.core.TiedUpMod.ForgeEvents}.</p>
*/
public class FurnitureServerReloadListener extends SimplePreparableReloadListener<Void> {
public class FurnitureServerReloadListener
extends SimplePreparableReloadListener<Void>
{
private static final Logger LOGGER = LogManager.getLogger("TiedUpFurniture");
private static final Logger LOGGER = LogManager.getLogger(
"TiedUpFurniture"
);
/** Resource directory containing furniture definition JSON files (under data/). */
private static final String DIRECTORY = "tiedup_furniture";
@Override
protected Void prepare(ResourceManager resourceManager, ProfilerFiller profiler) {
protected Void prepare(
ResourceManager resourceManager,
ProfilerFiller profiler
) {
// No preparation needed -- parsing happens in apply phase
return null;
}
@Override
protected void apply(Void nothing, ResourceManager resourceManager, ProfilerFiller profiler) {
protected void apply(
Void nothing,
ResourceManager resourceManager,
ProfilerFiller profiler
) {
Map<ResourceLocation, FurnitureDefinition> newDefs = new HashMap<>();
Map<ResourceLocation, Resource> resources = resourceManager.listResources(
DIRECTORY, loc -> loc.getPath().endsWith(".json")
);
Map<ResourceLocation, Resource> resources =
resourceManager.listResources(DIRECTORY, loc ->
loc.getPath().endsWith(".json")
);
int skipped = 0;
for (Map.Entry<ResourceLocation, Resource> entry : resources.entrySet()) {
for (Map.Entry<
ResourceLocation,
Resource
> entry : resources.entrySet()) {
ResourceLocation fileId = entry.getKey();
Resource resource = entry.getValue();
@@ -55,17 +70,28 @@ public class FurnitureServerReloadListener extends SimplePreparableReloadListene
if (def != null) {
// Check for duplicate IDs
if (newDefs.containsKey(def.id())) {
LOGGER.warn("[TiedUpFurniture] Server: Duplicate furniture ID '{}' from file '{}' -- overwriting previous definition",
def.id(), fileId);
LOGGER.warn(
"[TiedUpFurniture] Server: Duplicate furniture ID '{}' from file '{}' -- overwriting previous definition",
def.id(),
fileId
);
}
newDefs.put(def.id(), def);
LOGGER.debug("[TiedUpFurniture] Server loaded: {} -> '{}'", def.id(), def.displayName());
LOGGER.debug(
"[TiedUpFurniture] Server loaded: {} -> '{}'",
def.id(),
def.displayName()
);
} else {
skipped++;
}
} catch (Exception e) {
LOGGER.error("[TiedUpFurniture] Server: Failed to read resource {}: {}", fileId, e.getMessage());
LOGGER.error(
"[TiedUpFurniture] Server: Failed to read resource {}: {}",
fileId,
e.getMessage()
);
skipped++;
}
}
@@ -77,12 +103,20 @@ public class FurnitureServerReloadListener extends SimplePreparableReloadListene
net.minecraft.server.MinecraftServer server =
net.minecraftforge.server.ServerLifecycleHooks.getCurrentServer();
if (server != null) {
for (net.minecraft.server.level.ServerPlayer p : server.getPlayerList().getPlayers()) {
com.tiedup.remake.v2.furniture.network.PacketSyncFurnitureDefinitions.sendToPlayer(p);
for (net.minecraft.server.level.ServerPlayer p : server
.getPlayerList()
.getPlayers()) {
com.tiedup.remake.v2.furniture.network.PacketSyncFurnitureDefinitions.sendToPlayer(
p
);
}
}
LOGGER.info("[TiedUpFurniture] Server loaded {} furniture definitions ({} skipped) from {} JSON files",
newDefs.size(), skipped, resources.size());
LOGGER.info(
"[TiedUpFurniture] Server loaded {} furniture definitions ({} skipped) from {} JSON files",
newDefs.size(),
skipped,
resources.size()
);
}
}

View File

@@ -14,7 +14,6 @@ import org.jetbrains.annotations.Nullable;
* never EntityFurniture directly.</p>
*/
public interface ISeatProvider {
/** All seat definitions for this entity. */
List<SeatDefinition> getSeats();

View File

@@ -7,11 +7,11 @@ import com.tiedup.remake.v2.BodyRegionV2;
import dev.kosmx.playerAnim.core.data.KeyframeAnimation;
import java.util.HashSet;
import java.util.Set;
import org.jetbrains.annotations.Nullable;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable;
/**
* Builds a {@link KeyframeAnimation} for a player seated on furniture.
@@ -43,7 +43,9 @@ import org.apache.logging.log4j.Logger;
@OnlyIn(Dist.CLIENT)
public final class FurnitureAnimationContext {
private static final Logger LOGGER = LogManager.getLogger("FurnitureAnimation");
private static final Logger LOGGER = LogManager.getLogger(
"FurnitureAnimation"
);
private FurnitureAnimationContext() {}
@@ -61,24 +63,30 @@ public final class FurnitureAnimationContext {
*/
@Nullable
public static KeyframeAnimation create(
GltfData.AnimationClip seatClip,
@Nullable GltfData seatSkeleton,
Set<BodyRegionV2> blockedRegions) {
GltfData.AnimationClip seatClip,
@Nullable GltfData seatSkeleton,
Set<BodyRegionV2> blockedRegions
) {
if (seatClip == null) {
LOGGER.warn("[FurnitureAnim] Cannot create animation: seatClip is null");
LOGGER.warn(
"[FurnitureAnim] Cannot create animation: seatClip is null"
);
return null;
}
if (seatSkeleton == null) {
// V1: skeleton parsing not yet implemented. Furniture animation requires
// rest pose data for glTF-to-PlayerAnimator conversion.
LOGGER.debug("[FurnitureAnim] Seat skeleton unavailable (V1), skipping animation");
LOGGER.debug(
"[FurnitureAnim] Seat skeleton unavailable (V1), skipping animation"
);
return null;
}
if (blockedRegions == null || blockedRegions.isEmpty()) {
LOGGER.debug("[FurnitureAnim] No blocked regions, skipping animation");
LOGGER.debug(
"[FurnitureAnim] No blocked regions, skipping animation"
);
return null;
}
@@ -87,9 +95,15 @@ public final class FurnitureAnimationContext {
KeyframeAnimation fullAnim;
try {
fullAnim = GltfPoseConverter.convertWithSkeleton(
seatSkeleton, seatClip, "furniture_seat");
seatSkeleton,
seatClip,
"furniture_seat"
);
} catch (Exception e) {
LOGGER.error("[FurnitureAnim] Failed to convert seat animation clip", e);
LOGGER.error(
"[FurnitureAnim] Failed to convert seat animation clip",
e
);
return null;
}
@@ -101,7 +115,10 @@ public final class FurnitureAnimationContext {
if (blockedParts.isEmpty()) {
// Blocked regions don't map to any animation bones (e.g., only NECK/FINGERS/TAIL/WINGS)
LOGGER.debug("[FurnitureAnim] Blocked regions {} map to zero bones, skipping", blockedRegions);
LOGGER.debug(
"[FurnitureAnim] Blocked regions {} map to zero bones, skipping",
blockedRegions
);
return null;
}
@@ -119,8 +136,12 @@ public final class FurnitureAnimationContext {
disableParts(builder, disabledParts);
KeyframeAnimation result = builder.build();
LOGGER.debug("[FurnitureAnim] Created animation: blocked={}, enabled={}, disabled={}",
blockedRegions, blockedParts, disabledParts);
LOGGER.debug(
"[FurnitureAnim] Created animation: blocked={}, enabled={}, disabled={}",
blockedRegions,
blockedParts,
disabledParts
);
return result;
}
@@ -135,7 +156,9 @@ public final class FurnitureAnimationContext {
* @param disabledParts set of PlayerAnimator part names to disable
*/
private static void disableParts(
KeyframeAnimation.AnimationBuilder builder, Set<String> disabledParts) {
KeyframeAnimation.AnimationBuilder builder,
Set<String> disabledParts
) {
for (String partName : disabledParts) {
KeyframeAnimation.StateCollection part = builder.getPart(partName);
if (part != null) {

View File

@@ -64,11 +64,18 @@ public class FurnitureEntityRenderer extends EntityRenderer<EntityFurniture> {
GltfData meshData = data.furnitureMesh();
// Compute joint matrices: animated if there is an active clip, static otherwise
GltfData.AnimationClip activeClip = resolveActiveAnimation(entity, meshData);
GltfData.AnimationClip activeClip = resolveActiveAnimation(
entity,
meshData
);
Matrix4f[] joints;
if (activeClip != null) {
float time = computeAnimationTime(entity, activeClip, partialTick);
joints = GltfSkinningEngine.computeJointMatricesAnimated(meshData, activeClip, time);
joints = GltfSkinningEngine.computeJointMatricesAnimated(
meshData,
activeClip,
time
);
} else {
joints = GltfSkinningEngine.computeJointMatrices(meshData);
}
@@ -88,15 +95,26 @@ public class FurnitureEntityRenderer extends EntityRenderer<EntityFurniture> {
// has multiple primitives (tintable and non-tintable parts).
Map<String, Integer> tintColors = def.tintChannels();
if (!tintColors.isEmpty() && meshData.primitives().size() > 1) {
RenderType renderType = GltfMeshRenderer.getRenderTypeForDefaultTexture();
RenderType renderType =
GltfMeshRenderer.getRenderTypeForDefaultTexture();
GltfMeshRenderer.renderSkinnedTinted(
meshData, joints, poseStack, buffer,
packedLight, packedOverlay, renderType, tintColors
meshData,
joints,
poseStack,
buffer,
packedLight,
packedOverlay,
renderType,
tintColors
);
} else {
GltfMeshRenderer.renderSkinned(
meshData, joints, poseStack, buffer,
packedLight, packedOverlay
meshData,
joints,
poseStack,
buffer,
packedLight,
packedOverlay
);
}
@@ -117,19 +135,22 @@ public class FurnitureEntityRenderer extends EntityRenderer<EntityFurniture> {
* @return the resolved animation clip, or null for static rendering
*/
private GltfData.AnimationClip resolveActiveAnimation(
EntityFurniture entity, GltfData meshData
EntityFurniture entity,
GltfData meshData
) {
String animName = switch (entity.getAnimState()) {
case EntityFurniture.STATE_OCCUPIED -> "Occupied";
case EntityFurniture.STATE_LOCKING -> "LockClose";
case EntityFurniture.STATE_STRUGGLE -> "Shake";
case EntityFurniture.STATE_UNLOCKING -> "LockOpen";
case EntityFurniture.STATE_ENTERING -> "Occupied"; // furniture plays Occupied during player enter transition
case EntityFurniture.STATE_EXITING -> "Idle"; // furniture transitions to Idle during player exit
case EntityFurniture.STATE_ENTERING -> "Occupied"; // furniture plays Occupied during player enter transition
case EntityFurniture.STATE_EXITING -> "Idle"; // furniture transitions to Idle during player exit
default -> "Idle";
};
GltfData.AnimationClip clip = meshData.getAnimation(animName);
if (clip == null && entity.getAnimState() != EntityFurniture.STATE_IDLE) {
if (
clip == null && entity.getAnimState() != EntityFurniture.STATE_IDLE
) {
// Specific state animation missing: fall back to Idle
clip = meshData.getAnimation("Idle");
}
@@ -150,7 +171,9 @@ public class FurnitureEntityRenderer extends EntityRenderer<EntityFurniture> {
* @return time in frame-space, suitable for {@link GltfSkinningEngine#computeJointMatricesAnimated}
*/
private float computeAnimationTime(
EntityFurniture entity, GltfData.AnimationClip clip, float partialTick
EntityFurniture entity,
GltfData.AnimationClip clip,
float partialTick
) {
int frameCount = clip.frameCount();
if (frameCount <= 1) return 0f;

View File

@@ -3,7 +3,6 @@ package com.tiedup.remake.v2.furniture.client;
import java.io.InputStream;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.jetbrains.annotations.Nullable;
import net.minecraft.client.Minecraft;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.Resource;
@@ -11,6 +10,7 @@ import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable;
/**
* Lazy-loading cache for parsed multi-armature furniture GLB data.
@@ -31,14 +31,11 @@ public final class FurnitureGltfCache {
* Sentinel value stored in the cache when loading fails, to avoid retrying
* broken resources on every frame.
*/
private static final FurnitureGltfData FAILED_SENTINEL = new FurnitureGltfData(
null,
Map.of(),
Map.of(),
Map.of()
);
private static final FurnitureGltfData FAILED_SENTINEL =
new FurnitureGltfData(null, Map.of(), Map.of(), Map.of());
private static final Map<ResourceLocation, FurnitureGltfData> CACHE = new ConcurrentHashMap<>();
private static final Map<ResourceLocation, FurnitureGltfData> CACHE =
new ConcurrentHashMap<>();
private FurnitureGltfCache() {}
@@ -51,7 +48,10 @@ public final class FurnitureGltfCache {
*/
@Nullable
public static FurnitureGltfData get(ResourceLocation modelLocation) {
FurnitureGltfData cached = CACHE.computeIfAbsent(modelLocation, FurnitureGltfCache::load);
FurnitureGltfData cached = CACHE.computeIfAbsent(
modelLocation,
FurnitureGltfCache::load
);
return cached == FAILED_SENTINEL ? null : cached;
}
@@ -72,12 +72,19 @@ public final class FurnitureGltfCache {
}
try (InputStream is = resource.open()) {
FurnitureGltfData data = FurnitureGlbParser.parse(is, loc.toString());
FurnitureGltfData data = FurnitureGlbParser.parse(
is,
loc.toString()
);
LOGGER.debug("[FurnitureGltf] Cached: {}", loc);
return data;
}
} catch (Exception e) {
LOGGER.error("[FurnitureGltf] Failed to load furniture GLB: {}", loc, e);
LOGGER.error(
"[FurnitureGltf] Failed to load furniture GLB: {}",
loc,
e
);
return FAILED_SENTINEL;
}
}

View File

@@ -38,7 +38,9 @@ public final class FurnitureSeatPositionHelper {
public static double[] getSeatWorldPosition(
FurnitureDefinition def,
String seatId,
double furnitureX, double furnitureY, double furnitureZ,
double furnitureX,
double furnitureY,
double furnitureZ,
float furnitureYRot
) {
ResourceLocation modelLoc = def.modelLocation();
@@ -47,7 +49,9 @@ public final class FurnitureSeatPositionHelper {
FurnitureGltfData gltfData = FurnitureGltfCache.get(modelLoc);
if (gltfData == null) return null;
FurnitureGltfData.SeatTransform transform = gltfData.seatTransforms().get(seatId);
FurnitureGltfData.SeatTransform transform = gltfData
.seatTransforms()
.get(seatId);
if (transform == null) return null;
// The seat transform position is in Minecraft model space (post-conversion):
@@ -67,7 +71,7 @@ public final class FurnitureSeatPositionHelper {
return new double[] {
furnitureX + rx,
furnitureY + sy,
furnitureZ + rz
furnitureZ + rz,
};
}
}

View File

@@ -88,7 +88,10 @@ public class PacketFurnitureEscape {
ctx.setPacketHandled(true);
}
private static void handleOnServer(PacketFurnitureEscape msg, NetworkEvent.Context ctx) {
private static void handleOnServer(
PacketFurnitureEscape msg,
NetworkEvent.Context ctx
) {
ServerPlayer sender = ctx.getSender();
if (sender == null) return;
@@ -103,10 +106,14 @@ public class PacketFurnitureEscape {
if (!entity.isAlive() || entity.isRemoved()) return;
// Validate escape method
if (msg.escapeMethod != METHOD_STRUGGLE && msg.escapeMethod != METHOD_LOCKPICK) {
if (
msg.escapeMethod != METHOD_STRUGGLE &&
msg.escapeMethod != METHOD_LOCKPICK
) {
TiedUpMod.LOGGER.warn(
"[PacketFurnitureEscape] Invalid escape method {} from {}",
msg.escapeMethod, sender.getName().getString()
msg.escapeMethod,
sender.getName().getString()
);
return;
}
@@ -133,7 +140,8 @@ public class PacketFurnitureEscape {
if (!furnitureEntity.hasPassenger(sender)) {
TiedUpMod.LOGGER.debug(
"[PacketFurnitureEscape] Struggle: {} is not a passenger of furniture {}",
sender.getName().getString(), furnitureEntity.getId()
sender.getName().getString(),
furnitureEntity.getId()
);
return;
}
@@ -160,12 +168,18 @@ public class PacketFurnitureEscape {
// Compute difficulty
int baseDifficulty = provider.getLockedDifficulty(seat.id());
int itemBonus = computeItemDifficultyBonus(sender, provider, seat);
int totalDifficulty = Math.min(baseDifficulty + itemBonus, MAX_DIFFICULTY);
int totalDifficulty = Math.min(
baseDifficulty + itemBonus,
MAX_DIFFICULTY
);
TiedUpMod.LOGGER.debug(
"[PacketFurnitureEscape] Struggle: {} on seat '{}' — difficulty {} (base {} + items {})",
sender.getName().getString(), seat.id(),
totalDifficulty, baseDifficulty, itemBonus
sender.getName().getString(),
seat.id(),
totalDifficulty,
baseDifficulty,
itemBonus
);
// Difficulty 0: immediate escape (no minigame needed)
@@ -181,13 +195,16 @@ public class PacketFurnitureEscape {
TiedUpMod.LOGGER.info(
"[PacketFurnitureEscape] {} escaped furniture {} (difficulty was 0)",
sender.getName().getString(), furnitureEntity.getId()
sender.getName().getString(),
furnitureEntity.getId()
);
return;
}
// Respect server config: if struggle minigame is disabled, skip
if (!com.tiedup.remake.core.ModConfig.SERVER.struggleMiniGameEnabled.get()) {
if (
!com.tiedup.remake.core.ModConfig.SERVER.struggleMiniGameEnabled.get()
) {
TiedUpMod.LOGGER.debug(
"[PacketFurnitureEscape] Struggle minigame disabled by server config"
);
@@ -196,9 +213,13 @@ public class PacketFurnitureEscape {
// Launch struggle minigame session via StruggleSessionManager
StruggleSessionManager manager = StruggleSessionManager.getInstance();
ContinuousStruggleMiniGameState session = manager.startFurnitureStruggleSession(
sender, furnitureEntity.getId(), seat.id(), totalDifficulty
);
ContinuousStruggleMiniGameState session =
manager.startFurnitureStruggleSession(
sender,
furnitureEntity.getId(),
seat.id(),
totalDifficulty
);
if (session != null) {
// Send START packet to open the struggle GUI on the client
@@ -255,7 +276,11 @@ public class PacketFurnitureEscape {
// Use look-direction-based seat targeting (same vector math as
// EntityFurniture.findNearestOccupiedLockableSeat) instead of
// blindly picking the first locked seat.
SeatDefinition targetSeat = findNearestLockedOccupiedSeat(sender, provider, furnitureEntity);
SeatDefinition targetSeat = findNearestLockedOccupiedSeat(
sender,
provider,
furnitureEntity
);
if (targetSeat == null) {
TiedUpMod.LOGGER.debug(
"[PacketFurnitureEscape] Lockpick: no locked occupied seat found"
@@ -264,30 +289,52 @@ public class PacketFurnitureEscape {
}
// Find the passenger in this seat (needed for item bonus computation)
Entity passenger = findPassengerInSeat(provider, furnitureEntity, targetSeat.id());
Entity passenger = findPassengerInSeat(
provider,
furnitureEntity,
targetSeat.id()
);
// Compute difficulty
int baseDifficulty = provider.getLockedDifficulty(targetSeat.id());
int itemBonus = 0;
if (passenger instanceof LivingEntity livingPassenger) {
itemBonus = computeItemDifficultyBonus(livingPassenger, provider, targetSeat);
itemBonus = computeItemDifficultyBonus(
livingPassenger,
provider,
targetSeat
);
}
int totalDifficulty = Math.min(baseDifficulty + itemBonus, MAX_DIFFICULTY);
int totalDifficulty = Math.min(
baseDifficulty + itemBonus,
MAX_DIFFICULTY
);
TiedUpMod.LOGGER.debug(
"[PacketFurnitureEscape] Lockpick: {} on seat '{}' -- difficulty {} (base {} + items {})",
sender.getName().getString(), targetSeat.id(),
totalDifficulty, baseDifficulty, itemBonus
sender.getName().getString(),
targetSeat.id(),
totalDifficulty,
baseDifficulty,
itemBonus
);
// Difficulty 0: immediate success — unlock + dismount + consume lockpick
if (totalDifficulty == 0) {
completeLockpickSuccess(sender, furnitureEntity, provider, targetSeat, passenger);
completeLockpickSuccess(
sender,
furnitureEntity,
provider,
targetSeat,
passenger
);
return;
}
// Respect server config: if lockpick minigame is disabled, skip
if (!com.tiedup.remake.core.ModConfig.SERVER.lockpickMiniGameEnabled.get()) {
if (
!com.tiedup.remake.core.ModConfig.SERVER.lockpickMiniGameEnabled.get()
) {
TiedUpMod.LOGGER.debug(
"[PacketFurnitureEscape] Lockpick minigame disabled by server config"
);
@@ -298,11 +345,15 @@ public class PacketFurnitureEscape {
ItemStack lockpickStack = ItemLockpick.findLockpickInInventory(sender);
if (lockpickStack.isEmpty()) return; // double-check; hasLockpickInInventory passed above
int remainingUses = lockpickStack.getMaxDamage() - lockpickStack.getDamageValue();
int remainingUses =
lockpickStack.getMaxDamage() - lockpickStack.getDamageValue();
// Sweet spot width scales inversely with difficulty: harder locks = narrower sweet spot.
// Base width 0.15 at difficulty 1, down to 0.03 at MAX_DIFFICULTY.
float sweetSpotWidth = Math.max(0.03f, 0.15f - (totalDifficulty / (float) MAX_DIFFICULTY) * 0.12f);
float sweetSpotWidth = Math.max(
0.03f,
0.15f - (totalDifficulty / (float) MAX_DIFFICULTY) * 0.12f
);
// Start lockpick session via LockpickSessionManager.
// The existing lockpick session uses a targetSlot (BodyRegionV2 ordinal) for
@@ -311,7 +362,8 @@ public class PacketFurnitureEscape {
// For now, we use the simplified approach: start the session and let the existing
// PacketLockpickAttempt handler manage the sweet-spot interaction. On success,
// the furniture-specific completion is handled by a post-session check.
LockpickSessionManager lockpickManager = LockpickSessionManager.getInstance();
LockpickSessionManager lockpickManager =
LockpickSessionManager.getInstance();
LockpickMiniGameState session = lockpickManager.startLockpickSession(
sender,
furnitureEntity.getId(), // repurpose targetSlot as entity ID
@@ -350,8 +402,11 @@ public class PacketFurnitureEscape {
TiedUpMod.LOGGER.info(
"[PacketFurnitureEscape] {} started lockpick on seat '{}' of furniture {} (difficulty {}, sweet spot width {})",
sender.getName().getString(), targetSeat.id(),
furnitureEntity.getId(), totalDifficulty, sweetSpotWidth
sender.getName().getString(),
targetSeat.id(),
furnitureEntity.getId(),
totalDifficulty,
sweetSpotWidth
);
}
@@ -368,7 +423,9 @@ public class PacketFurnitureEscape {
) {
provider.setSeatLocked(targetSeat.id(), false);
if (passenger instanceof ServerPlayer passengerPlayer) {
passengerPlayer.getPersistentData().remove("tiedup_locked_furniture");
passengerPlayer
.getPersistentData()
.remove("tiedup_locked_furniture");
}
if (passenger != null) {
passenger.stopRiding();
@@ -378,7 +435,9 @@ public class PacketFurnitureEscape {
ItemStack lockpickStack = ItemLockpick.findLockpickInInventory(sender);
if (!lockpickStack.isEmpty()) {
lockpickStack.setDamageValue(lockpickStack.getDamageValue() + 1);
if (lockpickStack.getDamageValue() >= lockpickStack.getMaxDamage()) {
if (
lockpickStack.getDamageValue() >= lockpickStack.getMaxDamage()
) {
lockpickStack.shrink(1);
}
}
@@ -390,7 +449,9 @@ public class PacketFurnitureEscape {
TiedUpMod.LOGGER.info(
"[PacketFurnitureEscape] {} lockpicked seat '{}' on furniture {} (difficulty was 0)",
sender.getName().getString(), targetSeat.id(), furnitureEntity.getId()
sender.getName().getString(),
targetSeat.id(),
furnitureEntity.getId()
);
}
@@ -411,8 +472,11 @@ public class PacketFurnitureEscape {
return 0;
}
Set<BodyRegionV2> blockedRegions = provider.getBlockedRegions(seat.id());
Map<BodyRegionV2, ItemStack> equipped = V2EquipmentHelper.getAllEquipped(passenger);
Set<BodyRegionV2> blockedRegions = provider.getBlockedRegions(
seat.id()
);
Map<BodyRegionV2, ItemStack> equipped =
V2EquipmentHelper.getAllEquipped(passenger);
int bonus = 0;
for (Map.Entry<BodyRegionV2, ItemStack> entry : equipped.entrySet()) {
@@ -436,7 +500,9 @@ public class PacketFurnitureEscape {
if (stack.getItem() instanceof ItemLockpick) return true;
}
// Also check offhand
if (player.getOffhandItem().getItem() instanceof ItemLockpick) return true;
if (
player.getOffhandItem().getItem() instanceof ItemLockpick
) return true;
return false;
}
@@ -526,7 +592,9 @@ public class PacketFurnitureEscape {
String seatId
) {
for (Entity passenger : furnitureEntity.getPassengers()) {
SeatDefinition passengerSeat = provider.getSeatForPassenger(passenger);
SeatDefinition passengerSeat = provider.getSeatForPassenger(
passenger
);
if (passengerSeat != null && passengerSeat.id().equals(seatId)) {
return passenger;
}

View File

@@ -1,11 +1,11 @@
package com.tiedup.remake.v2.furniture.network;
import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.v2.BodyRegionV2;
import com.tiedup.remake.items.base.ItemCollar;
import com.tiedup.remake.network.PacketRateLimiter;
import com.tiedup.remake.state.IBondageState;
import com.tiedup.remake.util.KidnappedHelper;
import com.tiedup.remake.v2.BodyRegionV2;
import com.tiedup.remake.v2.furniture.EntityFurniture;
import com.tiedup.remake.v2.furniture.FurnitureDefinition;
import com.tiedup.remake.v2.furniture.FurnitureFeedback;
@@ -48,7 +48,10 @@ public class PacketFurnitureForcemount {
// ==================== Codec ====================
public static void encode(PacketFurnitureForcemount msg, FriendlyByteBuf buf) {
public static void encode(
PacketFurnitureForcemount msg,
FriendlyByteBuf buf
) {
buf.writeInt(msg.furnitureEntityId);
buf.writeUUID(msg.captiveUUID);
}
@@ -68,7 +71,10 @@ public class PacketFurnitureForcemount {
ctx.setPacketHandled(true);
}
private static void handleOnServer(PacketFurnitureForcemount msg, NetworkEvent.Context ctx) {
private static void handleOnServer(
PacketFurnitureForcemount msg,
NetworkEvent.Context ctx
) {
ServerPlayer sender = ctx.getSender();
if (sender == null) return;
@@ -126,8 +132,10 @@ public class PacketFurnitureForcemount {
}
ItemStack collarStack = captiveState.getEquipment(BodyRegionV2.NECK);
if (collarStack.isEmpty()
|| !(collarStack.getItem() instanceof ItemCollar collar)) {
if (
collarStack.isEmpty() ||
!(collarStack.getItem() instanceof ItemCollar collar)
) {
TiedUpMod.LOGGER.debug(
"[PacketFurnitureForcemount] Invalid collar item on captive"
);
@@ -174,13 +182,21 @@ public class PacketFurnitureForcemount {
FurnitureFeedback feedback = def.feedback();
ResourceLocation mountSoundRL = feedback.mountSound();
if (mountSoundRL != null) {
SoundEvent sound = SoundEvent.createVariableRangeEvent(mountSoundRL);
entity.level().playSound(
null,
entity.getX(), entity.getY(), entity.getZ(),
sound, SoundSource.BLOCKS,
1.0f, 1.0f
SoundEvent sound = SoundEvent.createVariableRangeEvent(
mountSoundRL
);
entity
.level()
.playSound(
null,
entity.getX(),
entity.getY(),
entity.getZ(),
sound,
SoundSource.BLOCKS,
1.0f,
1.0f
);
}
}
@@ -202,15 +218,20 @@ public class PacketFurnitureForcemount {
* Find a living entity by UUID within a reasonable range of the sender.
* Checks players first (O(1) lookup), then falls back to entity search.
*/
private static LivingEntity findCaptiveByUUID(ServerPlayer sender, UUID uuid) {
private static LivingEntity findCaptiveByUUID(
ServerPlayer sender,
UUID uuid
) {
// Try player lookup first (fast)
net.minecraft.world.entity.player.Player player =
sender.level().getPlayerByUUID(uuid);
net.minecraft.world.entity.player.Player player = sender
.level()
.getPlayerByUUID(uuid);
if (player != null) return player;
// Search nearby entities (64 block radius)
AABB searchBox = sender.getBoundingBox().inflate(64);
for (LivingEntity nearby : sender.level()
for (LivingEntity nearby : sender
.level()
.getEntitiesOfClass(LivingEntity.class, searchBox)) {
if (nearby.getUUID().equals(uuid)) {
return nearby;
@@ -224,12 +245,20 @@ public class PacketFurnitureForcemount {
*
* @return the seat ID, or null if all seats are occupied
*/
private static String findFirstAvailableSeat(ISeatProvider provider, Entity furniture) {
private static String findFirstAvailableSeat(
ISeatProvider provider,
Entity furniture
) {
for (SeatDefinition seat : provider.getSeats()) {
boolean occupied = false;
for (Entity passenger : furniture.getPassengers()) {
SeatDefinition passengerSeat = provider.getSeatForPassenger(passenger);
if (passengerSeat != null && passengerSeat.id().equals(seat.id())) {
SeatDefinition passengerSeat = provider.getSeatForPassenger(
passenger
);
if (
passengerSeat != null &&
passengerSeat.id().equals(seat.id())
) {
occupied = true;
break;
}

View File

@@ -63,7 +63,10 @@ public class PacketFurnitureLock {
ctx.setPacketHandled(true);
}
private static void handleOnServer(PacketFurnitureLock msg, NetworkEvent.Context ctx) {
private static void handleOnServer(
PacketFurnitureLock msg,
NetworkEvent.Context ctx
) {
ServerPlayer sender = ctx.getSender();
if (sender == null) return;
@@ -78,8 +81,9 @@ public class PacketFurnitureLock {
if (!entity.isAlive() || entity.isRemoved()) return;
// Sender must hold a key item in either hand
boolean hasKey = (sender.getMainHandItem().getItem() instanceof ItemMasterKey)
|| (sender.getOffhandItem().getItem() instanceof ItemMasterKey);
boolean hasKey =
(sender.getMainHandItem().getItem() instanceof ItemMasterKey) ||
(sender.getOffhandItem().getItem() instanceof ItemMasterKey);
if (!hasKey) {
TiedUpMod.LOGGER.debug(
"[PacketFurnitureLock] {} does not hold a key item in either hand",
@@ -93,7 +97,8 @@ public class PacketFurnitureLock {
if (seat == null) {
TiedUpMod.LOGGER.debug(
"[PacketFurnitureLock] Seat '{}' not found on entity {}",
msg.seatId, msg.entityId
msg.seatId,
msg.entityId
);
return;
}
@@ -135,22 +140,32 @@ public class PacketFurnitureLock {
? feedback.unlockSound()
: feedback.lockSound();
if (soundRL != null) {
SoundEvent sound = SoundEvent.createVariableRangeEvent(soundRL);
entity.level().playSound(
null, // null = play for all nearby players
entity.getX(), entity.getY(), entity.getZ(),
sound, SoundSource.BLOCKS,
1.0f, 1.0f
SoundEvent sound = SoundEvent.createVariableRangeEvent(
soundRL
);
entity
.level()
.playSound(
null, // null = play for all nearby players
entity.getX(),
entity.getY(),
entity.getZ(),
sound,
SoundSource.BLOCKS,
1.0f,
1.0f
);
}
}
// Set lock/unlock animation state. The next updateAnimState() call
// (from tick or passenger change) will reset it to OCCUPIED/IDLE.
boolean nowLocked = !wasLocked;
furniture.setAnimState(nowLocked
? EntityFurniture.STATE_LOCKING
: EntityFurniture.STATE_UNLOCKING);
furniture.setAnimState(
nowLocked
? EntityFurniture.STATE_LOCKING
: EntityFurniture.STATE_UNLOCKING
);
// Broadcast updated state to all tracking clients
PacketSyncFurnitureState.sendToTracking(furniture);
@@ -162,7 +177,10 @@ public class PacketFurnitureLock {
/**
* Find a SeatDefinition by ID from the provider's seat list.
*/
private static SeatDefinition findSeatById(ISeatProvider provider, String seatId) {
private static SeatDefinition findSeatById(
ISeatProvider provider,
String seatId
) {
for (SeatDefinition seat : provider.getSeats()) {
if (seat.id().equals(seatId)) return seat;
}
@@ -178,7 +196,9 @@ public class PacketFurnitureLock {
String seatId
) {
for (Entity passenger : furnitureEntity.getPassengers()) {
SeatDefinition passengerSeat = provider.getSeatForPassenger(passenger);
SeatDefinition passengerSeat = provider.getSeatForPassenger(
passenger
);
if (passengerSeat != null && passengerSeat.id().equals(seatId)) {
return true;
}

View File

@@ -40,7 +40,9 @@ import org.apache.logging.log4j.Logger;
*/
public class PacketSyncFurnitureDefinitions {
private static final Logger LOGGER = LogManager.getLogger("PacketSyncFurnitureDefinitions");
private static final Logger LOGGER = LogManager.getLogger(
"PacketSyncFurnitureDefinitions"
);
/**
* Safety cap on the number of definitions to prevent memory exhaustion
@@ -65,13 +67,18 @@ public class PacketSyncFurnitureDefinitions {
private final Map<ResourceLocation, FurnitureDefinition> definitions;
public PacketSyncFurnitureDefinitions(Map<ResourceLocation, FurnitureDefinition> definitions) {
public PacketSyncFurnitureDefinitions(
Map<ResourceLocation, FurnitureDefinition> definitions
) {
this.definitions = definitions;
}
// ==================== Codec ====================
public static void encode(PacketSyncFurnitureDefinitions msg, FriendlyByteBuf buf) {
public static void encode(
PacketSyncFurnitureDefinitions msg,
FriendlyByteBuf buf
) {
buf.writeVarInt(msg.definitions.size());
for (FurnitureDefinition def : msg.definitions.values()) {
@@ -91,7 +98,9 @@ public class PacketSyncFurnitureDefinitions {
// Tint channels
buf.writeVarInt(def.tintChannels().size());
for (Map.Entry<String, Integer> entry : def.tintChannels().entrySet()) {
for (Map.Entry<String, Integer> entry : def
.tintChannels()
.entrySet()) {
buf.writeUtf(entry.getKey());
buf.writeInt(entry.getValue());
}
@@ -138,7 +147,10 @@ public class PacketSyncFurnitureDefinitions {
buf.writeBoolean(seat.itemDifficultyBonus());
}
private static void encodeFeedback(FurnitureFeedback feedback, FriendlyByteBuf buf) {
private static void encodeFeedback(
FurnitureFeedback feedback,
FriendlyByteBuf buf
) {
writeOptionalRL(buf, feedback.mountSound());
writeOptionalRL(buf, feedback.lockSound());
writeOptionalRL(buf, feedback.unlockSound());
@@ -147,7 +159,10 @@ public class PacketSyncFurnitureDefinitions {
writeOptionalRL(buf, feedback.deniedSound());
}
private static void writeOptionalRL(FriendlyByteBuf buf, ResourceLocation rl) {
private static void writeOptionalRL(
FriendlyByteBuf buf,
ResourceLocation rl
) {
buf.writeBoolean(rl != null);
if (rl != null) {
buf.writeResourceLocation(rl);
@@ -203,11 +218,23 @@ public class PacketSyncFurnitureDefinitions {
ResourceLocation icon = readOptionalRL(buf);
FurnitureDefinition def = new FurnitureDefinition(
id, displayName, translationKey, modelLocation,
Map.copyOf(tintChannels), supportsColor,
hitboxWidth, hitboxHeight, snapToWall, floorOnly,
lockable, breakResistance, dropOnBreak,
List.copyOf(seats), feedback, category, icon
id,
displayName,
translationKey,
modelLocation,
Map.copyOf(tintChannels),
supportsColor,
hitboxWidth,
hitboxHeight,
snapToWall,
floorOnly,
lockable,
breakResistance,
dropOnBreak,
List.copyOf(seats),
feedback,
category,
icon
);
defs.put(id, def);
@@ -235,8 +262,12 @@ public class PacketSyncFurnitureDefinitions {
boolean itemDifficultyBonus = buf.readBoolean();
return new SeatDefinition(
id, armatureName, Set.copyOf(blockedRegions),
lockable, lockedDifficulty, itemDifficultyBonus
id,
armatureName,
Set.copyOf(blockedRegions),
lockable,
lockedDifficulty,
itemDifficultyBonus
);
}
@@ -249,8 +280,12 @@ public class PacketSyncFurnitureDefinitions {
ResourceLocation deniedSound = readOptionalRL(buf);
return new FurnitureFeedback(
mountSound, lockSound, unlockSound,
struggleLoopSound, escapeSound, deniedSound
mountSound,
lockSound,
unlockSound,
struggleLoopSound,
escapeSound,
deniedSound
);
}
@@ -276,8 +311,10 @@ public class PacketSyncFurnitureDefinitions {
@OnlyIn(Dist.CLIENT)
private static void handleOnClient(PacketSyncFurnitureDefinitions msg) {
FurnitureRegistry.reload(msg.definitions);
LOGGER.debug("Client received {} furniture definitions from server",
msg.definitions.size());
LOGGER.debug(
"Client received {} furniture definitions from server",
msg.definitions.size()
);
}
// ==================== Server-side Helpers ====================

View File

@@ -33,7 +33,11 @@ public class PacketSyncFurnitureState {
private final byte lockBits;
private final byte animState;
public PacketSyncFurnitureState(int entityId, byte lockBits, byte animState) {
public PacketSyncFurnitureState(
int entityId,
byte lockBits,
byte animState
) {
this.entityId = entityId;
this.lockBits = lockBits;
this.animState = animState;
@@ -41,7 +45,10 @@ public class PacketSyncFurnitureState {
// ==================== Codec ====================
public static void encode(PacketSyncFurnitureState msg, FriendlyByteBuf buf) {
public static void encode(
PacketSyncFurnitureState msg,
FriendlyByteBuf buf
) {
buf.writeInt(msg.entityId);
buf.writeByte(msg.lockBits);
buf.writeByte(msg.animState);