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:
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user