Merge branch 'entity-meta' into render

This commit is contained in:
Bixilon 2020-11-23 16:36:59 +01:00
commit 46671aa1bf
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
24 changed files with 384 additions and 250 deletions

View File

@ -2,8 +2,8 @@
# Minosoft
Minosoft is an open source minecraft client, written from scratch in java. It aims to bring more functionality and stability.
[![pipeline status](https://gitlab.bixilon.de/bixilon/minosoft/badges/master/pipeline.svg)](https://gitlab.bixilon.de/bixilon/minosoft/-/commits/master)
[![pipeline status](https://gitlab.bixilon.de/bixilon/minosoft/badges/master/pipeline.svg)](https://gitlab.bixilon.de/bixilon/minosoft/-/commits/master)
[![](https://jitpack.io/v/de.bixilon.gitlab.bixilon/minosoft.svg)](https://jitpack.io/#de.bixilon.gitlab.bixilon/minosoft)
## Feature overview
- Rendering (WIP)

View File

@ -24,7 +24,7 @@ public class StaticConfiguration {
public static boolean SKIP_MOJANG_AUTHENTICATION = false; // disables all connections to mojang
public static boolean COLORED_LOG = true; // the log should be colored with ANSI (does not affect base components)
public static boolean LOG_RELATIVE_TIME = false; // prefix all log messages with the relative start time in milliseconds instead of the formatted time
public static boolean VERBOSE_ENTITY_META_DATA_LOGGING = false; // if true, the entity meta data is getting serial
public static boolean VERBOSE_ENTITY_META_DATA_LOGGING = false; // if true, the entity meta data is getting serialized
public static String HOME_DIRECTORY;
static {

View File

@ -13,6 +13,7 @@
package de.bixilon.minosoft.data.assets;
import com.google.common.base.StandardSystemProperty;
import com.google.errorprone.annotations.DoNotCall;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
@ -260,7 +261,7 @@ public class AssetsManager {
private static String saveAsset(InputStream data) throws IOException {
File tempDestinationFile = null;
while (tempDestinationFile == null || tempDestinationFile.exists()) { // file exist? lol
tempDestinationFile = new File(System.getProperty("java.io.tmpdir") + "/minosoft/" + Util.generateRandomString(32));
tempDestinationFile = new File(System.getProperty(StandardSystemProperty.JAVA_IO_TMPDIR.value()) + "/minosoft/" + Util.generateRandomString(32));
}
Util.createParentFolderIfNotExist(tempDestinationFile);

View File

@ -13,6 +13,7 @@
package de.bixilon.minosoft.data.entities.block;
import de.bixilon.minosoft.data.text.ChatColors;
import de.bixilon.minosoft.data.text.RGBColor;
import de.bixilon.minosoft.util.nbt.tag.IntTag;
import de.bixilon.minosoft.util.nbt.tag.NBTTag;
@ -26,6 +27,10 @@ public class BedEntityMetaData extends BlockEntityMetaData {
}
public BedEntityMetaData(NBTTag nbt) {
if (nbt == null) {
color = ChatColors.RED;
return;
}
if (nbt instanceof StringTag stringTag) {
// yes, we support bed rgb colors :D
color = new RGBColor(stringTag.getValue());

View File

@ -212,11 +212,9 @@ public abstract class Entity {
@EntityMetaDataFunction(identifier = "pose")
public Poses getPose() {
if (isCrouching()) {
// crouching
return Poses.SNEAKING;
}
if (isSwimming()) {
// crouching
return Poses.SWIMMING;
}
if (isFlyingWithElytra()) {

View File

@ -16,6 +16,8 @@ package de.bixilon.minosoft.data.text;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import de.bixilon.minosoft.modding.event.events.annotations.Unsafe;
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition;
import de.bixilon.minosoft.util.hash.BetterHashSet;
import javafx.collections.ObservableList;
import javafx.scene.Node;
@ -23,9 +25,8 @@ import javax.annotation.Nullable;
import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
import java.util.ArrayList;
import java.util.HashSet;
public class BaseComponent implements ChatComponent {
public class BaseComponent extends ChatComponent {
private final ArrayList<ChatComponent> parts = new ArrayList<>();
public BaseComponent() {
@ -35,12 +36,12 @@ public class BaseComponent implements ChatComponent {
// legacy String
StringBuilder currentText = new StringBuilder();
RGBColor color = null;
HashSet<ChatFormattingCodes> formattingCodes = new HashSet<>();
BetterHashSet<ChatFormattingCode> formattingCodes = new BetterHashSet<>();
StringCharacterIterator iterator = new StringCharacterIterator(text);
while (iterator.current() != CharacterIterator.DONE) {
char c = iterator.current();
iterator.next();
if (c != '§') {
if (c != ProtocolDefinition.TEXT_COMPONENT_SPECIAL_PREFIX_CHAR) {
currentText.append(c);
continue;
}
@ -55,19 +56,19 @@ public class BaseComponent implements ChatComponent {
currentText = new StringBuilder();
}
color = nextColor;
formattingCodes = new HashSet<>();
formattingCodes = new BetterHashSet<>();
continue;
}
ChatFormattingCodes nextFormattingCode = ChatFormattingCodes.getChatFormattingCodeByChar(nextFormattingChar);
ChatFormattingCode nextFormattingCode = ChatFormattingCodes.getChatFormattingCodeByChar(nextFormattingChar);
if (nextFormattingCode != null) {
if (currentText.length() > 0) {
parts.add(new TextComponent(currentText.toString(), color, formattingCodes));
currentText = new StringBuilder();
color = null;
formattingCodes = new HashSet<>();
formattingCodes = new BetterHashSet<>();
}
formattingCodes.add(nextFormattingCode);
if (nextFormattingCode == ChatFormattingCodes.RESET) {
if (nextFormattingCode == PostChatFormattingCodes.RESET) {
// special rule here
if (currentText.length() > 0) {
// color change, add text part
@ -75,7 +76,7 @@ public class BaseComponent implements ChatComponent {
currentText = new StringBuilder();
}
color = null;
formattingCodes = new HashSet<>();
formattingCodes = new BetterHashSet<>();
}
}
}
@ -92,7 +93,7 @@ public class BaseComponent implements ChatComponent {
TextComponent thisTextComponent = null;
if (json.has("text")) {
String text = json.get("text").getAsString();
if (text.contains("§")) {
if (text.contains(String.valueOf(ProtocolDefinition.TEXT_COMPONENT_SPECIAL_PREFIX_CHAR))) {
// legacy text component
parts.add(new BaseComponent(text));
return;
@ -112,47 +113,26 @@ public class BaseComponent implements ChatComponent {
color = ChatColors.getColorByName(colorString);
}
}
HashSet<ChatFormattingCodes> formattingCodes;
BetterHashSet<ChatFormattingCode> formattingCodes;
if (parent != null && parent.getFormatting() != null) {
formattingCodes = (HashSet<ChatFormattingCodes>) parent.getFormatting().clone();
formattingCodes = (BetterHashSet<ChatFormattingCode>) parent.getFormatting().clone();
} else {
formattingCodes = new HashSet<>();
formattingCodes = new BetterHashSet<>();
}
if (json.has("bold")) {
if (json.get("bold").getAsBoolean()) {
formattingCodes.add(ChatFormattingCodes.BOLD);
} else {
formattingCodes.remove(ChatFormattingCodes.BOLD);
}
formattingCodes.addOrRemove(PreChatFormattingCodes.BOLD, json.get("bold").getAsBoolean());
}
if (json.has("italic")) {
if (json.get("italic").getAsBoolean()) {
formattingCodes.add(ChatFormattingCodes.ITALIC);
} else {
formattingCodes.remove(ChatFormattingCodes.ITALIC);
}
formattingCodes.addOrRemove(PreChatFormattingCodes.ITALIC, json.get("italic").getAsBoolean());
}
if (json.has("underlined")) {
if (json.get("underlined").getAsBoolean()) {
formattingCodes.add(ChatFormattingCodes.UNDERLINED);
} else {
formattingCodes.remove(ChatFormattingCodes.UNDERLINED);
}
formattingCodes.addOrRemove(PreChatFormattingCodes.UNDERLINED, json.get("underlined").getAsBoolean());
}
if (json.has("strikethrough")) {
if (json.get("strikethrough").getAsBoolean()) {
formattingCodes.add(ChatFormattingCodes.STRIKETHROUGH);
} else {
formattingCodes.remove(ChatFormattingCodes.STRIKETHROUGH);
}
formattingCodes.addOrRemove(PreChatFormattingCodes.STRIKETHROUGH, json.get("strikethrough").getAsBoolean());
}
if (json.has("obfuscated")) {
if (json.get("obfuscated").getAsBoolean()) {
formattingCodes.add(ChatFormattingCodes.OBFUSCATED);
} else {
formattingCodes.remove(ChatFormattingCodes.OBFUSCATED);
}
formattingCodes.addOrRemove(PreChatFormattingCodes.OBFUSCATED, json.get("obfuscated").getAsBoolean());
}
thisTextComponent = new TextComponent(text, color, formattingCodes);
}
@ -161,20 +141,20 @@ public class BaseComponent implements ChatComponent {
parts.add(thisTextComponent);
}
final TextComponent parentParameter = thisTextComponent == null ? parent : thisTextComponent;
if (json.has("extra")) {
JsonArray extras = json.getAsJsonArray("extra");
TextComponent finalThisChatPart = thisTextComponent;
extras.forEach((extra -> parts.add(new BaseComponent(finalThisChatPart, extra.getAsJsonObject()))));
extras.forEach((extra -> parts.add(new BaseComponent(parentParameter, extra.getAsJsonObject()))));
}
if (json.has("translate")) {
parts.add(new TranslatableComponent(json.get("translate").getAsString(), json.getAsJsonArray("with")));
parts.add(new TranslatableComponent(parentParameter, json.get("translate").getAsString(), json.getAsJsonArray("with")));
}
}
@Override
public String toString() {
return getANSIColoredMessage();
return PostChatFormattingCodes.RESET.getANSI() + getANSIColoredMessage();
}
public String getANSIColoredMessage() {

View File

@ -0,0 +1,17 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.text;
public interface ChatCode {
}

View File

@ -54,17 +54,17 @@ public final class ChatColors {
return getColorById(Character.digit(c, 16));
}
public static ChatFormattingCode getFormattingById(int id) {
public static ChatCode getFormattingById(int id) {
if (id <= 15) {
return getColorById(id);
}
return switch (id) {
case 16 -> ChatFormattingCodes.OBFUSCATED;
case 17 -> ChatFormattingCodes.BOLD;
case 18 -> ChatFormattingCodes.STRIKETHROUGH;
case 19 -> ChatFormattingCodes.UNDERLINED;
case 20 -> ChatFormattingCodes.ITALIC;
case 21 -> ChatFormattingCodes.RESET;
case 16 -> PreChatFormattingCodes.OBFUSCATED;
case 17 -> PreChatFormattingCodes.BOLD;
case 18 -> PreChatFormattingCodes.STRIKETHROUGH;
case 19 -> PreChatFormattingCodes.UNDERLINED;
case 20 -> PreChatFormattingCodes.ITALIC;
case 21 -> PostChatFormattingCodes.RESET;
default -> null;
};
}
@ -83,6 +83,10 @@ public final class ChatColors {
return colorIntMap.get(color);
}
public static String getColorChar(RGBColor color) {
return String.format("%x", colorIntMap.get(color));
}
public static RGBColor getColorByName(String name) {
return switch (name.toLowerCase()) {
case "black" -> BLACK;
@ -100,7 +104,7 @@ public final class ChatColors {
case "red" -> RED;
case "light_purple" -> LIGHT_PURPLE;
case "yellow" -> YELLOW;
case "white" -> WHITE;
case "white", "reset" -> WHITE;
default -> throw new IllegalStateException("Unexpected value: " + name);
};
}

View File

@ -15,16 +15,23 @@ package de.bixilon.minosoft.data.text;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Node;
public interface ChatComponent {
static ChatComponent fromString(String raw) {
import javax.annotation.Nullable;
public abstract class ChatComponent {
public static ChatComponent fromString(String raw) {
return fromString(null, raw);
}
public static ChatComponent fromString(@Nullable TextComponent parent, String raw) {
if (raw == null) {
return new BaseComponent();
}
try {
return new BaseComponent(JsonParser.parseString(raw).getAsJsonObject());
return new BaseComponent(parent, JsonParser.parseString(raw).getAsJsonObject());
} catch (JsonParseException | IllegalStateException ignored) {
}
return new BaseComponent(raw);
@ -33,20 +40,27 @@ public interface ChatComponent {
/**
* @return Returns the message formatted with ANSI Formatting codes
*/
String getANSIColoredMessage();
public abstract String getANSIColoredMessage();
/**
* @return Returns the message formatted with minecraft formatting codes (§)
*/
String getLegacyText();
public abstract String getLegacyText();
/**
* @return Returns the unformatted message
*/
String getMessage();
public abstract String getMessage();
/**
* @return Returns the a list of Nodes, drawable in JavaFX (TextFlow)
*/
ObservableList<Node> getJavaFXText(ObservableList<Node> nodes);
public abstract ObservableList<Node> getJavaFXText(ObservableList<Node> nodes);
/**
* @return Returns the a list of Nodes, drawable in JavaFX (TextFlow)
*/
public ObservableList<Node> getJavaFXText() {
return getJavaFXText(FXCollections.observableArrayList());
}
}

View File

@ -13,5 +13,11 @@
package de.bixilon.minosoft.data.text;
public interface ChatFormattingCode {
public interface ChatFormattingCode extends ChatCode {
char getChar();
@Override
String toString();
String getANSI();
}

View File

@ -17,60 +17,17 @@ import com.google.common.collect.HashBiMap;
import java.util.Arrays;
public enum ChatFormattingCodes implements ChatFormattingCode {
OBFUSCATED('k', "\u001b[5m"),
BOLD('l', "\u001b[1m"),
STRIKETHROUGH('m', "\u001b[9m"),
UNDERLINED('n', "\u001b[4m"),
ITALIC('o', "\u001b[3m"),
RESET('r', "\u001b[0m", ChatFormattingCodePosition.POST);
public final class ChatFormattingCodes {
private final static HashBiMap<Character, ChatFormattingCodes> formattingCodes = HashBiMap.create();
private static final HashBiMap<Character, ChatFormattingCode> formattingCodes = HashBiMap.create();
static {
Arrays.stream(values()).forEach(chatFormattingCodes -> formattingCodes.put(chatFormattingCodes.getChar(), chatFormattingCodes));
Arrays.stream(PreChatFormattingCodes.values()).forEach(chatFormattingCodes -> formattingCodes.put(chatFormattingCodes.getChar(), chatFormattingCodes));
Arrays.stream(PostChatFormattingCodes.values()).forEach(chatFormattingCodes -> formattingCodes.put(chatFormattingCodes.getChar(), chatFormattingCodes));
}
final char c;
final String ansi;
final ChatFormattingCodePosition position;
ChatFormattingCodes(char c, String ansi) {
this.c = c;
this.ansi = ansi;
this.position = ChatFormattingCodePosition.PRE;
}
ChatFormattingCodes(char c, String ansi, ChatFormattingCodePosition position) {
this.c = c;
this.ansi = ansi;
this.position = position;
}
public static ChatFormattingCodes getChatFormattingCodeByChar(char c) {
return formattingCodes.get(c);
}
public char getChar() {
return c;
}
public ChatFormattingCodePosition getPosition() {
return position;
}
@Override
public String toString() {
return getANSI();
}
public String getANSI() {
return ansi;
}
public enum ChatFormattingCodePosition {
PRE,
POST
public static ChatFormattingCode getChatFormattingCodeByChar(char nextFormattingChar) {
return formattingCodes.get(nextFormattingChar);
}
}

View File

@ -0,0 +1,40 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.text;
public enum PostChatFormattingCodes implements ChatFormattingCode {
RESET('r', "\u001b[0m");
final char c;
final String ansi;
PostChatFormattingCodes(char c, String ansi) {
this.c = c;
this.ansi = ansi;
}
public char getChar() {
return c;
}
@Override
public String toString() {
return getANSI();
}
public String getANSI() {
return ansi;
}
}

View File

@ -0,0 +1,46 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.text;
public enum PreChatFormattingCodes implements ChatFormattingCode {
OBFUSCATED('k', "\u001b[5m"),
BOLD('l', "\u001b[1m"),
STRIKETHROUGH('m', "\u001b[9m"),
UNDERLINED('n', "\u001b[4m"),
ITALIC('o', "\u001b[3m");
final char c;
final String ansi;
PreChatFormattingCodes(char c, String ansi) {
this.c = c;
this.ansi = ansi;
}
public char getChar() {
return c;
}
@Override
public String toString() {
return getANSI();
}
public String getANSI() {
return ansi;
}
}

View File

@ -13,7 +13,7 @@
package de.bixilon.minosoft.data.text;
public final class RGBColor implements ChatFormattingCode {
public final class RGBColor implements ChatCode {
private final int color;
public RGBColor(int color) {

View File

@ -13,6 +13,8 @@
package de.bixilon.minosoft.data.text;
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition;
import de.bixilon.minosoft.util.hash.BetterHashSet;
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
@ -22,15 +24,14 @@ import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.util.Duration;
import java.util.HashSet;
import java.util.Objects;
public class TextComponent implements ChatComponent {
public class TextComponent extends ChatComponent {
private final String text;
private RGBColor color;
private HashSet<ChatFormattingCodes> formatting;
private BetterHashSet<ChatFormattingCode> formatting;
public TextComponent(String text, RGBColor color, HashSet<ChatFormattingCodes> formatting) {
public TextComponent(String text, RGBColor color, BetterHashSet<ChatFormattingCode> formatting) {
this.text = text;
this.color = color;
this.formatting = formatting;
@ -46,56 +47,32 @@ public class TextComponent implements ChatComponent {
}
public TextComponent setObfuscated(boolean obfuscated) {
if (obfuscated) {
formatting.add(ChatFormattingCodes.OBFUSCATED);
} else {
formatting.remove(ChatFormattingCodes.OBFUSCATED);
}
formatting.addOrRemove(PreChatFormattingCodes.OBFUSCATED, obfuscated);
return this;
}
public TextComponent setBold(boolean bold) {
if (bold) {
formatting.add(ChatFormattingCodes.BOLD);
} else {
formatting.remove(ChatFormattingCodes.BOLD);
}
formatting.addOrRemove(PreChatFormattingCodes.BOLD, bold);
return this;
}
public TextComponent setStrikethrough(boolean strikethrough) {
if (strikethrough) {
formatting.add(ChatFormattingCodes.STRIKETHROUGH);
} else {
formatting.remove(ChatFormattingCodes.STRIKETHROUGH);
}
formatting.addOrRemove(PreChatFormattingCodes.STRIKETHROUGH, strikethrough);
return this;
}
public TextComponent setUnderlined(boolean underlined) {
if (underlined) {
formatting.add(ChatFormattingCodes.UNDERLINED);
} else {
formatting.remove(ChatFormattingCodes.UNDERLINED);
}
formatting.addOrRemove(PreChatFormattingCodes.UNDERLINED, underlined);
return this;
}
public TextComponent setItalic(boolean italic) {
if (italic) {
formatting.add(ChatFormattingCodes.ITALIC);
} else {
formatting.remove(ChatFormattingCodes.ITALIC);
}
formatting.addOrRemove(PreChatFormattingCodes.ITALIC, italic);
return this;
}
public TextComponent setReset(boolean reset) {
if (reset) {
formatting.add(ChatFormattingCodes.RESET);
} else {
formatting.remove(ChatFormattingCodes.RESET);
}
formatting.addOrRemove(PostChatFormattingCodes.RESET, reset);
return this;
}
@ -108,11 +85,11 @@ public class TextComponent implements ChatComponent {
return this;
}
public HashSet<ChatFormattingCodes> getFormatting() {
public BetterHashSet<ChatFormattingCode> getFormatting() {
return formatting;
}
public TextComponent setFormatting(HashSet<ChatFormattingCodes> formatting) {
public TextComponent setFormatting(BetterHashSet<ChatFormattingCode> formatting) {
this.formatting = formatting;
return this;
}
@ -142,20 +119,20 @@ public class TextComponent implements ChatComponent {
}
if (formatting != null) {
formatting.forEach((chatFormattingCodes -> {
if (chatFormattingCodes.getPosition() == ChatFormattingCodes.ChatFormattingCodePosition.PRE) {
builder.append(chatFormattingCodes.getANSI());
if (chatFormattingCodes instanceof PreChatFormattingCodes code) {
builder.append(code.getANSI());
}
}));
}
builder.append(text);
if (formatting != null) {
formatting.forEach((chatFormattingCodes -> {
if (chatFormattingCodes.getPosition() == ChatFormattingCodes.ChatFormattingCodePosition.POST) {
builder.append(chatFormattingCodes.getANSI());
if (chatFormattingCodes instanceof PostChatFormattingCodes code) {
builder.append(code.getANSI());
}
}));
}
builder.append(ChatFormattingCodes.RESET);
builder.append(PostChatFormattingCodes.RESET);
return builder.toString();
}
@ -164,11 +141,11 @@ public class TextComponent implements ChatComponent {
StringBuilder output = new StringBuilder();
Integer colorChar = ChatColors.getColorId(color);
if (colorChar != null) {
output.append('§').append(Integer.toHexString(colorChar));
output.append(ProtocolDefinition.TEXT_COMPONENT_SPECIAL_PREFIX_CHAR).append(Integer.toHexString(colorChar));
}
formatting.forEach((chatFormattingCode -> output.append('§').append(chatFormattingCode.getChar())));
formatting.forEach((chatFormattingCode -> output.append(ProtocolDefinition.TEXT_COMPONENT_SPECIAL_PREFIX_CHAR).append(chatFormattingCode.getChar())));
output.append(text);
output.append('§').append(ChatFormattingCodes.RESET.getChar());
output.append(ProtocolDefinition.TEXT_COMPONENT_SPECIAL_PREFIX_CHAR).append(PostChatFormattingCodes.RESET.getChar());
return output.toString();
}
@ -184,16 +161,18 @@ public class TextComponent implements ChatComponent {
text.setFill(Color.web(color.toString()));
}
formatting.forEach((chatFormattingCode -> {
switch (chatFormattingCode) {
case OBFUSCATED -> {
Timeline flasher = new Timeline(new KeyFrame(Duration.seconds(1), e -> text.setVisible(false)), new KeyFrame(Duration.seconds(2), e -> text.setVisible(true)));
flasher.setCycleCount(Animation.INDEFINITE);
flasher.play();
if (chatFormattingCode instanceof PreChatFormattingCodes code) {
switch (code) {
case OBFUSCATED -> {
Timeline flasher = new Timeline(new KeyFrame(Duration.seconds(1), e -> text.setVisible(false)), new KeyFrame(Duration.seconds(2), e -> text.setVisible(true)));
flasher.setCycleCount(Animation.INDEFINITE);
flasher.play();
}
case BOLD -> text.setStyle("-fx-font-weight: bold;");
case STRIKETHROUGH -> text.setStyle("-fx-strikethrough: true;");
case UNDERLINED -> text.setStyle("-fx-underline: true;");
case ITALIC -> text.setStyle("-fx-font-weight: italic;");
}
case BOLD -> text.setStyle("-fx-font-weight: bold;");
case STRIKETHROUGH -> text.setStyle("-fx-strikethrough: true;");
case UNDERLINED -> text.setStyle("-fx-underline: true;");
case ITALIC -> text.setStyle("-fx-font-weight: italic;");
}
}));
nodes.add(text);

View File

@ -15,54 +15,50 @@ package de.bixilon.minosoft.data.text;
import com.google.gson.JsonArray;
import de.bixilon.minosoft.data.locale.minecraft.MinecraftLocaleManager;
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javax.annotation.Nullable;
import java.util.ArrayList;
public class TranslatableComponent implements ChatComponent {
public class TranslatableComponent extends ChatComponent {
private final ArrayList<ChatComponent> data = new ArrayList<>();
private final String key;
private final TextComponent parent;
public TranslatableComponent(String key, JsonArray data) {
this(null, key, data);
}
public TranslatableComponent(@Nullable TextComponent parent, String key, JsonArray data) {
this.parent = parent;
this.key = key;
if (data == null) {
return;
}
data.forEach((jsonElement -> {
if (jsonElement.isJsonPrimitive()) {
this.data.add(ChatComponent.fromString(jsonElement.getAsString()));
this.data.add(ChatComponent.fromString(parent, jsonElement.getAsString()));
} else {
this.data.add(new BaseComponent(jsonElement.getAsJsonObject()));
this.data.add(new BaseComponent(parent, jsonElement.getAsJsonObject()));
}
}));
}
@Override
public String getANSIColoredMessage() {
Object[] data = new String[this.data.size()];
for (int i = 0; i < this.data.size(); i++) {
data[i] = this.data.get(i).getANSIColoredMessage();
}
return MinecraftLocaleManager.translate(key, data);
return getList("getANSIColoredMessage");
}
@Override
public String getLegacyText() {
Object[] data = new String[this.data.size()];
for (int i = 0; i < this.data.size(); i++) {
data[i] = this.data.get(i).getLegacyText();
}
return MinecraftLocaleManager.translate(key, data);
return getList("getLegacyText");
}
@Override
public String getMessage() {
Object[] data = new String[this.data.size()];
for (int i = 0; i < this.data.size(); i++) {
data[i] = this.data.get(i).getMessage();
}
return MinecraftLocaleManager.translate(key, data);
return getList("getMessage");
}
@Override
@ -72,4 +68,46 @@ public class TranslatableComponent implements ChatComponent {
// This is just a dirty workaround to enable formatting and coloring. Still need to do hover, click, ... stuff
return new BaseComponent(getLegacyText()).getJavaFXText(nodes);
}
// just used reflections to not write this twice anc only change the method name
private String getList(String methodName) {
try {
Object[] data = new String[this.data.size()];
for (int i = 0; i < this.data.size(); i++) {
data[i] = this.data.get(i).getClass().getMethod(methodName).invoke(this.data.get(i));
}
if (parent != null) {
StringBuilder builder = new StringBuilder();
if (methodName.equals("getANSIColoredMessage")) {
builder.append(ChatColors.getANSIColorByRGBColor(parent.getColor()));
} else if (methodName.equals("getLegacyText")) {
builder.append(ChatColors.getColorChar(parent.getColor()));
}
for (ChatFormattingCode code : parent.getFormatting()) {
if (code instanceof PreChatFormattingCodes preCode) {
builder.append(switch (methodName) {
case "getANSIColoredMessage" -> preCode.getANSI();
case "getLegacyText" -> ProtocolDefinition.TEXT_COMPONENT_SPECIAL_PREFIX_CHAR + preCode.getChar();
default -> "";
});
}
}
builder.append(MinecraftLocaleManager.translate(key, data));
for (ChatFormattingCode code : parent.getFormatting()) {
if (code instanceof PostChatFormattingCodes postCode) {
builder.append(switch (methodName) {
case "getANSIColoredMessage" -> postCode.getANSI();
case "getLegacyText" -> ProtocolDefinition.TEXT_COMPONENT_SPECIAL_PREFIX_CHAR + postCode.getChar();
default -> "";
});
}
}
return builder.toString();
}
return MinecraftLocaleManager.translate(key, data);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@ -28,7 +28,6 @@ import de.bixilon.minosoft.protocol.ping.ForgeModInfo;
import de.bixilon.minosoft.protocol.ping.ServerListPing;
import de.bixilon.minosoft.util.DNSUtil;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.geometry.Insets;
@ -178,7 +177,7 @@ public class ServerListCell extends ListCell<Server> implements Initializable {
}
serverBrand.setText(ping.getServerModInfo().getBrand());
serverBrand.setTooltip(new Tooltip(ping.getServerModInfo().getInfo()));
motd.getChildren().addAll(ping.getMotd().getJavaFXText(FXCollections.observableArrayList()));
motd.getChildren().addAll(ping.getMotd().getJavaFXText());
if (ping.getFavicon() != null) {
icon.setImage(GUITools.getImage(ping.getFavicon()));
if (!Arrays.equals(ping.getFavicon(), server.getFavicon())) {
@ -416,7 +415,7 @@ public class ServerListCell extends ListCell<Server> implements Initializable {
Label serverBrandLabel = new Label(lastPing.getServerBrand());
Label playersOnlineMaxLabel = new Label(LocaleManager.translate(Strings.SERVER_INFO_SLOTS_PLAYERS_ONLINE, lastPing.getPlayerOnline(), lastPing.getMaxPlayers()));
TextFlow motdLabel = new TextFlow();
motdLabel.getChildren().addAll(lastPing.getMotd().getJavaFXText(FXCollections.observableArrayList()));
motdLabel.getChildren().addAll(lastPing.getMotd().getJavaFXText());
Label moddedBrandLabel = new Label(lastPing.getServerModInfo().getBrand());
grid.add(new Label(LocaleManager.translate(Strings.SERVER_INFO_REAL_SERVER_ADDRESS) + ":"), 0, ++column);

View File

@ -15,7 +15,7 @@ package de.bixilon.minosoft.logging;
import de.bixilon.minosoft.config.StaticConfiguration;
import de.bixilon.minosoft.data.text.ChatColors;
import de.bixilon.minosoft.data.text.ChatFormattingCodes;
import de.bixilon.minosoft.data.text.PostChatFormattingCodes;
import de.bixilon.minosoft.data.text.RGBColor;
import java.text.SimpleDateFormat;
@ -45,18 +45,24 @@ public class Log {
}, "Log").start();
}
public static void log(LogLevels level, String message, RGBColor color) {
log(level, "", message, color);
public static void log(LogLevels level, RGBColor color, Object message, Object... format) {
log(level, "", color, message, format);
}
public static void log(LogLevels level, String prefix, String message, RGBColor color) {
if (message.isBlank()) {
return;
}
public static void log(LogLevels level, String prefix, RGBColor color, Object message, Object... format) {
if (level.ordinal() > Log.level.ordinal()) {
// log level too low
return;
}
if (message == null) {
return;
}
if (message instanceof String string) {
if (string.isBlank()) {
return;
}
message = String.format(string, format);
}
StringBuilder builder = new StringBuilder();
builder.append("[");
if (StaticConfiguration.LOG_RELATIVE_TIME) {
@ -73,11 +79,11 @@ public class Log {
if (color != null && StaticConfiguration.COLORED_LOG) {
builder.append(ChatColors.getANSIColorByRGBColor(color));
builder.append(message);
builder.append(ChatFormattingCodes.RESET.getANSI());
builder.append(PostChatFormattingCodes.RESET.getANSI());
} else {
builder.append(message);
}
builder.append(ChatFormattingCodes.RESET.getANSI());
builder.append(PostChatFormattingCodes.RESET.getANSI());
queue.add(builder.toString());
}
@ -86,8 +92,8 @@ public class Log {
*
* @param message Raw message to log
*/
public static void game(String message) {
log(LogLevels.GAME, message, ChatColors.GREEN);
public static void game(Object message, Object... format) {
log(LogLevels.GAME, ChatColors.GREEN, message, format);
}
/**
@ -95,8 +101,8 @@ public class Log {
*
* @param message Raw message to log
*/
public static void fatal(String message) {
log(LogLevels.FATAL, message, ChatColors.DARK_RED);
public static void fatal(Object message, Object... format) {
log(LogLevels.FATAL, ChatColors.DARK_RED, message, format);
}
/**
@ -104,8 +110,8 @@ public class Log {
*
* @param message Raw message to log
*/
public static void warn(String message) {
log(LogLevels.WARNING, message, ChatColors.RED);
public static void warn(Object message, Object... format) {
log(LogLevels.WARNING, ChatColors.RED, message, format);
}
/**
@ -113,8 +119,8 @@ public class Log {
*
* @param message Raw message to log
*/
public static void debug(String message) {
log(LogLevels.DEBUG, message, ChatColors.GRAY);
public static void debug(Object message, Object... format) {
log(LogLevels.DEBUG, ChatColors.GRAY, message, format);
}
/**
@ -122,8 +128,8 @@ public class Log {
*
* @param message Raw message to log
*/
public static void verbose(String message) {
log(LogLevels.VERBOSE, message, ChatColors.YELLOW);
public static void verbose(Object message, Object... format) {
log(LogLevels.VERBOSE, ChatColors.YELLOW, message, format);
}
/**
@ -131,8 +137,8 @@ public class Log {
*
* @param message Raw message to log
*/
public static void protocol(String message) {
log(LogLevels.PROTOCOL, message, ChatColors.BLUE);
public static void protocol(Object message, Object... format) {
log(LogLevels.PROTOCOL, ChatColors.BLUE, message, format);
}
/**
@ -140,10 +146,20 @@ public class Log {
*
* @param message Raw message to log
*/
public static void mojang(String message) {
log(LogLevels.MOJANG, message, ChatColors.AQUA);
public static void mojang(Object message, Object... format) {
log(LogLevels.MOJANG, ChatColors.AQUA, message, format);
}
/**
* Logs all general infos, that are more or less important to the user (connecting to server, ...)
*
* @param message Raw message to log
*/
public static void info(Object message, Object... format) {
log(LogLevels.INFO, ChatColors.WHITE, message, format);
}
public static LogLevels getLevel() {
return level;
}
@ -156,15 +172,6 @@ public class Log {
Log.level = level;
}
/**
* Logs all general infos, that are more or less important to the user (connecting to server, ...)
*
* @param message Raw message to log
*/
public static void info(String message) {
log(LogLevels.INFO, message, ChatColors.WHITE);
}
public static boolean printException(Exception exception, LogLevels minimumLogLevel) {
// ToDo: log to file, print also exceptions that are not printed with this method
if (Log.getLevel().ordinal() >= minimumLogLevel.ordinal()) {

View File

@ -25,31 +25,31 @@ public class Logger {
this.modName = modName;
}
public void log(LogLevels level, String message, RGBColor color) {
Log.log(level, String.format("[%s] ", modName), message, color);
public void log(LogLevels level, RGBColor color, Object message, Object... format) {
Log.log(level, String.format("[%s] ", modName), color, message, format);
}
public void game(String message) {
log(LogLevels.GAME, message, ChatColors.GREEN);
public void game(Object message, Object... format) {
log(LogLevels.GAME, ChatColors.GREEN, message, format);
}
public void fatal(String message) {
log(LogLevels.FATAL, message, ChatColors.DARK_RED);
public void fatal(Object message, Object... format) {
log(LogLevels.FATAL, ChatColors.DARK_RED, message, format);
}
public void info(String message) {
log(LogLevels.INFO, message, ChatColors.WHITE);
public void info(Object message, Object... format) {
log(LogLevels.INFO, ChatColors.WHITE, message, format);
}
public void warn(String message) {
log(LogLevels.WARNING, message, ChatColors.RED);
public void warn(Object message, Object... format) {
log(LogLevels.WARNING, ChatColors.RED, message, format);
}
public void debug(String message) {
log(LogLevels.DEBUG, message, ChatColors.GRAY);
public void debug(Object message, Object... format) {
log(LogLevels.DEBUG, ChatColors.GRAY, message, format);
}
public void verbose(String message) {
log(LogLevels.VERBOSE, message, ChatColors.YELLOW);
public void verbose(Object message, Object... format) {
log(LogLevels.VERBOSE, ChatColors.YELLOW, message, format);
}
}

View File

@ -23,7 +23,6 @@ import org.xeustechnologies.jcl.JarClassLoader;
import org.xeustechnologies.jcl.JclObjectFactory;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
@ -87,7 +86,12 @@ public class ModLoader {
progress.countDown();
return;
}
if (!instance.start(phase)) {
try {
if (!instance.start(phase)) {
throw new ModLoadingException(String.format("Could not load nod %s", instance.getInfo()));
}
} catch (Throwable e) {
e.printStackTrace();
Log.warn(String.format("An error occurred while loading %s", instance.getInfo()));
instance.setEnabled(false);
}
@ -127,7 +131,7 @@ public class ModLoader {
instance.setInfo(modInfo);
Log.verbose(String.format("[MOD] Mod file loaded and added to classpath (%s)", modInfo));
zipFile.close();
} catch (IOException | ModLoadingException | NullPointerException e) {
} catch (Throwable e) {
instance = null;
e.printStackTrace();
Log.warn(String.format("Could not load mod: %s", file.getAbsolutePath()));

View File

@ -13,9 +13,9 @@
package de.bixilon.minosoft.protocol.packets.clientbound.play;
import de.bixilon.minosoft.data.text.ChatCode;
import de.bixilon.minosoft.data.text.ChatColors;
import de.bixilon.minosoft.data.text.ChatComponent;
import de.bixilon.minosoft.data.text.ChatFormattingCode;
import de.bixilon.minosoft.logging.Log;
import de.bixilon.minosoft.protocol.packets.ClientboundPacket;
import de.bixilon.minosoft.protocol.protocol.InByteBuffer;
@ -32,7 +32,7 @@ public class PacketTeams implements ClientboundPacket {
boolean seeFriendlyInvisibles;
TeamCollisionRules collisionRule = TeamCollisionRules.NEVER;
TeamNameTagVisibilities nameTagVisibility = TeamNameTagVisibilities.ALWAYS;
ChatFormattingCode formattingCode;
ChatCode formattingCode;
String[] playerNames;
@Override
@ -133,7 +133,7 @@ public class PacketTeams implements ClientboundPacket {
return seeFriendlyInvisibles;
}
public ChatFormattingCode getFormattingCode() {
public ChatCode getFormattingCode() {
return formattingCode;
}

View File

@ -278,15 +278,15 @@ public class PacketHandler {
}
Log.game(switch (pkg.getReason()) {
case STOP_RAINING -> "Received weather packet: Starting rain...";
case START_RAINING -> "Received weather packet: Stopping rain...";
case START_RAINING -> "Received weather packet: Starting rain...";
case STOP_RAINING -> "Received weather packet: Stopping rain...";
case CHANGE_GAMEMODE -> String.format("Received game mode change: Now in %s", GameModes.byId(pkg.getValue().intValue()));
default -> "";
});
switch (pkg.getReason()) {
case STOP_RAINING -> connection.getPlayer().getWorld().setRaining(true);
case START_RAINING -> connection.getPlayer().getWorld().setRaining(false);
case STOP_RAINING -> connection.getPlayer().getWorld().setRaining(false);
case START_RAINING -> connection.getPlayer().getWorld().setRaining(true);
case CHANGE_GAMEMODE -> connection.getPlayer().setGameMode(GameModes.byId(pkg.getValue().intValue()));
}
}

View File

@ -14,7 +14,6 @@
package de.bixilon.minosoft.protocol.protocol;
import java.net.InetAddress;
import java.net.UnknownHostException;
public final class ProtocolDefinition {
public static final int STRING_MAX_LEN = 32767;
@ -36,15 +35,19 @@ public final class ProtocolDefinition {
public static final int LAN_SERVER_MAXIMUM_SERVERS = 100; // maximum number of lan servers, set because otherwise dos attacks would be easy
public static final String DEFAULT_MOD = "minecraft";
public static final char TEXT_COMPONENT_SPECIAL_PREFIX_CHAR = '§';
public static final int DEFAULT_BUFFER_SIZE = 4096;
static {
// java does (why ever) not allow to directly assign a null
InetAddress temp;
try {
LAN_SERVER_BROADCAST_ADDRESS = InetAddress.getByName("224.0.2.60");
} catch (UnknownHostException e) {
temp = InetAddress.getByName("224.0.2.60");
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
temp = null;
}
LAN_SERVER_BROADCAST_ADDRESS = temp;
}
}

View File

@ -0,0 +1,36 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.util.hash;
import java.util.Collection;
import java.util.HashSet;
public class BetterHashSet<E> extends HashSet<E> {
public BetterHashSet() {
super();
}
public BetterHashSet(Collection<? extends E> c) {
super(c);
}
public void addOrRemove(E value, boolean addOrRemove) {
if (addOrRemove) {
super.add(value);
} else {
super.remove(value);
}
}
}