# 6.2 release

## Notes
- This release addes the ablility to provide your own or a speedometer made by someone else and not the 2 option I(The developer) provides as hard coded features

## Added:
- Resorcepack source for visual speedometer
- Added documentation for resourcepack setup [resourcepack.md](https://github.com/zaze06/Speedometer/blob/master/resourcepack.md)
- Override config for speedometer pointer (only works if it's not an image)

## Fixes:
- Fixed [#2](https://github.com/zaze06/Speedometer/issues/2)

## TO-DO:

## Resourcepack
This release contains a base resourcepack that shuld be like the old one, but there is also an optinaly downloadible resourcepack from GitHub, and as a additinal file in this release on Github and Modrith

## Code notes
This push updates the development envirment to 1.21.1, but fabric is tested and works on 1.21 still
This commit is contained in:
2024-10-10 09:10:57 +02:00
parent f48a4aaf6c
commit 95222c1c95
24 changed files with 403 additions and 78 deletions

View File

@@ -11,18 +11,13 @@ import net.minecraft.client.KeyMapping;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.network.chat.ClickEvent;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.Resource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.vehicle.Boat;
import net.minecraft.world.phys.Vec3;
import net.minecraft.network.chat.Component;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import static me.zacharias.speedometer.Speedometer.*;
@@ -96,6 +91,39 @@ public class Client {
ClientGuiEvent.RENDER_HUD.register(Client::render);
LOGGER.info("Finished loading speedometer");
/*((IPackRepository) Minecraft.getInstance().getResourcePackRepository()).addSource(new RepositorySource() {
@Override
public void loadPacks(Consumer<Pack> consumer) {
consumer.accept(new Pack(new PackLocationInfo(
"quarter_circle_speedometer",
Component.translatable("resourcepack.speedometer.quarter_circle_speedometer"),
new PackSource() {
@Override
public Component decorate(Component component) {
return component;
}
@Override
public boolean shouldAddAutomatically() {
return false;
}
},
Optional.empty()
),
new Pack.ResourcesSupplier() {
@Override
public PackResources openPrimary(PackLocationInfo packLocationInfo) {
return packLocationInfo.;
}
@Override
public PackResources openFull(PackLocationInfo packLocationInfo, Pack.Metadata metadata) {
return null;
}
}));
}
});*/
}
private static void render(GuiGraphics graphics, DeltaTracker deltaTracker) {
@@ -239,9 +267,9 @@ public class Client {
" Total: " + lSpeed + "\n" +
"Velocity total average: " + speed + "\n" +
"Velocity total in " + speedType.name() + ": " + speedTypeSpeed + "\n" +
"Percentage point of visual speedometer: " + /*v*/"error" + "\n" +
"Degree end point: " + /*i*/"error" +"\n" +
(Config.getVisualSpeedometer()?"Visual Size: ":"Textual display") + Config.getImageSize();
"Endpoint position: (" + Debuger.x + ", " + Debuger.y + ")\n" +
"Percentage point of visual speedometer: " + Debuger.angle + "\n" +
(Config.getVisualSpeedometer()?"Visual Size: "+Config.getImageSize():"Textual display");
Color color = new Color(255, 255, 255);

View File

@@ -1,18 +1,19 @@
package me.zacharias.speedometer;
import dev.architectury.platform.Platform;
import net.minecraft.client.Minecraft;
import org.json.JSONException;
import org.json.JSONObject;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import static me.zacharias.speedometer.Speedometer.MOD_ID;
import static me.zacharias.speedometer.Speedometer.*;
public class Config {
private static JSONObject config;
public static final float configVersion = 3f;
public static final float configVersion = 4f;
private static int counter = 0;
private static String configPath;
private static BufferedImage speedometer;
@@ -40,15 +41,43 @@ public class Config {
while((tmp = in.readLine()) != null){
builder.append(tmp);
}
config = new JSONObject(builder.toString());
try{
config = new JSONObject(builder.toString());
}catch (JSONException e){
File dump = new File(Platform.getConfigFolder().toString()+"/"+MOD_ID+"/config-dump_"+formatMillisToDHMS(System.currentTimeMillis())+"_.json");
LOGGER.error("Config is reset due to invalid content, dumping old content to {}", dump.getPath());
if(!dump.exists()){
dump.createNewFile();
}
try{
BufferedWriter out = new BufferedWriter(new FileWriter(dump));
out.write(builder.toString());
out.close();
}catch (IOException ex)
{
LOGGER.error("Failed to create a dump file and write to it.", ex);
LOGGER.warn("Dump content: \n{{}}", builder.toString());
}
config = new JSONObject();
defualt();
return;
}
LOGGER.info("Loaded config successfully");
if(config.has("version")){
if(config.getFloat("version")!=configVersion){
if(config.getFloat("version") > configVersion){
LOGGER.warn("Config version is too new, resting");
defualt();
save();
}else if(config.getFloat("version") < configVersion){
config = new JSONObject();
LOGGER.warn("Config version is outdated, resting");
defualt();
save();
@@ -109,6 +138,10 @@ public class Config {
if(!config.has("showSpeedType")){
config.put("showSpeedType", false);
}
if(!config.has("overrideColor")) {
config.put("overrideColor", false);
}
}
public static void save(){
@@ -162,6 +195,11 @@ public class Config {
}
}
public static int getColorRGB()
{
return getColor().getRGB() & 0xFFFFFF;
}
public static boolean isDebug() {
if(config.has("debug")){
return config.getBoolean("debug");
@@ -231,10 +269,19 @@ public class Config {
public static boolean isDisableVisualSpeedometer() {
return disableVisualSpeedometer;
}
public static boolean isOverrideColor() {
if(config.has("overrideColor")){
return config.getBoolean("overrideColor");
} else {
return false;
}
}
//endregion
//region Config Setters
public static void setColor(int r, int g, int b){
config.put("color", new JSONObject()
.put("r", r)
@@ -282,5 +329,10 @@ public class Config {
public static void setDisableVisualSpeedometer (boolean disableVisualSpeedometer){
Config.disableVisualSpeedometer = disableVisualSpeedometer;
}
public static void setOverrideColor (boolean overrideColor)
{
config.put("overrideColor", overrideColor);
}
//endregion
}

View File

@@ -22,7 +22,7 @@ public class ConfigMenu {
.build()
);
category.addEntry(entryBuilder.startColorField(Component.translatable("speedometer.config.color"), me.zacharias.speedometer.Config.getColor().getRGB())
category.addEntry(entryBuilder.startColorField(Component.translatable("speedometer.config.color"), me.zacharias.speedometer.Config.getColorRGB())
.setSaveConsumer2(color -> {
me.zacharias.speedometer.Config.setColor(color.getRed(), color.getGreen(), color.getBlue());
})
@@ -87,6 +87,15 @@ public class ConfigMenu {
category.addEntry(entryBuilder.startIntField(Component.translatable("speedometer.config.imageSize"), Config.getImageSize())
.setSaveConsumer(Config::setImageSize)
.setTooltip(Component.translatable("speedometer.config.tooltip.imageSize"))
.setErrorSupplier(size -> {
if(size > 300 || size < 10)
{
return Optional.of(Component.translatable("speedometer.config.error.size_outofbounds"));
}
else {
return Optional.empty();
}
})
.build()
);
@@ -99,7 +108,14 @@ public class ConfigMenu {
.build()
);
category.addEntry(entryBuilder.startBooleanToggle(Component.translatable("speedometer.config.override_color"), Config.isOverrideColor())
.setSaveConsumer(Config::setOverrideColor)
.setTooltip(
Component.translatable("speedometer.config.tooltip.override_color.line1"),
Component.translatable("speedometer.config.tooltip.override_color.line2")
)
.build()
);
category.addEntry(entryBuilder.startBooleanToggle(Component.translatable("speedometer.config.debug"),Config.isDebug())
.setSaveConsumer(Config::setDebug)

View File

@@ -0,0 +1,7 @@
package me.zacharias.speedometer;
public class Debuger {
public static double angle;
public static double x;
public static double y;
}

View File

@@ -24,25 +24,44 @@ public class ImageHandler {
}
return out;
}
public static BufferedImage rotate(BufferedImage img, double angle) {
double rads = Math.toRadians(angle);
int w = img.getWidth();
int h = img.getHeight();
BufferedImage rotated = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = rotated.createGraphics();
AffineTransform at = new AffineTransform();
at.translate(w / 2d, h / 2d);
int x = w / 2;
int y = h / 2;
at.rotate(rads, x, y);
g2d.setTransform(at);
g2d.drawImage(img, 0, h, null);
/**
* Rotates a BufferedImage around a specific point.
*
* @param image The original BufferedImage to rotate.
* @param angle The angle to rotate in degrees (double precision).
* @param x The x coordinate of the point to rotate around (int precision).
* @param y The y coordinate of the point to rotate around (int precision).
* @return A new BufferedImage containing the rotated image.
*/
public static BufferedImage rotateImage(BufferedImage image, double angle, int x, int y) {
// Convert the angle from degrees to radians
double radians = Math.toRadians(angle);
// Get image dimensions
int width = image.getWidth();
int height = image.getHeight();
// Create a new BufferedImage with the same width and height
BufferedImage rotatedImage = new BufferedImage(width, height, image.getType());
// Create a Graphics2D object from the new image
Graphics2D g2d = rotatedImage.createGraphics();
// Perform the rotation around the specified point (x, y)
AffineTransform transform = new AffineTransform();
// Translate the rotation point to the origin (0, 0)
transform.translate(x, y);
// Rotate around the origin
transform.rotate(radians);
// Translate back to the original position
transform.translate(-x, -y);
// Draw the rotated image
g2d.setTransform(transform);
g2d.drawImage(image, 0, 0, null);
g2d.dispose();
return rotated;
return rotatedImage;
}
}

View File

@@ -3,6 +3,7 @@ package me.zacharias.speedometer;
import dev.architectury.platform.Platform;
import dev.architectury.utils.Env;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.Resource;
import net.minecraft.server.packs.resources.ResourceManager;
@@ -12,9 +13,9 @@ import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStream;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
public class Speedometer
{
@@ -53,8 +54,9 @@ public class Speedometer
public static void loadSpeedometers(ResourceManager resourceManager)
{
//List< Resource > resource = Minecraft.getInstance().getResourceManager().getResourceStack(ResourceLocation.fromNamespaceAndPath(MOD_ID, "models/speedometer.json"));
Optional< Resource > resource = resourceManager.getResource(ResourceLocation.fromNamespaceAndPath(MOD_ID, "models/speedometer.json"));
if(resource.isEmpty())
{
Config.setDisableVisualSpeedometer(true);
@@ -70,6 +72,10 @@ public class Speedometer
builder.append(tmp);
}
JSONObject data = new JSONObject(builder.toString());
if(Config.isDebug())
{
LOGGER.info("Loaded speedometer from {}, with speedometer name: {}", resource.get().source().packId(), data.get("name"));
}
ICON = new SpeedometerIcon(data, resourceManager);
}
catch (Exception e){
@@ -80,4 +86,15 @@ public class Speedometer
LOGGER.info("Successfully loaded speedometer config from {}", resource.get().source().packId());
}
public static String formatMillisToDHMS(long millis) {
// Calculate the days, hours, minutes, and seconds
long seconds = TimeUnit.MILLISECONDS.toSeconds(millis) % 60;
long minutes = TimeUnit.MILLISECONDS.toMinutes(millis) % 60;
long hours = TimeUnit.MILLISECONDS.toHours(millis) % 24;
long days = TimeUnit.MILLISECONDS.toDays(millis);
// Format the result as DD+HH-MM-SS
return String.format("%02d+%02d-%02d-%02d", days, hours, minutes, seconds);
}
}

View File

@@ -26,6 +26,7 @@ public class SpeedometerIcon {
private float scale;
private int max;
private boolean overflow;
private boolean g = false;
public SpeedometerIcon(JSONObject config, ResourceManager resourceManager) throws MissingPropertyException, IOException, JSONException
{
@@ -70,15 +71,17 @@ public class SpeedometerIcon {
public BufferedImage getSpeedometerIcon(double speed)
{
Graphics2D graphics = ImageHandler.clone(speedometerIcon).createGraphics();
BufferedImage img = ImageHandler.clone(speedometerIcon);
Graphics2D graphics = img.createGraphics();
pointer.draw(graphics, start, end, max, overflow, Math.pow(speed, scale));
return speedometerIcon;
return img;
}
}
class Pointer
{
private BufferedImage image;
private Color color;
private Vector2i start;
private int length;
private boolean g = false;
@@ -96,7 +99,7 @@ class Pointer
{
if(str.isEmpty()) throw new MissingPropertyException("pointer/start");
if(str.matches("^\\([0-9]+,( )?[0-9]\\)+$"))
if(str.matches("^\\([0-9]+,( )?[0-9]+\\)+$"))
{
String[] split = str.split(",");
start = new Vector2i(Integer.parseInt(split[0].substring(1)), Integer.parseInt(split[1].substring(0, split[1].length()-1)));
@@ -126,7 +129,7 @@ class Pointer
if(image.isEmpty()) throw new MissingPropertyException("pointer/image");
InputStream stream = image.get().open();
this.image = ImageIO.read(stream);
this.image = ImageHandler.scale(ImageIO.read(stream), size.x, size.y);
stream.close();
}
else if(pointer.has("length"))
@@ -145,32 +148,52 @@ class Pointer
length = integer;
}
else throw new MissingPropertyException("pointer/length");
if(pointer.has("color"))
{
String c = pointer.getString("color");
if(!c.matches("^#[0-9a-fA-F]{6}$")) throw new MissingPropertyException("pointer/color");
color = new Color(Integer.parseInt(c.substring(1), 16));
}
else throw new MissingPropertyException("pointer/color");
}
else throw new MissingPropertyException("pointer/image or pointer/length");
}
public void draw(Graphics2D g2d, int start, int end, int max, boolean overflow, double speed)
{
double angle = (speed * end)+start;
if(angle > max && overflow) angle = end;
Color c = color;
if(Config.isOverrideColor())
{
c = Config.getColor();
}
double angle = ((speed/max) * end)+start;
if(angle > end && !overflow) angle = end;
Debuger.angle = angle;
if(Objects.nonNull(image))
{
BufferedImage image = ImageHandler.rotate(this.image, angle);
if(angle>start+10 && !g) {
try {
File output = new File("./dev.png");
ImageIO.write(image, "png", output);
} catch (IOException e) {
throw new RuntimeException(e);
}
g = true;
}
g2d.drawImage(image, this.start.x, this.start.y, null);
int centerX = this.start.x;
int centerY = this.start.y;
BufferedImage image = ImageHandler.rotateImage(this.image, angle, centerX, centerY);
g2d.drawImage(image, 0, 0, null);
}
else if(c != null && length > 0)
{
double angleRads = Math.toRadians(180+angle);
int endX = (int) (Math.cos(angleRads) * length + this.start.x);
int endY = (int) (Math.sin(angleRads) * length + this.start.y);
Debuger.x = endX;
Debuger.y = endY;
g2d.setColor(c);
g2d.setStroke(new BasicStroke(3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g2d.drawLine(this.start.x, this.start.y, endX, endY);
}
else
{
Config.setDisableVisualSpeedometer(true);
throw new NullPointerException("image and line pointer both are undefined");
}
}
}

View File

@@ -15,6 +15,7 @@
"speedometer.config.imageSize": "Image Size",
"speedometer.config.showVisualSpeedType": "\u00A74⨻\u00A7rShow Visual Speed Type",
"speedometer.config.showSpeedType": "Show Visual Speed Type",
"speedometer.config.override_color": "Override color",
"speedometer.key.configKey": "Config Key",
"speedometer.key.debugKey": "Debug Key",
@@ -29,7 +30,7 @@
"speedometer.meter.large": "Large",
"speedometer.meter.small": "Small",
"speedometer.meter.medium": "Small",
"speedometer.meter.medium": "Medium",
"speedometer.visualSpeedometer.true": "Visual",
"speedometer.visualSpeedometer.false": "Text",
@@ -60,7 +61,14 @@
"speedometer.config.tooltip.showSpeedType.line1": "This setting shows the speed type above the visual meter which is better then within the visual meter",
"speedometer.config.tooltip.override_color.line1": "Override the color of a line pointer",
"speedometer.config.tooltip.override_color.line2": "OPS doesn't work if the pointer is an image!",
"speedometer.invalid": "Invalid String",
"speedometer.config.error.size_outofbounds": "The size is out of bounds 10<=size<=300",
"speedometer.error.missing_cloth": "Missing Cloth Config API for Config Screen"
"speedometer.error.missing_cloth": "Missing Cloth Config API for Config Screen",
"resourcepack.speedometer.quarter_circle_speedometer": "Quarter circle Speedometer"
}

View File

@@ -0,0 +1,74 @@
{
"speedometer.config.name": "Speedometer Config",
"speedometer.config.category.name": "Speedometer Konfigurationskatigori",
"speedometer.config.speed": "Fart Typ",
"speedometer.config.color": "Färg",
"speedometer.config.knot": "Använd Knot i Båtar",
"speedometer.config.visualSpeedometer": "Visuel Fartmätare",
"speedometer.config.xPosition.visual": "X Positionen för Visuel Mätare",
"speedometer.config.yPosition.visual": "Y Positionen för Visuel Mätare",
"speedometer.config.xPosition.text": "X Positionen för Text Mätare",
"speedometer.config.yPosition.text": "Y Positionen för Text Mätare",
"speedometer.config.xPosition": "X Positionen för Mätare",
"speedometer.config.yPosition": "Y Positionen för Mätare",
"speedometer.config.debug": "Debug",
"speedometer.config.imageSize": "Bildstorlek",
"speedometer.config.showVisualSpeedType": "\u00A74⨻\u00A7rVisa Visuell Farttyp",
"speedometer.config.showSpeedType": "Visa Visuell Farttyp",
"speedometer.config.override_color": "Överskrid färgen",
"speedometer.key.configKey": "Konfigurations Knapp",
"speedometer.key.debugKey": "Debug Knapp",
"speedometer.key.category": "Speedometer",
"speedometer.speed.mph": "MPH",
"speedometer.speed.mps": "Meter/s",
"speedometer.speed.kmph": "Km/h",
"speedometer.speed.bps": "Blocks/s",
"speedometer.speed.knot": "Knot",
"speedometer.speed.error": "Okänd Fart Typ",
"speedometer.meter.large": "Stor",
"speedometer.meter.small": "Liten",
"speedometer.meter.medium": "Mellan",
"speedometer.visualSpeedometer.true": "Visuel",
"speedometer.visualSpeedometer.false": "Text",
"speedometer.useKnot.true": "Ja",
"speedometer.useKnot.false": "Nej",
"speedometer.debug.true": "\u00A74Ja",
"speedometer.debug.false": "\u00A74Nej",
"speedometer.show": "Visa",
"speedometer.hide": "Göm",
"speedometer.config.tooltip.xPosition.line1": "W = fönstrets bred",
"speedometer.config.tooltip.xPosition.line2": "w = halva fönstrets bred",
"speedometer.config.tooltip.xPosition.line3": "s = breden på texten eller bilden",
"speedometer.config.tooltip.yPosition.line1": "H = fönstrets höjd",
"speedometer.config.tooltip.yPosition.line2": "h = halva fönstrets höjd",
"speedometer.config.tooltip.yPosition.line3": "s = höjden av texten eller bilden",
"speedometer.config.tooltip.debug": "Debug Information",
"speedometer.config.tooltip.imageSize": "Storleken på bilden. Detta bestämmer både bredden och höjden av bilden",
"speedometer.config.tooltip.showVisualSpeedType.line1": "Denna instälningen funkar inte helt och hållet",
"speedometer.config.tooltip.showVisualSpeedType.line2": "Jag (utvecklaren) avråder användandet av denna inställning då den inte fungerar vid mindre storlekar och förstör FPS vid större storlekar",
"speedometer.config.tooltip.showSpeedType.line1": "Denna inställning visar farttypen ovanför den visuella mätaren, vilket är bättre än inuti mätaren",
"speedometer.config.tooltip.override_color.line1": "Överskrider förgen av linjen pekaren",
"speedometer.config.tooltip.override_color.line2": "OBS funkar inte om pekaren är en bild!",
"speedometer.invalid": "Inte korrekt Sträng",
"speedometer.config.error.size_outofbounds": "Stolekten är felakting 10<=stolek<=300",
"speedometer.error.missing_cloth": "Saknar Cloth Config API för konfigurationsskärmen",
"resourcepack.speedometer.quarter_circle_speedometer": "Quarter circle Speedometer"
}

View File

@@ -1,13 +1,14 @@
{
"background": "speedometer:meter/speedometer.png",
"start": 45,
"end": 215,
"start": -45,
"end": 225,
"maxSpeed": 120,
"overflow": true,
"pointer": {
"image": "speedometer:meter/pointer.png",
"start": "(0,0)"
"color": "#b00219",
"length": 50,
"start": "center"
},
"scale": 1,
"name": "Test"
"name": "Full cycle style"
}

View File

@@ -1,6 +0,0 @@
{
"pack": {
"pack_format": 34,
"description": "speedometer resources"
}
}