diff options
Diffstat (limited to 'src/main/java/de/cowtipper/cowlection/util')
3 files changed, 232 insertions, 10 deletions
diff --git a/src/main/java/de/cowtipper/cowlection/util/GsonUtils.java b/src/main/java/de/cowtipper/cowlection/util/GsonUtils.java index c0b2735..e97a88d 100644 --- a/src/main/java/de/cowtipper/cowlection/util/GsonUtils.java +++ b/src/main/java/de/cowtipper/cowlection/util/GsonUtils.java @@ -3,14 +3,19 @@ package de.cowtipper.cowlection.util; import com.google.gson.*; import com.mojang.util.UUIDTypeAdapter; import de.cowtipper.cowlection.data.HyPlayerData; -import net.minecraft.nbt.NBTBase; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.nbt.NBTTagList; -import net.minecraft.nbt.NBTTagString; +import net.minecraft.nbt.*; import net.minecraftforge.common.util.Constants; +import org.apache.commons.codec.binary.Base64; import java.io.Reader; import java.lang.reflect.Type; +import java.time.DateTimeException; +import java.time.Instant; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Map; +import java.util.TreeMap; import java.util.UUID; public final class GsonUtils { @@ -29,13 +34,51 @@ public final class GsonUtils { } public static String toJson(Object object) { + return toJson(object, false); + } + + public static String toJson(Object object, boolean sort) { if (object instanceof NBTBase) { - return gsonPrettyPrinter.toJson(nbtToJson((NBTBase) object)); + JsonElement jsonElement = nbtToJson((NBTBase) object); + if (sort && (jsonElement instanceof JsonObject || jsonElement instanceof JsonArray)) { + jsonElement = sortJsonElement(jsonElement); + } + return gsonPrettyPrinter.toJson(jsonElement); } else { return gson.toJson(object); } } + private static JsonElement sortJsonElement(JsonElement jsonElement) { + if (jsonElement instanceof JsonArray) { + // sort each element of array + JsonArray sortedJsonArray = new JsonArray(); + for (JsonElement arrayElement : (JsonArray) jsonElement) { + sortedJsonArray.add(sortJsonElement(arrayElement)); + } + return sortedJsonArray; + } else if (jsonElement instanceof JsonObject) { + // sort json by key + TreeMap<String, JsonElement> sortedJsonObject = new TreeMap<>(String::compareToIgnoreCase); + for (Map.Entry<String, JsonElement> jsonEntry : ((JsonObject) jsonElement).entrySet()) { + JsonElement sortedJsonElement = jsonEntry.getValue(); + if (sortedJsonElement instanceof JsonObject) { + sortedJsonElement = sortJsonElement(sortedJsonElement); + } + sortedJsonObject.put(jsonEntry.getKey(), sortedJsonElement); + } + // overwrite jsonElement with json sorted by key alphabetically + JsonObject sortedJsonElement = new JsonObject(); + for (Map.Entry<String, JsonElement> jsonEntrySorted : sortedJsonObject.entrySet()) { + sortedJsonElement.add(jsonEntrySorted.getKey(), jsonEntrySorted.getValue()); + } + return sortedJsonElement; + } else { + // neither array, nor object: return original element + return jsonElement; + } + } + private static JsonElement nbtToJson(NBTBase nbtElement) { if (nbtElement instanceof NBTBase.NBTPrimitive) { NBTBase.NBTPrimitive nbtNumber = (NBTBase.NBTPrimitive) nbtElement; @@ -56,7 +99,34 @@ public final class GsonUtils { return new JsonObject(); } } else if (nbtElement instanceof NBTTagString) { - return new JsonPrimitive(((NBTTagString) nbtElement).getString()); + String str = ((NBTTagString) nbtElement).getString(); + if (str.length() > 100 && (str.startsWith("eyJ") || str.startsWith("ewo")) && Base64.isBase64(str)) { + // base64 decode NBTTagStrings starting with {" or {\n + try { + JsonElement base64DecodedJson = new JsonParser().parse(new String(Base64.decodeBase64(str))); + if (base64DecodedJson.isJsonObject()) { + JsonObject jsonObject = base64DecodedJson.getAsJsonObject(); + JsonElement timestamp = jsonObject.get("timestamp"); + if (timestamp != null) { + // convert unix timestamp to human-readable dates + try { + ZonedDateTime utcDateTime = ZonedDateTime.ofInstant(Instant.ofEpochMilli(timestamp.getAsLong()), ZoneOffset.UTC); + ZonedDateTime localDateTime = utcDateTime.withZoneSameInstant(ZoneOffset.systemDefault()); + String zoneOffset = localDateTime.getOffset().toString(); + + DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss O"); + jsonObject.add("timeInUTC", new JsonPrimitive(utcDateTime.format(dateTimeFormatter))); + jsonObject.add("timeInLocalZone (UTC" + ("Z".equals(zoneOffset) ? "" : zoneOffset) + ")", new JsonPrimitive(localDateTime.format(dateTimeFormatter))); + } catch (DateTimeException | NumberFormatException ignored) { + } + } + } + return base64DecodedJson; + } catch (JsonParseException ignored) { + // failed to parse as json; leaving original string unmodified + } + } + return new JsonPrimitive(str); } else if (nbtElement instanceof NBTTagList) { NBTTagList nbtList = (NBTTagList) nbtElement; JsonArray jsonArray = new JsonArray(); @@ -64,6 +134,13 @@ public final class GsonUtils { jsonArray.add(nbtToJson(nbtList.get(tagId))); } return jsonArray; + } else if (nbtElement instanceof NBTTagIntArray) { + int[] intArray = ((NBTTagIntArray) nbtElement).getIntArray(); + JsonArray jsonArray = new JsonArray(); + for (int number : intArray) { + jsonArray.add(new JsonPrimitive(number)); + } + return jsonArray; } else if (nbtElement instanceof NBTTagCompound) { NBTTagCompound nbtCompound = (NBTTagCompound) nbtElement; JsonObject jsonObject = new JsonObject(); diff --git a/src/main/java/de/cowtipper/cowlection/util/ImageUtils.java b/src/main/java/de/cowtipper/cowlection/util/ImageUtils.java index 69ee561..1d6820f 100644 --- a/src/main/java/de/cowtipper/cowlection/util/ImageUtils.java +++ b/src/main/java/de/cowtipper/cowlection/util/ImageUtils.java @@ -1,12 +1,20 @@ package de.cowtipper.cowlection.util; import com.mojang.authlib.minecraft.MinecraftProfileTexture; +import de.cowtipper.cowlection.Cowlection; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.ThreadDownloadImageData; import net.minecraft.util.ResourceLocation; +import net.minecraft.world.storage.MapData; import net.minecraftforge.fml.relauncher.ReflectionHelper; +import javax.imageio.ImageIO; +import java.awt.*; import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; public class ImageUtils { public static int getTierFromTexture(String minionSkinId) { @@ -62,4 +70,128 @@ public class ImageUtils { return ordinal(); } } + + private static final Color[] MAP_COLORS; + + static { + // base colors: https://minecraft.gamepedia.com/Map_item_format?oldid=778280#Base_colors + MAP_COLORS = new Color[36]; + MAP_COLORS[0] = new Color(0, 0, 0, 0); + MAP_COLORS[1] = new Color(125, 176, 55, 255); + MAP_COLORS[2] = new Color(244, 230, 161, 255); + MAP_COLORS[3] = new Color(197, 197, 197, 255); + MAP_COLORS[4] = new Color(252, 0, 0, 255); + MAP_COLORS[5] = new Color(158, 158, 252, 255); + MAP_COLORS[6] = new Color(165, 165, 165, 255); + MAP_COLORS[7] = new Color(0, 123, 0, 255); + MAP_COLORS[8] = new Color(252, 252, 252, 255); + MAP_COLORS[9] = new Color(162, 166, 182, 255); + MAP_COLORS[10] = new Color(149, 108, 76, 255); + MAP_COLORS[11] = new Color(111, 111, 111, 255); + MAP_COLORS[12] = new Color(63, 63, 252, 255); + MAP_COLORS[13] = new Color(141, 118, 71, 255); + MAP_COLORS[14] = new Color(252, 249, 242, 255); + MAP_COLORS[15] = new Color(213, 125, 50, 255); + MAP_COLORS[16] = new Color(176, 75, 213, 255); + MAP_COLORS[17] = new Color(101, 151, 213, 255); + MAP_COLORS[18] = new Color(226, 226, 50, 255); + MAP_COLORS[19] = new Color(125, 202, 25, 255); + MAP_COLORS[20] = new Color(239, 125, 163, 255); + MAP_COLORS[21] = new Color(75, 75, 75, 255); + MAP_COLORS[22] = new Color(151, 151, 151, 255); + MAP_COLORS[23] = new Color(75, 125, 151, 255); + MAP_COLORS[24] = new Color(125, 62, 176, 255); + MAP_COLORS[25] = new Color(50, 75, 176, 255); + MAP_COLORS[26] = new Color(101, 75, 50, 255); + MAP_COLORS[27] = new Color(101, 125, 50, 255); + MAP_COLORS[28] = new Color(151, 50, 50, 255); + MAP_COLORS[29] = new Color(25, 25, 25, 255); + MAP_COLORS[30] = new Color(247, 235, 76, 255); + MAP_COLORS[31] = new Color(91, 216, 210, 255); + MAP_COLORS[32] = new Color(73, 129, 252, 255); + MAP_COLORS[33] = new Color(0, 214, 57, 255); + MAP_COLORS[34] = new Color(127, 85, 48, 255); + MAP_COLORS[35] = new Color(111, 2, 0, 255); + } + + public static File saveMapToFile(MapData mapData) { + int size = 128; + BufferedImage image = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB); + + int x = 0; + int y = 0; + for (int i = 0; i < mapData.colors.length; i++) { + Color pixelColor = colorIdToColor(mapData.colors[i]); + image.setRGB(x, y, pixelColor.getRGB()); + ++x; + if (x >= size) { + // new line + ++y; + x = 0; + } + } + try { + File cowlectionImagePath = new File(Minecraft.getMinecraft().mcDataDir, "cowlection_images"); + if (!cowlectionImagePath.exists() && !cowlectionImagePath.mkdirs()) { + // dir didn't exist and couldn't be created + return null; + } + File imageFile = getTimestampedPngFileForDirectory(cowlectionImagePath, "map"); + ImageIO.write(image, "png", imageFile); + return imageFile.getCanonicalFile(); + } catch (IOException e) { + // couldn't save map image + e.printStackTrace(); + return null; + } + } + + private static Color colorIdToColor(byte rawId) { + int id = rawId & 255; + int baseId = id / 4; + int shadeId = id & 3; + + if (baseId >= MAP_COLORS.length) { + Cowlection.getInstance().getLogger().warn("Unknown base color id " + baseId + " (id=" + id + ")"); + return new Color(0xf700d5); + } + + Color c = MAP_COLORS[baseId]; + int shadeMul; + switch (shadeId) { + case 0: + shadeMul = 180; + break; + case 1: + shadeMul = 220; + break; + case 2: + shadeMul = 255; + break; + case 3: + shadeMul = 135; + break; + default: + shadeMul = 180; + Cowlection.getInstance().getLogger().warn("Unknown shade id " + shadeId + " (raw: " + id + ")"); + c = new Color(0xf700d5); + } + return new Color((shadeMul * c.getRed()) / 255, (shadeMul * c.getGreen()) / 255, (shadeMul * c.getBlue()) / 255, c.getAlpha()); + } + + /** + * Based on ScreenShotHelper#getTimestampedPNGFileForDirectory + */ + private static File getTimestampedPngFileForDirectory(File directory, String prefix) { + String currentDateTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss")); + int i = 1; + + while (true) { + File timestampedFile = new File(directory, prefix + "_" + currentDateTime + (i == 1 ? "" : "_" + i) + ".png"); + if (!timestampedFile.exists()) { + return timestampedFile; + } + ++i; + } + } } diff --git a/src/main/java/de/cowtipper/cowlection/util/MooChatComponent.java b/src/main/java/de/cowtipper/cowlection/util/MooChatComponent.java index 95415d3..856cbb3 100644 --- a/src/main/java/de/cowtipper/cowlection/util/MooChatComponent.java +++ b/src/main/java/de/cowtipper/cowlection/util/MooChatComponent.java @@ -6,6 +6,9 @@ import net.minecraft.util.ChatComponentText; import net.minecraft.util.EnumChatFormatting; import net.minecraft.util.IChatComponent; +import java.io.File; + +@SuppressWarnings("unused") public class MooChatComponent extends ChatComponentText { public MooChatComponent(String msg) { super(msg); @@ -127,13 +130,11 @@ public class MooChatComponent extends ChatComponentText { } public MooChatComponent setUrl(String url) { - setUrl(url, new KeyValueTooltipComponent("Click to visit", url)); - return this; + return setUrl(url, new KeyValueTooltipComponent("Click to visit", url)); } public MooChatComponent setUrl(String url, String hover) { - setUrl(url, new MooChatComponent(hover).yellow()); - return this; + return setUrl(url, new MooChatComponent(hover).yellow()); } public MooChatComponent setUrl(String url, IChatComponent hover) { @@ -142,6 +143,12 @@ public class MooChatComponent extends ChatComponentText { return this; } + public MooChatComponent setOpenFile(File filePath) { + setChatStyle(getChatStyle().setChatClickEvent(new ClickEvent(ClickEvent.Action.OPEN_FILE, filePath.getAbsolutePath()))); + setHover(new MooChatComponent(filePath.isFile() ? "Open " + filePath.getName() : "Open folder: " + filePath.toString()).yellow()); + return this; + } + public MooChatComponent setSuggestCommand(String command) { setSuggestCommand(command, true); return this; @@ -155,6 +162,12 @@ public class MooChatComponent extends ChatComponentText { return this; } + @Override + public MooChatComponent appendSibling(IChatComponent component) { + super.appendSibling(component); + return this; + } + /** * Appends the given component in a new line, without inheriting formatting of previous siblings. * |
