Skip to content

Commit fc5eb67

Browse files
committed
improve cursor validation. change maximum size to 128 in steps of 8.
1 parent cffeb63 commit fc5eb67

12 files changed

Lines changed: 78 additions & 53 deletions

File tree

common/src/main/java/io/github/fishstiz/minecraftcursor/CursorResourceLoader.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,12 @@ public static boolean isResourceSetting(@NotNull Cursor cursor, @Nullable Config
8282
}
8383

8484
public static boolean retoreActiveResourceSettings(@NotNull Cursor cursor) {
85-
if (resourceConfig != null) {
85+
if (cursor.isLoaded() && resourceConfig != null) {
8686
CONFIG.replaceActiveSettings(resourceConfig, cursor);
8787
cursor.apply(CONFIG.getGlobal().apply(CONFIG.getOrCreateSettings(cursor)));
8888
return true;
8989
} else {
90-
LOGGER.error("Failed to apply resource settings: Not Found");
90+
LOGGER.error("Failed to apply resource settings for '{}'", cursor.getTypeKey());
9191
}
9292
return false;
9393
}
@@ -136,7 +136,7 @@ private static boolean loadCursorTexture(ResourceManager manager, Cursor cursor,
136136
CursorManager.INSTANCE.loadCursor(cursor, image, CONFIG.getGlobal().apply(settings), animation);
137137
return true;
138138
} catch (IOException e) {
139-
LOGGER.error("[minecraft-cursor] Failed to load cursor at '{}'", location);
139+
LOGGER.error("[minecraft-cursor] Failed to load cursor at '{}': {}", location, e.getMessage());
140140
return false;
141141
}
142142
}

common/src/main/java/io/github/fishstiz/minecraftcursor/cursor/Cursor.java

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import io.github.fishstiz.minecraftcursor.compat.ExternalCursorTracker;
88
import io.github.fishstiz.minecraftcursor.config.Config;
99
import io.github.fishstiz.minecraftcursor.util.NativeImageUtil;
10+
import io.github.fishstiz.minecraftcursor.util.SettingsUtil;
1011
import net.minecraft.network.chat.Component;
1112
import net.minecraft.resources.ResourceLocation;
1213
import org.jetbrains.annotations.NotNull;
@@ -44,31 +45,34 @@ public Cursor(CursorType type, @Nullable Consumer<Cursor> onLoad) {
4445
}
4546

4647
public void loadImage(@NotNull NativeImage image, Config.Settings settings) throws IOException {
47-
this.textureWidth = image.getWidth();
48-
this.textureHeight = image.getHeight();
49-
50-
if (!SUPPORTED_SIZES.contains(textureWidth) || textureHeight % textureWidth != 0) {
51-
throw new IOException("Invalid cursor size. Width must be one of " + SUPPORTED_SIZES + ", and height must be a multiple of width.");
52-
}
53-
54-
boolean cropped = false;
55-
NativeImage croppedImage = image;
56-
5748
try {
58-
int size = this.textureWidth;
59-
if (image.getHeight() > size) {
60-
croppedImage = NativeImageUtil.cropImage(image, 0, 0, size, size);
61-
cropped = true;
62-
}
63-
64-
this.base64Image = NativeImageUtil.toBase64String(croppedImage);
65-
this.enabled = settings.isEnabled();
66-
67-
create(croppedImage, settings.getScale(), settings.getXHot(), settings.getYHot());
68-
} finally {
69-
if (cropped) {
70-
croppedImage.close();
49+
int imageWidth = image.getWidth();
50+
int imageHeight = image.getHeight();
51+
SettingsUtil.assertImageSize(imageWidth, imageHeight);
52+
53+
NativeImage croppedImage = null;
54+
try {
55+
if (image.getHeight() > imageWidth) {
56+
// noinspection SuspiciousNameCombination
57+
croppedImage = NativeImageUtil.cropImage(image, 0, 0, imageWidth, imageWidth);
58+
}
59+
60+
NativeImage validImage = croppedImage != null ? croppedImage : image;
61+
this.base64Image = NativeImageUtil.toBase64String(validImage);
62+
this.enabled = settings.isEnabled();
63+
this.textureWidth = imageWidth;
64+
this.textureHeight = imageHeight;
65+
66+
create(validImage, settings.getScale(), settings.getXHot(), settings.getYHot());
67+
} finally {
68+
if (croppedImage != null) {
69+
croppedImage.close();
70+
}
7171
}
72+
} catch (Exception e) {
73+
this.loaded = false;
74+
this.destroy();
75+
throw e;
7276
}
7377
}
7478

@@ -86,8 +90,8 @@ protected void updateImage(double scale, int xhot, int yhot) {
8690

8791
private void create(NativeImage image, double scale, int xhot, int yhot) {
8892
scale = sanitizeScale(scale);
89-
xhot = sanitizeHotspot(xhot, this);
90-
yhot = sanitizeHotspot(yhot, this);
93+
xhot = sanitizeHotspot(xhot, image.getWidth());
94+
yhot = sanitizeHotspot(yhot, image.getWidth());
9195

9296
long glfwImageAddress = MemoryUtil.NULL;
9397
long previousId = this.id;

common/src/main/java/io/github/fishstiz/minecraftcursor/gui/screen/ConfigurationScreen.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ protected void postInit() {
5858
}
5959

6060
private void addGlobalItems() {
61-
this.addCategoryOnly(GLOBAL_CATEGORY, new GlobalOptionsPanel());
61+
this.addCategoryOnly(GLOBAL_CATEGORY, new GlobalOptionsPanel(this::refreshCursors));
6262
}
6363

6464
private void addAdaptiveItems() {

common/src/main/java/io/github/fishstiz/minecraftcursor/gui/screen/panel/GlobalOptionsPanel.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,16 @@ public class GlobalOptionsPanel extends AbstractOptionsPanel {
4040
private static final Tooltip RESET_INFO = Tooltip.create(Component.translatable("minecraft-cursor.options.resource_pack.reset.tooltip"));
4141
private static final int PREVIEW_BUTTON_SIZE = 20;
4242
private static final int SCALE_OVERRIDE = -1;
43+
private final Runnable refreshCursors;
4344
private @NotNull Iterator<Cursor> cursors = cursorIterator();
4445
private @NotNull Cursor currentCursor = getDefaultCursor();
4546
private OptionsListWidget optionList;
4647
private GlobalPreviewWidget previewWidget;
4748

48-
public GlobalOptionsPanel() {
49+
public GlobalOptionsPanel(Runnable refreshCursors) {
4950
super(TITLE);
51+
52+
this.refreshCursors = refreshCursors;
5053
}
5154

5255
@Override
@@ -213,6 +216,7 @@ private void toggleCursorAnimations(boolean animated) {
213216

214217
private void resetCursorSettings() {
215218
CursorResourceLoader.restoreResourceSettings();
219+
this.refreshCursors.run();
216220
this.refreshWidgets();
217221
this.repositionElements();
218222
}

common/src/main/java/io/github/fishstiz/minecraftcursor/gui/widget/CursorHotspotWidget.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import static io.github.fishstiz.minecraftcursor.MinecraftCursor.CONFIG;
1717

1818
public class CursorHotspotWidget extends CursorWidget {
19-
private static final ResourceLocation BACKGROUND_64 = MinecraftCursor.loc("textures/gui/background_64.png");
19+
private static final ResourceLocation BACKGROUND_64 = MinecraftCursor.loc("textures/gui/background_128.png");
2020
private static final int BACKGROUND_DISABLED = 0xAF000000; // 70% black
2121
private static final int RULER_COLOR = 0xFFFF0000; // red
2222
private static final int OVERRIDE_RULER_COLOR = 0xFF00FF00; // green
@@ -25,6 +25,7 @@ public class CursorHotspotWidget extends CursorWidget {
2525
private final SliderWidget xhotSlider;
2626
private final SliderWidget yhotSlider;
2727
private final @Nullable MouseEventListener mouseEventListener;
28+
private final int maxHotspot;
2829
private boolean renderRuler = true;
2930
private boolean dragging = false;
3031

@@ -41,6 +42,7 @@ public CursorHotspotWidget(
4142
this.xhotSlider = xhotSlider;
4243
this.yhotSlider = yhotSlider;
4344
this.mouseEventListener = mouseEventListener;
45+
this.maxHotspot = SettingsUtil.getMaxHotspot(cursor);
4446
}
4547

4648
@Override
@@ -79,9 +81,9 @@ protected void renderRuler(@NotNull GuiGraphics guiGraphics, int mouseX, int mou
7981
int yhot = this.clampHotspot(isGlobalY ? this.global.getYHot() : (int) this.yhotSlider.getMappedValue());
8082

8183
float rulerSize = this.getCellSize();
82-
int xhotX1 = (int) (getX() + xhot * rulerSize);
84+
int xhotX1 = (int) ((getX() + xhot * rulerSize) - (rulerSize > 1 || xhot != this.maxHotspot ? 0 : 1));
8385
int xhotX2 = (int) ((getX() + xhot * rulerSize) + (xhot > 0 ? rulerSize : Math.max(rulerSize, 2)));
84-
int yhotY1 = (int) (getY() + yhot * rulerSize);
86+
int yhotY1 = (int) ((getY() + yhot * rulerSize) - (rulerSize > 1 || yhot != this.maxHotspot ? 0 : 1));
8587
int yhotY2 = (int) ((getY() + yhot * rulerSize) + (yhot > 0 ? rulerSize : Math.max(rulerSize, 2)));
8688

8789

@@ -137,7 +139,7 @@ public void setHotspots(MouseEvent mouseEvent, double mouseX, double mouseY) {
137139
}
138140

139141
private int clampHotspot(int hotspot) {
140-
return SettingsUtil.sanitizeHotspot(hotspot, this.getCursor());
142+
return SettingsUtil.clamp(hotspot, SettingsUtil.HOT_MIN, this.maxHotspot);
141143
}
142144

143145
@Override

common/src/main/java/io/github/fishstiz/minecraftcursor/gui/widget/CursorPreviewWidget.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import org.jetbrains.annotations.Nullable;
1515

1616
public class CursorPreviewWidget extends CursorWidget {
17-
private static final ResourceLocation BACKGROUND_64 = MinecraftCursor.loc("textures/gui/background_dark_64.png");
17+
private static final ResourceLocation BACKGROUND_128 = MinecraftCursor.loc("textures/gui/background_dark_128.png");
1818
private static final Component PREVIEW_TEXT = Component.translatable("minecraft-cursor.options.preview");
1919
private static final int PREVIEW_TEXT_OFFSET = 4;
2020
private static final int PREVIEW_TEXT_COLOR = 0x7FFFFFFF; // 50% white
@@ -25,7 +25,7 @@ public class CursorPreviewWidget extends CursorWidget {
2525
private final Font font;
2626

2727
public CursorPreviewWidget(@NotNull Cursor cursor, @NotNull Font font, @Nullable Button button) {
28-
super(CommonComponents.EMPTY, cursor, BACKGROUND_64);
28+
super(CommonComponents.EMPTY, cursor, BACKGROUND_128);
2929

3030
this.active = false;
3131
this.font = font;

common/src/main/java/io/github/fishstiz/minecraftcursor/gui/widget/CursorWidget.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@
1414
public abstract class CursorWidget extends AbstractWidget implements CursorProvider {
1515
public static final int DEFAULT_HEIGHT = 32;
1616
public static final int DEFAULT_WIDTH = 32;
17+
private static final int BACKGROUND_SIZE = 128;
1718
private static final int BORDER_COLOR = 0xFF000000; // black
1819
private static final int FOCUSED_BORDER_COLOR = 0xFFFFFFFF; // white
19-
private final ResourceLocation background64;
20+
private final ResourceLocation background128;
2021
private final Cursor cursor;
2122

2223
protected CursorWidget(
@@ -26,16 +27,16 @@ protected CursorWidget(
2627
int height,
2728
@NotNull Component message,
2829
@NotNull Cursor cursor,
29-
@NotNull ResourceLocation background64
30+
@NotNull ResourceLocation background128
3031
) {
3132
super(x, y, width, height, message);
3233

3334
this.cursor = cursor;
34-
this.background64 = background64;
35+
this.background128 = background128;
3536
}
3637

37-
protected CursorWidget(@NotNull Component message, @NotNull Cursor cursor, @NotNull ResourceLocation background64) {
38-
this(0, 0, DEFAULT_WIDTH, DEFAULT_HEIGHT, message, cursor, background64);
38+
protected CursorWidget(@NotNull Component message, @NotNull Cursor cursor, @NotNull ResourceLocation background128) {
39+
this(0, 0, DEFAULT_WIDTH, DEFAULT_HEIGHT, message, cursor, background128);
3940
}
4041

4142
protected void renderCursor(@NotNull GuiGraphics guiGraphics, @NotNull Cursor cursor) {
@@ -51,8 +52,8 @@ protected void renderBackground(@NotNull GuiGraphics guiGraphics) {
5152
this.getWidth(),
5253
this.getHeight(),
5354
this.getCellSize(),
54-
this.background64,
55-
64
55+
this.background128,
56+
BACKGROUND_SIZE
5657
);
5758
}
5859

common/src/main/java/io/github/fishstiz/minecraftcursor/util/SettingsUtil.java

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,14 @@
88
import org.jetbrains.annotations.NotNull;
99
import org.jetbrains.annotations.Nullable;
1010

11+
import java.io.IOException;
1112
import java.util.Collection;
12-
import java.util.Collections;
1313
import java.util.Objects;
14-
import java.util.Set;
1514

1615
public class SettingsUtil {
17-
public static final Set<Integer> SUPPORTED_SIZES = Set.of(8, 16, 32, 48, 64);
16+
public static final int IMAGE_SIZE_MIN = 8;
17+
public static final int IMAGE_SIZE_MAX = 128;
18+
public static final int IMAGE_SIZE_STEP = 8;
1819
public static final double SCALE_AUTO_PREFERRED = 0;
1920
public static final double SCALE_AUTO_THRESHOLD_MAX = 0.49;
2021
public static final double SCALE = 1.0;
@@ -25,12 +26,21 @@ public class SettingsUtil {
2526
public static final int Y_HOT = 0;
2627
public static final int HOT_MIN = 0;
2728
public static final int HOT_STEP = 1;
28-
public static final int GLOBAL_HOT_MAX = Collections.max(SUPPORTED_SIZES) - 1;
29+
public static final int GLOBAL_HOT_MAX = IMAGE_SIZE_MAX - 1;
2930
public static final boolean ENABLED = true;
3031

3132
private SettingsUtil() {
3233
}
3334

35+
public static void assertImageSize(int imageWidth, int imageHeight) throws IOException {
36+
if (imageWidth < IMAGE_SIZE_MIN || imageWidth > IMAGE_SIZE_MAX || imageWidth % IMAGE_SIZE_STEP != 0) {
37+
throw new IOException("Unsupported image width: " + imageWidth);
38+
}
39+
if (imageHeight % imageWidth != 0) {
40+
throw new IOException("Image height must be divisible by width: " + imageHeight + " % " + imageWidth);
41+
}
42+
}
43+
3444
public static boolean isAutoScale(double scale) {
3545
return scale <= SCALE_AUTO_THRESHOLD_MAX;
3646
}
@@ -54,8 +64,12 @@ public static double sanitizeScale(double scale) {
5464
return (double) Math.round(mappedScale * 100) / 100;
5565
}
5666

67+
public static int sanitizeHotspot(int hotspot, int imageWidth) {
68+
return clamp(hotspot, HOT_MIN, imageWidth - 1);
69+
}
70+
5771
public static int sanitizeHotspot(int hotspot, @NotNull Cursor cursor) {
58-
return clamp(hotspot, HOT_MIN, cursor.isLoaded() ? Objects.requireNonNull(cursor).getTextureWidth() - 1 : GLOBAL_HOT_MAX);
72+
return sanitizeHotspot(hotspot, cursor.isLoaded() ? cursor.getTextureWidth() : IMAGE_SIZE_MAX);
5973
}
6074

6175
public static int sanitizeHotspot(double hotspot, @NotNull Cursor cursor) {
@@ -82,16 +96,16 @@ public static int getMaxHotspot(Cursor cursor) {
8296
}
8397

8498
public static int getMaxHotspot(Collection<Cursor> cursors) {
85-
int max = SUPPORTED_SIZES.iterator().next();
99+
int max = -1;
86100
for (Cursor cursor : cursors) {
87101
if (cursor.isLoaded()) {
88-
int textureWidth = cursor.getTextureWidth();
89-
if (textureWidth > max) {
90-
max = textureWidth;
102+
int maxHotspot = getMaxHotspot(cursor);
103+
if (maxHotspot > max) {
104+
max = maxHotspot;
91105
}
92106
}
93107
}
94-
return max - 1;
108+
return max != -1 ? max : GLOBAL_HOT_MAX;
95109
}
96110

97111
public static boolean equalSettings(@Nullable Config.Settings a, @Nullable Config.Settings b, boolean excludeGlobal) {
471 Bytes
Loading
Binary file not shown.

0 commit comments

Comments
 (0)