Skip to content

Commit b222c74

Browse files
committed
chore: update mod version to 2.2.1 and enhance PlayerPreset validation
1 parent 6c7f874 commit b222c74

File tree

5 files changed

+106
-85
lines changed

5 files changed

+106
-85
lines changed

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ org.gradle.parallel=true
44
org.gradle.configuration-cache=true
55

66
# Mod properties
7-
mod.version=2.2.0
7+
mod.version=2.2.1
88
mod.group=com.github.kd_gaming1
99
mod.id=scaleme
1010
mod.name=ScaleMe

src/main/java/com/github/kd_gaming1/scaleme/client/data/PlayerPreset.java

Lines changed: 31 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -7,118 +7,96 @@
77
import java.util.Objects;
88
import java.util.UUID;
99

10-
/**
11-
* Represents a scaling preset for a specific player.
12-
* <p>
13-
* Each preset contains:
14-
* <ul>
15-
* <li>An identifier (UUID or username)</li>
16-
* <li>An optional friendly display name</li>
17-
* <li>A scale factor for the player</li>
18-
* <li>An enabled/disabled state</li>
19-
* </ul>
20-
* <p>
21-
* Presets are immutable after creation, use {@link #copy()} to create modified versions.
22-
*/
2310
public class PlayerPreset {
2411

2512
@SerializedName("identifier")
26-
public String identifier; // Package-private for manager access
13+
public String identifier;
2714

2815
@SerializedName("friendlyName")
29-
public String friendlyName; // Package-private for manager access
16+
public String friendlyName;
3017

3118
@SerializedName("scale")
32-
public float scale; // Package-private for manager access
19+
public float scale;
3320

3421
@SerializedName("enabled")
35-
public boolean enabled; // Package-private for manager access
22+
public boolean enabled;
3623

3724
/**
3825
* Creates a new player preset with validation.
3926
* @param identifier The player identifier (UUID or username)
4027
* @param friendlyName Optional display name (can be null)
4128
* @param scale The scale factor
42-
* @throws IllegalArgumentException if identifier is invalid or scale is out of range
29+
* @throws IllegalArgumentException if scale is out of range
4330
*/
4431
public PlayerPreset(String identifier, String friendlyName, float scale) {
45-
validateIdentifier(identifier);
46-
validateScale(scale);
47-
48-
this.identifier = identifier.trim();
32+
// Allow empty identifier for draft presets
33+
// Validation will occur during save operation
34+
this.identifier = identifier != null ? identifier.trim() : "";
4935
this.friendlyName = (friendlyName != null && !friendlyName.trim().isEmpty())
5036
? friendlyName.trim() : null;
37+
38+
validateScale(scale);
5139
this.scale = ScaleConstants.clampScale(scale);
5240
this.enabled = true;
5341
}
5442

55-
// ===== Validation Helper Methods =====
43+
/**
44+
* Validates that this preset is ready to be saved.
45+
* @throws IllegalArgumentException if preset is invalid
46+
*/
47+
public void validateForSave() {
48+
validateIdentifier(this.identifier);
49+
validateScale(this.scale);
50+
}
5651

5752
/**
58-
* Validates and sets the scale value.
59-
* @param scale The new scale value
60-
* @throws IllegalArgumentException if scale is invalid
53+
* Checks if this preset is valid for saving.
54+
* @return true if valid, false otherwise
6155
*/
56+
public boolean isValidForSave() {
57+
try {
58+
validateForSave();
59+
return true;
60+
} catch (IllegalArgumentException e) {
61+
return false;
62+
}
63+
}
64+
65+
// ===== Validation Helper Methods =====
66+
6267
public void setScaleValidated(float scale) {
6368
validateScale(scale);
6469
this.scale = ScaleConstants.clampScale(scale);
6570
}
6671

67-
/**
68-
* Validates and sets the identifier.
69-
* @param identifier The new identifier
70-
* @throws IllegalArgumentException if identifier is invalid
71-
*/
7272
public void setIdentifierValidated(String identifier) {
7373
validateIdentifier(identifier);
7474
this.identifier = identifier.trim();
7575
}
7676

7777
// ===== Core Methods =====
7878

79-
/**
80-
* Creates a deep copy of this preset.
81-
* @return A new PlayerPreset with identical values
82-
*/
8379
public PlayerPreset copy() {
8480
PlayerPreset copy = new PlayerPreset(this.identifier, this.friendlyName, this.scale);
8581
copy.enabled = this.enabled;
8682
return copy;
8783
}
8884

89-
/**
90-
* Returns the display name for this preset.
91-
* Uses friendly name if available, otherwise falls back to identifier.
92-
* @return The effective display name
93-
*/
9485
public String getEffectiveDisplayName() {
9586
return (friendlyName != null && !friendlyName.isEmpty())
9687
? friendlyName
9788
: identifier;
9889
}
9990

100-
/**
101-
* Checks if this preset matches a given identifier (case-insensitive).
102-
* Only returns true if the preset is enabled.
103-
* @param id The identifier to check
104-
* @return true if enabled and identifier matches
105-
*/
10691
public boolean matches(String id) {
10792
return enabled && identifier.equalsIgnoreCase(id);
10893
}
10994

110-
/**
111-
* Checks if this preset matches a player by UUID or username.
112-
* @param playerUUID The player's UUID
113-
* @param playerName The player's name (can be null)
114-
* @return true if enabled and matches either UUID or username
115-
*/
11695
public boolean matchesPlayer(UUID playerUUID, String playerName) {
11796
if (!enabled || playerUUID == null) {
11897
return false;
11998
}
12099

121-
// Check UUID match if identifier is a UUID
122100
if (isUUID()) {
123101
try {
124102
return UUID.fromString(identifier).equals(playerUUID);
@@ -127,32 +105,19 @@ public boolean matchesPlayer(UUID playerUUID, String playerName) {
127105
}
128106
}
129107

130-
// Check username match (case insensitive)
131108
return playerName != null && identifier.equalsIgnoreCase(playerName);
132109
}
133110

134-
/**
135-
* Attempts to resolve this preset's identifier to a UUID.
136-
* @return The resolved UUID, or null if resolution fails
137-
*/
138111
public UUID resolveToUUID() {
139112
return PlayerUUIDResolver.resolvePlayerUUID(this.identifier);
140113
}
141114

142115
// ===== Validation Methods =====
143116

144-
/**
145-
* Checks if the identifier is in UUID format.
146-
* @return true if identifier matches UUID pattern
147-
*/
148117
public boolean isUUID() {
149118
return identifier != null && identifier.matches(ScaleConstants.UUID_REGEX);
150119
}
151120

152-
/**
153-
* Checks if the identifier is in username format.
154-
* @return true if identifier matches username pattern
155-
*/
156121
public boolean isUsername() {
157122
return identifier != null && identifier.matches(ScaleConstants.USERNAME_REGEX);
158123
}
@@ -168,11 +133,6 @@ private void validateIdentifier(String identifier) {
168133
}
169134
}
170135

171-
/**
172-
* Validates that a scale value is within acceptable range.
173-
* @param scale The scale value to validate
174-
* @throws IllegalArgumentException if scale is invalid
175-
*/
176136
private void validateScale(float scale) {
177137
if (!ScaleConstants.isValidScale(scale)) {
178138
throw new IllegalArgumentException(

src/main/java/com/github/kd_gaming1/scaleme/client/gui/components/PresetEditorComponent.java

Lines changed: 63 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.github.kd_gaming1.scaleme.client.data.PlayerPreset;
44
import com.github.kd_gaming1.scaleme.client.util.PlayerPresetManager;
55
import com.github.kd_gaming1.scaleme.client.util.PlayerUUIDResolver;
6+
import com.github.kd_gaming1.scaleme.client.util.ScaleConstants;
67
import io.wispforest.owo.ui.component.ButtonComponent;
78
import io.wispforest.owo.ui.component.Components;
89
import io.wispforest.owo.ui.component.LabelComponent;
@@ -465,21 +466,38 @@ private void validateIdentifierFormat(String identifier) {
465466
}
466467

467468
String trimmed = identifier.trim();
469+
470+
// Check for valid UUID format
468471
boolean isUUIDFormat = isValidUUID(trimmed);
469-
boolean isUsernameFormat = trimmed.matches("^[a-zA-Z0-9_]{3,16}$");
472+
473+
// Check for valid username format (3-16 chars, alphanumeric + underscore)
474+
boolean isUsernameFormat = trimmed.matches(ScaleConstants.USERNAME_REGEX);
470475

471476
if (this.isNewPreset) {
472477
this.isIdentifierValid = isUsernameFormat;
473-
this.statusLabel.text(Text.literal(isUsernameFormat
474-
? "Username format - will resolve when saved"
475-
: "Only usernames (3-16 chars) are allowed when creating a new preset.")
476-
.formatted(isUsernameFormat ? Formatting.YELLOW : Formatting.RED));
478+
479+
String message;
480+
Formatting color;
481+
482+
if (isUsernameFormat) {
483+
message = "Username format valid - will resolve when saved";
484+
color = Formatting.YELLOW;
485+
} else if (trimmed.length() < 3) {
486+
message = "Username too short (minimum 3 characters)";
487+
color = Formatting.RED;
488+
} else if (trimmed.length() > 16) {
489+
message = "Username too long (maximum 16 characters)";
490+
color = Formatting.RED;
491+
} else {
492+
message = "Invalid characters - use only letters, numbers, and underscores";
493+
color = Formatting.RED;
494+
}
495+
496+
this.statusLabel.text(Text.literal(message).formatted(color));
477497
validateField(this.identifierField, this.isIdentifierValid, 0xFFFFFFFF, 0xFFEB1D36);
478498
} else {
479499
this.isIdentifierValid = isUUIDFormat || isUsernameFormat;
480-
this.statusLabel.text(Text.literal(isUUIDFormat
481-
? "Valid UUID format"
482-
: "Username format")
500+
this.statusLabel.text(Text.literal(isUUIDFormat ? "Valid UUID format" : "Username format")
483501
.formatted(Formatting.GREEN));
484502
}
485503
}
@@ -512,7 +530,15 @@ private void updateScaleValueLabel(float scale) {
512530

513531
private void onIdentifierChanged(String newValue) {
514532
if (!this.isNewPreset) return;
515-
validateIdentifierFormat(newValue);
533+
534+
// Sanitize input: remove spaces and invalid characters
535+
String sanitized = newValue.replaceAll("[^a-zA-Z0-9_]", "");
536+
if (!sanitized.equals(newValue)) {
537+
this.identifierField.text(sanitized);
538+
return; // Will trigger this method again with sanitized value
539+
}
540+
541+
validateIdentifierFormat(sanitized);
516542
this.hasUnsavedChanges = true;
517543
updateButtonStates();
518544
}
@@ -564,15 +590,34 @@ private void updateButtonStates() {
564590
}
565591

566592
private void saveCurrentPreset() {
567-
if (this.currentPreset == null || !this.isIdentifierValid) return;
593+
if (this.currentPreset == null) return;
594+
595+
// Validate the preset before saving
596+
try {
597+
this.currentPreset.validateForSave();
598+
} catch (IllegalArgumentException e) {
599+
showErrorMessage("Cannot save: " + e.getMessage());
600+
return;
601+
}
602+
603+
if (!this.isIdentifierValid) {
604+
showErrorMessage("Please provide a valid player identifier");
605+
return;
606+
}
568607

569608
if (this.isNewPreset) {
570609
String inputIdentifier = this.identifierField.getText().trim();
571610

611+
// Additional validation for username format
612+
if (!inputIdentifier.matches(ScaleConstants.USERNAME_REGEX)) {
613+
showErrorMessage("Invalid username format. Use 3-16 alphanumeric characters or underscores.");
614+
return;
615+
}
616+
572617
// Resolve UUID from username
573618
UUID resolvedUUID = PlayerUUIDResolver.resolvePlayerUUID(inputIdentifier);
574619
if (resolvedUUID == null) {
575-
showErrorMessage("Failed to resolve player: " + inputIdentifier + ". Please check the username.");
620+
showErrorMessage("Failed to resolve player: " + inputIdentifier + ". Player must be online or check the username.");
576621
return;
577622
}
578623

@@ -584,6 +629,12 @@ private void saveCurrentPreset() {
584629
}
585630
}
586631

632+
// Final validation before saving
633+
if (!this.currentPreset.isValidForSave()) {
634+
showErrorMessage("Preset contains invalid data and cannot be saved");
635+
return;
636+
}
637+
587638
// Save the preset
588639
PlayerPresetManager.addOrUpdatePreset(this.currentPreset);
589640

@@ -601,7 +652,7 @@ private void saveCurrentPreset() {
601652
updateButtonStates();
602653
updateTitle();
603654
updateStatusLabel();
604-
showEditorState(); // Refresh to hide help container and update info
655+
showEditorState();
605656

606657
if (this.onPresetUpdated != null) {
607658
this.onPresetUpdated.accept(this.currentPreset);

src/main/java/com/github/kd_gaming1/scaleme/client/util/PlayerPresetManager.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,14 @@ public static void addOrUpdatePreset(PlayerPreset preset) {
138138
return;
139139
}
140140

141+
// Validate before saving
142+
try {
143+
preset.validateForSave();
144+
} catch (IllegalArgumentException e) {
145+
LOGGER.error("Preset validation failed: {}", e.getMessage());
146+
throw new IllegalArgumentException("Cannot save invalid preset: " + e.getMessage(), e);
147+
}
148+
141149
// Remove existing preset with same identifier
142150
removePresetQuiet(preset.identifier);
143151

src/main/java/com/github/kd_gaming1/scaleme/client/util/ScaleManager.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,11 @@ public static void tick() {
2828
* Get current scale for a player.
2929
*/
3030
public static float getCurrentScale(UUID playerUUID) {
31+
if (playerUUID == null) {
32+
return ScaleConstants.DEFAULT_SCALE;
33+
}
3134
return PlayerPresetManager.getCurrentScale(playerUUID);
3235
}
33-
3436
/** Returns NPC scale with safety check including dungeons detection. */
3537
public static float getNpcScale() {
3638
// Only apply scaling when it's safe (not in competitive modes or dungeons)

0 commit comments

Comments
 (0)