Skip to content

Commit 319a896

Browse files
committed
Add player presets feature with command support and UI integration 'P' to open
1 parent 7f37147 commit 319a896

File tree

13 files changed

+2162
-4
lines changed

13 files changed

+2162
-4
lines changed

build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ dependencies {
3636
modImplementation include ("maven.modrinth:midnightlib:${project.midnightlib_version}")
3737

3838
modImplementation("com.terraformersmc:modmenu:${project.modmenu_version}")
39+
40+
implementation 'com.google.code.gson:gson:2.10.1'
3941
}
4042

4143
// Add this mixin configuration block

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ minecraft_version=1.21.5
66
yarn_mappings=1.21.5+build.1
77
loader_version=0.16.14
88
# Mod Properties
9-
mod_version=1.0.1
9+
mod_version=1.1.0
1010
maven_group=com.github
1111
archives_base_name=scaleme
1212
# Dependencies

src/main/java/com/github/scaleme/Scaleme.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@
44
import eu.midnightdust.lib.config.MidnightConfig;
55
import net.fabricmc.api.ModInitializer;
66

7+
import org.slf4j.Logger;
8+
import org.slf4j.LoggerFactory;
9+
710
public class Scaleme implements ModInitializer {
811
public static final String MOD_ID = "scaleme";
12+
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
913

1014
@Override
1115
public void onInitialize() {

src/main/java/com/github/scaleme/client/ScalemeClient.java

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
package com.github.scaleme.client;
22

33
import com.github.scaleme.Scaleme;
4+
import com.github.scaleme.client.command.PresetCommand;
5+
import com.github.scaleme.client.gui.PlayerPresetScreen;
46
import com.github.scaleme.client.util.ScaleManager;
57
import com.github.scaleme.config.ScaleMeConfig;
68
import eu.midnightdust.lib.config.MidnightConfig;
79
import net.fabricmc.api.ClientModInitializer;
10+
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
811
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
912
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
1013
import net.minecraft.client.option.KeyBinding;
@@ -14,26 +17,47 @@
1417
public class ScalemeClient implements ClientModInitializer {
1518

1619
private static KeyBinding openConfigKey;
20+
private static KeyBinding openPresetsKey;
1721

1822
@Override
1923
public void onInitializeClient() {
2024
MidnightConfig.init(Scaleme.MOD_ID, ScaleMeConfig.class);
2125

22-
// Register keybinding for opening config
26+
// Register keybindings
2327
openConfigKey = KeyBindingHelper.registerKeyBinding(new KeyBinding(
2428
"key.scaleme.open_config",
2529
InputUtil.Type.KEYSYM,
2630
GLFW.GLFW_KEY_O,
2731
"category.scaleme.general"
2832
));
2933

30-
// Handle key press
34+
openPresetsKey = KeyBindingHelper.registerKeyBinding(new KeyBinding(
35+
"key.scaleme.open_presets",
36+
InputUtil.Type.KEYSYM,
37+
GLFW.GLFW_KEY_P,
38+
"category.scaleme.general"
39+
));
40+
41+
// Register commands
42+
ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> {
43+
PresetCommand.register(dispatcher);
44+
});
45+
46+
// Handle key presses
3147
ClientTickEvents.END_CLIENT_TICK.register(client -> {
48+
// Config menu
3249
while (openConfigKey.wasPressed()) {
3350
if (client.player != null) {
3451
client.setScreen(MidnightConfig.getScreen(client.currentScreen, Scaleme.MOD_ID));
3552
}
3653
}
54+
55+
// Presets menu
56+
while (openPresetsKey.wasPressed()) {
57+
if (client.player != null) {
58+
client.setScreen(new PlayerPresetScreen(client.currentScreen));
59+
}
60+
}
3761
});
3862

3963
ScaleManager.init();
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
package com.github.scaleme.client.command;
2+
3+
import com.github.scaleme.client.data.PlayerPreset;
4+
import com.github.scaleme.client.util.PlayerPresetManager;
5+
import com.mojang.brigadier.CommandDispatcher;
6+
import com.mojang.brigadier.arguments.FloatArgumentType;
7+
import com.mojang.brigadier.arguments.StringArgumentType;
8+
import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
9+
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
10+
import net.minecraft.text.Text;
11+
import net.minecraft.util.Formatting;
12+
13+
public class PresetCommand {
14+
15+
public static void register(CommandDispatcher<FabricClientCommandSource> dispatcher) {
16+
dispatcher.register(ClientCommandManager.literal("scalepreset")
17+
.then(ClientCommandManager.literal("add")
18+
.then(ClientCommandManager.argument("identifier", StringArgumentType.word())
19+
.then(ClientCommandManager.argument("scale", FloatArgumentType.floatArg(0.1f, 3.0f))
20+
.executes(context -> {
21+
String identifier = StringArgumentType.getString(context, "identifier");
22+
float scale = FloatArgumentType.getFloat(context, "scale");
23+
24+
// Create preset with identifier as display name and default category
25+
PlayerPreset preset = new PlayerPreset(identifier, null, scale, "command");
26+
27+
PlayerPresetManager.addPreset(preset);
28+
29+
context.getSource().sendFeedback(Text.literal("Added preset for: " + identifier + " (Scale: " + scale + "x)")
30+
.formatted(Formatting.GREEN));
31+
32+
return 1;
33+
})
34+
.then(ClientCommandManager.argument("displayName", StringArgumentType.greedyString())
35+
.executes(context -> {
36+
String identifier = StringArgumentType.getString(context, "identifier");
37+
float scale = FloatArgumentType.getFloat(context, "scale");
38+
String displayName = StringArgumentType.getString(context, "displayName");
39+
40+
PlayerPreset preset = new PlayerPreset(identifier, displayName, scale, "command");
41+
42+
PlayerPresetManager.addPreset(preset);
43+
44+
context.getSource().sendFeedback(Text.literal("Added preset: " + displayName + " for " + identifier + " (Scale: " + scale + "x)")
45+
.formatted(Formatting.GREEN));
46+
47+
return 1;
48+
})))))
49+
.then(ClientCommandManager.literal("remove")
50+
.then(ClientCommandManager.argument("identifier", StringArgumentType.greedyString())
51+
.executes(context -> {
52+
String identifier = StringArgumentType.getString(context, "identifier");
53+
54+
PlayerPresetManager.removePreset(identifier, isUUID(identifier));
55+
56+
context.getSource().sendFeedback(Text.literal("Removed preset for: " + identifier)
57+
.formatted(Formatting.YELLOW));
58+
59+
return 1;
60+
})))
61+
.then(ClientCommandManager.literal("list")
62+
.executes(context -> {
63+
var presets = PlayerPresetManager.getAllPresets();
64+
if (presets.isEmpty()) {
65+
context.getSource().sendFeedback(Text.literal("No presets found.")
66+
.formatted(Formatting.GRAY));
67+
} else {
68+
context.getSource().sendFeedback(Text.literal("Player Presets:")
69+
.formatted(Formatting.GOLD));
70+
for (PlayerPreset preset : presets) {
71+
String displayName = preset.getEffectiveDisplayName();
72+
String identifierType = preset.isUUID() ? "UUID" : "Username";
73+
String line = String.format("- %s (%s: %s) - %.1fx [%s] %s",
74+
displayName,
75+
identifierType,
76+
preset.identifier,
77+
preset.scale,
78+
preset.category,
79+
preset.enabled ? "[Enabled]" : "[Disabled]");
80+
context.getSource().sendFeedback(Text.literal(line)
81+
.formatted(preset.enabled ? Formatting.WHITE : Formatting.GRAY));
82+
}
83+
}
84+
return 1;
85+
}))
86+
.then(ClientCommandManager.literal("toggle")
87+
.then(ClientCommandManager.argument("identifier", StringArgumentType.greedyString())
88+
.executes(context -> {
89+
String identifier = StringArgumentType.getString(context, "identifier");
90+
91+
// Find the preset
92+
PlayerPreset foundPreset = null;
93+
for (PlayerPreset preset : PlayerPresetManager.getAllPresets()) {
94+
if (preset.identifier.equalsIgnoreCase(identifier)) {
95+
foundPreset = preset;
96+
break;
97+
}
98+
}
99+
100+
if (foundPreset != null) {
101+
// Toggle the enabled state
102+
foundPreset.enabled = !foundPreset.enabled;
103+
104+
// Remove and re-add to save changes
105+
PlayerPresetManager.removePreset(foundPreset.identifier, foundPreset.isUUID());
106+
PlayerPresetManager.addPreset(foundPreset);
107+
108+
String status = foundPreset.enabled ? "enabled" : "disabled";
109+
context.getSource().sendFeedback(Text.literal("Preset for " + identifier + " is now " + status)
110+
.formatted(foundPreset.enabled ? Formatting.GREEN : Formatting.RED));
111+
} else {
112+
context.getSource().sendFeedback(Text.literal("No preset found for: " + identifier)
113+
.formatted(Formatting.RED));
114+
}
115+
116+
return 1;
117+
})))
118+
.then(ClientCommandManager.literal("category")
119+
.then(ClientCommandManager.argument("identifier", StringArgumentType.word())
120+
.then(ClientCommandManager.argument("category", StringArgumentType.word())
121+
.executes(context -> {
122+
String identifier = StringArgumentType.getString(context, "identifier");
123+
String category = StringArgumentType.getString(context, "category");
124+
125+
// Find the preset
126+
PlayerPreset foundPreset = null;
127+
for (PlayerPreset preset : PlayerPresetManager.getAllPresets()) {
128+
if (preset.identifier.equalsIgnoreCase(identifier)) {
129+
foundPreset = preset;
130+
break;
131+
}
132+
}
133+
134+
if (foundPreset != null) {
135+
// Update category
136+
foundPreset.category = category;
137+
138+
// Remove and re-add to save changes
139+
PlayerPresetManager.removePreset(foundPreset.identifier, foundPreset.isUUID());
140+
PlayerPresetManager.addPreset(foundPreset);
141+
142+
context.getSource().sendFeedback(Text.literal("Updated category for " + identifier + " to: " + category)
143+
.formatted(Formatting.GREEN));
144+
} else {
145+
context.getSource().sendFeedback(Text.literal("No preset found for: " + identifier)
146+
.formatted(Formatting.RED));
147+
}
148+
149+
return 1;
150+
}))))
151+
.then(ClientCommandManager.literal("help")
152+
.executes(context -> {
153+
context.getSource().sendFeedback(Text.literal("ScaleMe Preset Commands:")
154+
.formatted(Formatting.GOLD));
155+
context.getSource().sendFeedback(Text.literal("/scalepreset add <identifier> <scale> [displayName] - Add a new preset"));
156+
context.getSource().sendFeedback(Text.literal("/scalepreset remove <identifier> - Remove a preset"));
157+
context.getSource().sendFeedback(Text.literal("/scalepreset list - List all presets"));
158+
context.getSource().sendFeedback(Text.literal("/scalepreset toggle <identifier> - Enable/disable a preset"));
159+
context.getSource().sendFeedback(Text.literal("/scalepreset category <identifier> <category> - Change preset category"));
160+
context.getSource().sendFeedback(Text.literal("/scalepreset help - Show this help"));
161+
context.getSource().sendFeedback(Text.literal("Scale range: 0.1x to 3.0x")
162+
.formatted(Formatting.GRAY));
163+
context.getSource().sendFeedback(Text.literal("UUIDs are automatically detected")
164+
.formatted(Formatting.GRAY));
165+
return 1;
166+
}))
167+
);
168+
}
169+
170+
private static boolean isUUID(String identifier) {
171+
// Simple UUID detection - 36 characters with hyphens in correct positions
172+
return identifier != null && identifier.matches("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$");
173+
}
174+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.github.scaleme.client.data;
2+
3+
import com.google.gson.annotations.SerializedName;
4+
5+
public class PlayerPreset {
6+
@SerializedName("identifier")
7+
public String identifier; // UUID or username
8+
9+
@SerializedName("displayName")
10+
public String displayName; // Optional friendly name for the preset
11+
12+
@SerializedName("scale")
13+
public float scale;
14+
15+
@SerializedName("enabled")
16+
public boolean enabled = true;
17+
18+
@SerializedName("category")
19+
public String category = "default";
20+
21+
public PlayerPreset() {}
22+
23+
public PlayerPreset(String identifier, String displayName, float scale, String category) {
24+
this.identifier = identifier;
25+
this.displayName = displayName;
26+
this.scale = scale;
27+
this.category = category;
28+
}
29+
30+
// Auto-detect if identifier is UUID
31+
public boolean isUUID() {
32+
if (identifier == null) return false;
33+
// UUID format: 8-4-4-4-12 characters with hyphens
34+
return identifier.matches("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$");
35+
}
36+
37+
// Get effective display name (falls back to identifier if not set)
38+
public String getEffectiveDisplayName() {
39+
if (displayName != null && !displayName.trim().isEmpty()) {
40+
return displayName.trim();
41+
}
42+
// For UUIDs, show a shortened version as fallback
43+
if (isUUID()) {
44+
return identifier.substring(0, 8) + "...";
45+
}
46+
return identifier;
47+
}
48+
49+
public boolean matches(String uuid, String username) {
50+
if (!enabled) return false;
51+
52+
if (isUUID()) {
53+
return identifier.equalsIgnoreCase(uuid);
54+
} else {
55+
return identifier.equalsIgnoreCase(username);
56+
}
57+
}
58+
}

0 commit comments

Comments
 (0)