diff --git a/ReadMe.md b/ReadMe.md index 572f862cc..e56221699 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -47,7 +47,7 @@ After pushing it, it is supported. Some versions are more complicated and need a Check out [wiki.vg](https://wiki.vg/Main_Page) for more information. ### Supported versions -Almost all versions are supported, starting with 13w41b (pre 1.7 snapshot) and ending with the newest (1.16.4 as of writing this). I plan to maintain Minosoft to at least version 1.20, so stay tuned. +Almost all versions are supported, starting with 13w41b (pre 1.7 snapshot) and ending with the newest (1.16.4 as of writing this). I plan to maintain Minosoft to at least version 1.20, so stay tuned. Old versions are supported, but it is highly recommended using the latest stable release. ### Unsupported versions Sadly, we cannot support all versions. We will add support for all major versions (like 1.8.9, 1.9.4, 1.14.4, etc), but it is simply not possible to support all snapshots. diff --git a/src/main/java/de/bixilon/minosoft/data/entities/EntityMetaData.java b/src/main/java/de/bixilon/minosoft/data/entities/EntityMetaData.java index 3355aa287..741303823 100644 --- a/src/main/java/de/bixilon/minosoft/data/entities/EntityMetaData.java +++ b/src/main/java/de/bixilon/minosoft/data/entities/EntityMetaData.java @@ -229,7 +229,7 @@ public class EntityMetaData { public ChatComponent getChatComponent(EntityMetaDataFields field) { Object object = get(field); if (object instanceof String string) { - return ChatComponent.fromString(string); + return ChatComponent.valueOf(string); } return (ChatComponent) object; } diff --git a/src/main/java/de/bixilon/minosoft/data/entities/EntityMetaDataFields.java b/src/main/java/de/bixilon/minosoft/data/entities/EntityMetaDataFields.java index 9b3851d52..1e2bf3c64 100644 --- a/src/main/java/de/bixilon/minosoft/data/entities/EntityMetaDataFields.java +++ b/src/main/java/de/bixilon/minosoft/data/entities/EntityMetaDataFields.java @@ -259,7 +259,7 @@ public enum EntityMetaDataFields { MINECART_FURNACE_HAS_FUEL(false), MINECART_COMMAND_BLOCK_COMMAND(""), - MINECART_COMMAND_BLOCK_LAST_OUTPUT(ChatComponent.fromString("")), + MINECART_COMMAND_BLOCK_LAST_OUTPUT(ChatComponent.valueOf("")), PRIMED_TNT_FUSE_TIME(80), diff --git a/src/main/java/de/bixilon/minosoft/data/inventory/Slot.java b/src/main/java/de/bixilon/minosoft/data/inventory/Slot.java index 7e6af1d77..b54f189a7 100644 --- a/src/main/java/de/bixilon/minosoft/data/inventory/Slot.java +++ b/src/main/java/de/bixilon/minosoft/data/inventory/Slot.java @@ -70,11 +70,11 @@ public class Slot { if (nbt.containsKey("display")) { CompoundTag display = nbt.getCompoundTag("display"); if (display.containsKey("Name")) { - this.customDisplayName = ChatComponent.fromString(display.getStringTag("Name").getValue()); + this.customDisplayName = ChatComponent.valueOf(display.getStringTag("Name").getValue()); } if (display.containsKey("Lore")) { for (StringTag lore : display.getListTag("Lore").getValue()) { - this.lore.add(ChatComponent.fromString(lore.getValue())); + this.lore.add(ChatComponent.valueOf(lore.getValue())); } } } diff --git a/src/main/java/de/bixilon/minosoft/data/player/PlayerListItem.java b/src/main/java/de/bixilon/minosoft/data/player/PlayerListItem.java index 0f9f1a41d..53448c96e 100644 --- a/src/main/java/de/bixilon/minosoft/data/player/PlayerListItem.java +++ b/src/main/java/de/bixilon/minosoft/data/player/PlayerListItem.java @@ -79,7 +79,7 @@ public class PlayerListItem { } public ChatComponent getDisplayName() { - return (hasDisplayName() ? displayName : ChatComponent.fromString(name)); + return (hasDisplayName() ? displayName : ChatComponent.valueOf(name)); } public void setDisplayName(ChatComponent displayName) { diff --git a/src/main/java/de/bixilon/minosoft/data/player/PlayerListItemBulk.java b/src/main/java/de/bixilon/minosoft/data/player/PlayerListItemBulk.java index 510e9d2ea..fc0a51e06 100644 --- a/src/main/java/de/bixilon/minosoft/data/player/PlayerListItemBulk.java +++ b/src/main/java/de/bixilon/minosoft/data/player/PlayerListItemBulk.java @@ -72,7 +72,7 @@ public class PlayerListItemBulk { } public ChatComponent getDisplayName() { - return (hasDisplayName() ? displayName : ChatComponent.fromString(name)); + return (hasDisplayName() ? displayName : ChatComponent.valueOf(name)); } public boolean hasDisplayName() { diff --git a/src/main/java/de/bixilon/minosoft/data/text/BaseComponent.java b/src/main/java/de/bixilon/minosoft/data/text/BaseComponent.java index f7c3991eb..2f1a44b7b 100644 --- a/src/main/java/de/bixilon/minosoft/data/text/BaseComponent.java +++ b/src/main/java/de/bixilon/minosoft/data/text/BaseComponent.java @@ -98,7 +98,7 @@ public class BaseComponent extends ChatComponent { @SuppressWarnings("unchecked") public BaseComponent(@Nullable TextComponent parent, JsonElement data) { - TextComponent thisTextComponent = null; + MultiChatComponent thisTextComponent = null; if (data instanceof JsonObject json) { if (json.has("text")) { String text = json.get("text").getAsString(); @@ -143,7 +143,14 @@ public class BaseComponent extends ChatComponent { if (json.has("obfuscated")) { formattingCodes.addOrRemove(PreChatFormattingCodes.OBFUSCATED, json.get("obfuscated").getAsBoolean()); } - thisTextComponent = new TextComponent(text, color, formattingCodes); + + thisTextComponent = new MultiChatComponent(text, color, formattingCodes); + if (json.has("clickEvent")) { + thisTextComponent.setClickEvent(new ClickEvent(json.get("clickEvent").getAsJsonObject())); + } + if (json.has("hoverEvent")) { + thisTextComponent.setHoverEvent(new HoverEvent(json.get("hoverEvent").getAsJsonObject())); + } } if (thisTextComponent != null) { diff --git a/src/main/java/de/bixilon/minosoft/data/text/ChatComponent.java b/src/main/java/de/bixilon/minosoft/data/text/ChatComponent.java index 3104c5209..d28c6b76c 100644 --- a/src/main/java/de/bixilon/minosoft/data/text/ChatComponent.java +++ b/src/main/java/de/bixilon/minosoft/data/text/ChatComponent.java @@ -16,6 +16,7 @@ package de.bixilon.minosoft.data.text; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import com.google.gson.JsonParser; +import com.google.gson.JsonPrimitive; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.scene.Node; @@ -23,19 +24,29 @@ import javafx.scene.Node; import javax.annotation.Nullable; public abstract class ChatComponent { - public static ChatComponent fromString(String raw) { - return fromString(null, raw); + public static ChatComponent valueOf(Object raw) { + return valueOf(null, raw); } - public static ChatComponent fromString(@Nullable TextComponent parent, String raw) { + public static ChatComponent valueOf(@Nullable TextComponent parent, Object raw) { if (raw == null) { return new BaseComponent(); } + if (raw instanceof JsonPrimitive primitive) { + raw = primitive.getAsString(); + } + JsonObject json; - try { - json = JsonParser.parseString(raw).getAsJsonObject(); - } catch (JsonParseException | IllegalStateException ignored) { - return new BaseComponent(raw); + if (raw instanceof JsonObject) { + json = (JsonObject) raw; + } else if (raw instanceof String) { + try { + json = JsonParser.parseString((String) raw).getAsJsonObject(); + } catch (JsonParseException | IllegalStateException ignored) { + return new BaseComponent((String) raw); + } + } else { + throw new IllegalArgumentException(String.format("%s is not a valid type here!", raw.getClass().getSimpleName())); } return new BaseComponent(parent, json); } diff --git a/src/main/java/de/bixilon/minosoft/data/text/ClickEvent.java b/src/main/java/de/bixilon/minosoft/data/text/ClickEvent.java new file mode 100644 index 000000000..fe2fb6214 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/text/ClickEvent.java @@ -0,0 +1,52 @@ +/* + * Minosoft + * Copyright (C) 2020 Moritz Zwerger + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program.If not, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.data.text; + +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; + +public class ClickEvent { + private final ClickEventActions action; + private final Object value; + + public ClickEvent(JsonObject json) { + this.action = ClickEventActions.valueOf(json.get("action").getAsString().toUpperCase()); + JsonPrimitive primitive = json.get("value").getAsJsonPrimitive(); + if (primitive.isNumber()) { + this.value = primitive.getAsNumber(); + } else { + this.value = primitive.getAsString(); + } + } + + public ClickEvent(ClickEventActions action, Object value) { + this.action = action; + this.value = value; + } + + public ClickEventActions getAction() { + return action; + } + + public Object getValue() { + return value; + } + + public enum ClickEventActions { + OPEN_URL, + RUN_COMMAND, + SUGGEST_COMMAND, + CHANGE_PAGE + } +} diff --git a/src/main/java/de/bixilon/minosoft/data/text/HoverEvent.java b/src/main/java/de/bixilon/minosoft/data/text/HoverEvent.java new file mode 100644 index 000000000..2504a4b9c --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/text/HoverEvent.java @@ -0,0 +1,63 @@ +/* + * Minosoft + * Copyright (C) 2020 Moritz Zwerger + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program.If not, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.data.text; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import de.bixilon.minosoft.data.mappings.ModIdentifier; +import de.bixilon.minosoft.util.Util; + +import java.util.UUID; + +public class HoverEvent { + private final HoverEventActions action; + private final Object value; // TextComponent, NBT, Entity, Achievement Id + + public HoverEvent(JsonObject json) { + this.action = HoverEventActions.valueOf(json.get("action").getAsString().toUpperCase()); + JsonElement data = json.get("value"); + value = switch (action) { // ToDo + case SHOW_TEXT -> ChatComponent.valueOf(data); + case SHOW_ENTITY -> EntityHoverData.deserialize(JsonParser.parseString(json.get("value").getAsString()).getAsJsonObject()); + default -> null; + }; + } + + public HoverEvent(HoverEventActions action, Object value) { + this.action = action; + if (!(value instanceof ChatComponent) && !(value instanceof EntityHoverData)) { + throw new IllegalArgumentException(String.format("%s is not a valid value hier", value.getClass().getSimpleName())); + } + this.value = value; + } + + public Object getValue() { + return value; + } + + public enum HoverEventActions { + SHOW_TEXT, + SHOW_ITEM, + SHOW_ENTITY, + SHOW_ACHIEVEMENT + } + + public static record EntityHoverData(UUID uuid, ModIdentifier identifier, ChatComponent name) { + + public static EntityHoverData deserialize(JsonObject json) { + return new EntityHoverData(Util.getUUIDFromString(json.get("id").getAsString()), new ModIdentifier(json.get("type").getAsString()), ChatComponent.valueOf(json.get("name").getAsString())); + } + } +} diff --git a/src/main/java/de/bixilon/minosoft/data/text/MultiChatComponent.java b/src/main/java/de/bixilon/minosoft/data/text/MultiChatComponent.java new file mode 100644 index 000000000..28eb15e3c --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/text/MultiChatComponent.java @@ -0,0 +1,55 @@ +/* + * Minosoft + * Copyright (C) 2020 Moritz Zwerger + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program.If not, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.data.text; + +import de.bixilon.minosoft.util.hash.BetterHashSet; + +public class MultiChatComponent extends TextComponent { + private ClickEvent clickEvent; + private HoverEvent hoverEvent; + + public MultiChatComponent(String text, RGBColor color, BetterHashSet formatting, ClickEvent clickEvent, HoverEvent hoverEvent) { + super(text, color, formatting); + this.clickEvent = clickEvent; + this.hoverEvent = hoverEvent; + } + + public MultiChatComponent(String text, RGBColor color, BetterHashSet formatting) { + super(text, color, formatting); + } + + public MultiChatComponent(String text, RGBColor color) { + super(text, color); + } + + public MultiChatComponent(String text) { + super(text); + } + + public ClickEvent getClickEvent() { + return clickEvent; + } + + public void setClickEvent(ClickEvent clickEvent) { + this.clickEvent = clickEvent; + } + + public HoverEvent getHoverEvent() { + return hoverEvent; + } + + public void setHoverEvent(HoverEvent hoverEvent) { + this.hoverEvent = hoverEvent; + } +} diff --git a/src/main/java/de/bixilon/minosoft/data/text/TranslatableComponent.java b/src/main/java/de/bixilon/minosoft/data/text/TranslatableComponent.java index ceb0e3893..af905d4a4 100644 --- a/src/main/java/de/bixilon/minosoft/data/text/TranslatableComponent.java +++ b/src/main/java/de/bixilon/minosoft/data/text/TranslatableComponent.java @@ -39,7 +39,7 @@ public class TranslatableComponent extends ChatComponent { } data.forEach((jsonElement -> { if (jsonElement.isJsonPrimitive()) { - this.data.add(ChatComponent.fromString(parent, jsonElement.getAsString())); + this.data.add(ChatComponent.valueOf(parent, jsonElement.getAsString())); } else { this.data.add(new BaseComponent(parent, jsonElement.getAsJsonObject())); } diff --git a/src/main/java/de/bixilon/minosoft/protocol/ping/ServerListPing.java b/src/main/java/de/bixilon/minosoft/protocol/ping/ServerListPing.java index 0ec157282..e0aa08dde 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/ping/ServerListPing.java +++ b/src/main/java/de/bixilon/minosoft/protocol/ping/ServerListPing.java @@ -46,7 +46,7 @@ public class ServerListPing { } if (json.get("description").isJsonPrimitive()) { - motd = ChatComponent.fromString(json.get("description").getAsString()); + motd = ChatComponent.valueOf(json.get("description").getAsString()); } else { motd = new BaseComponent(json.getAsJsonObject("description")); } diff --git a/src/main/java/de/bixilon/minosoft/protocol/protocol/InByteBuffer.java b/src/main/java/de/bixilon/minosoft/protocol/protocol/InByteBuffer.java index 0e853abdc..a943e00ff 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/protocol/InByteBuffer.java +++ b/src/main/java/de/bixilon/minosoft/protocol/protocol/InByteBuffer.java @@ -200,7 +200,7 @@ public class InByteBuffer { } public ChatComponent readTextComponent() { - return ChatComponent.fromString(readString()); + return ChatComponent.valueOf(readString()); } public int getLength() { diff --git a/src/main/java/de/bixilon/minosoft/util/Util.java b/src/main/java/de/bixilon/minosoft/util/Util.java index ec2cafa20..f828adbe6 100644 --- a/src/main/java/de/bixilon/minosoft/util/Util.java +++ b/src/main/java/de/bixilon/minosoft/util/Util.java @@ -38,6 +38,7 @@ import java.util.zip.*; public final class Util { public static final Pattern UUID_FIX = Pattern.compile("(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})"); // thanks https://www.spigotmc.org/threads/free-code-easily-convert-between-trimmed-and-full-uuids.165615 public static final String RANDOM_STRING_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + public static final String LINE_SEPARATOR = System.getProperty("line.separator"); private static final Random random = ThreadLocalRandom.current(); public static UUID getUUIDFromString(String uuid) { @@ -167,11 +168,10 @@ public final class Util { public static String readFile(BufferedReader reader, boolean closeStream) throws IOException { StringBuilder stringBuilder = new StringBuilder(); - String ls = System.getProperty("line.separator"); String line; while ((line = reader.readLine()) != null) { stringBuilder.append(line); - stringBuilder.append(ls); + stringBuilder.append(LINE_SEPARATOR); } stringBuilder.deleteCharAt(stringBuilder.length() - 1); if (closeStream) { diff --git a/src/main/resources/layout/style.css b/src/main/resources/layout/style.css index fa475d19c..0224ec605 100644 --- a/src/main/resources/layout/style.css +++ b/src/main/resources/layout/style.css @@ -79,11 +79,11 @@ } .list-cell:selected { - -fx-background-color: -primary-color; + -fx-background-color: -primary-dark-color; } .list-cell:filled:hover { - -fx-background-color: -primary-dark-color; + -fx-background-color: -primary-color; } .menu-bar {