Support for translations in text messages, only used by DamageTypes for the moment

This commit is contained in:
jglrxavpok 2020-05-02 15:26:28 +02:00
parent e57addcbf7
commit 2bd5b1786b
10 changed files with 7 additions and 723 deletions

View File

@ -14,16 +14,10 @@ repositories {
}
def lombokDependency = 'org.projectlombok:lombok:1.18.2'
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
compileOnly lombokDependency
apt lombokDependency
// https://mvnrepository.com/artifact/io.netty/netty-all
compile group: 'io.netty', name: 'netty-all', version: '4.1.48.Final'
@ -40,6 +34,7 @@ dependencies {
implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.5'
compile 'com.github.TheMode:CommandBuilder:f893cfbfe4'
compile 'com.github.Minestom:minecraft-text:69fd808e92'
// https://mvnrepository.com/artifact/javax.vecmath/vecmath
compile group: 'javax.vecmath', name: 'vecmath', version: '1.5.2' // Used for Fastnoise

View File

@ -1,4 +0,0 @@
/**
* Utilities for Minecraft.
*/
package club.thectm.minecraft;

View File

@ -1,99 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2018 Ryan Willette
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package club.thectm.minecraft.text;
public enum ChatColor {
// Colors
BLACK,
DARK_BLUE,
DARK_GREEN,
DARK_AQUA,
DARK_RED,
DARK_PURPLE,
GOLD,
GRAY,
DARK_GRAY,
BLUE,
GREEN,
AQUA,
RED,
LIGHT_PURPLE,
YELLOW,
WHITE,
// Styles
OBFUSCATED(true),
BOLD(true),
STRIKETHROUGH(true),
UNDERLINE(true),
ITALIC(true),
RESET(true);
public static final char SECTION_SYMBOL = '\u00A7';
private static final String CHARS_STRING = "0123456789abcdefklmnor";
private static final char[] CHARS = CHARS_STRING.toCharArray();
private boolean format;
ChatColor(boolean format) {
this.format = format;
}
ChatColor() {
this(false);
}
public static boolean isValid(char c) {
return CHARS_STRING.indexOf(Character.toLowerCase(c)) != -1;
}
public static ChatColor getByCharCode(char c) {
return ChatColor.values()[CHARS_STRING.indexOf(Character.toLowerCase(c))];
}
public boolean isFormat() {
return format;
}
public char charCode() {
return CHARS[this.ordinal()];
}
public String toLegacyString() {
return String.valueOf(new char[]{SECTION_SYMBOL, this.charCode()});
}
public String toJsonString() {
return this.name().toLowerCase();
}
@Override
public String toString() {
return this.toLegacyString();
}
}

View File

@ -1,74 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2018 Ryan Willette
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package club.thectm.minecraft.text;
import com.google.gson.JsonObject;
public final class ClickEvent {
private Action action;
private String value;
public ClickEvent(Action action, String value) {
this.action = action;
this.value = value;
}
public static ClickEvent fromJson(JsonObject object) {
String action = object.getAsJsonPrimitive("action").getAsString();
String value = object.getAsJsonPrimitive("value").getAsString();
return new ClickEvent(Action.valueOf(action), value);
}
public JsonObject toJson() {
JsonObject object = new JsonObject();
object.addProperty("action", action.toString());
// CHANGE_PAGE is an integer, the rest are Strings.
if (this.action == Action.CHANGE_PAGE) {
object.addProperty("value", Integer.valueOf(value));
} else {
object.addProperty("value", value);
}
return object;
}
public enum Action {
OPEN_URL,
RUN_COMMAND,
SUGGEST_COMMAND,
// For Books
CHANGE_PAGE;
@Override
public String toString() {
return name().toLowerCase();
}
}
}

View File

@ -1,64 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2018 Ryan Willette
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package club.thectm.minecraft.text;
import com.google.gson.JsonObject;
public final class HoverEvent {
private Action action;
private String value;
public HoverEvent(Action action, String value) {
this.action = action;
this.value = value;
}
public static HoverEvent fromJson(JsonObject object) {
String action = object.getAsJsonPrimitive("action").getAsString();
String value = object.getAsJsonPrimitive("value").getAsString();
return new HoverEvent(Action.valueOf(action), value);
}
public JsonObject toJson() {
JsonObject object = new JsonObject();
object.addProperty("action", action.toString());
object.addProperty("value", value);
return object;
}
public enum Action {
SHOW_TEXT,
SHOW_ITEM,
SHOW_ENTITY;
@Override
public String toString() {
return name().toLowerCase();
}
}
}

View File

@ -1,191 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2018 Ryan Willette
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package club.thectm.minecraft.text;
/**
* Legacy text is a conversion util which will convert a "legacy" chat string into a TextObject,
* and a TextObject into a legacy string
*/
public final class LegacyText {
public static TextObject fromLegacy(String legacyText) {
return LegacyText.fromLegacy(legacyText, '&');
}
/**
* This function takes in a legacy text string and converts it into a {@link TextObject}.
* <p>
* Legacy text strings use the {@link ChatColor#SECTION_SYMBOL}. Many keyboards do not have this symbol however,
* which is probably why it was chosen. To get around this, it is common practice to substitute
* the symbol for another, then translate it later. Often '&' is used, but this can differ from person
* to person. In case the string does not have a {@link ChatColor#SECTION_SYMBOL}, the method also checks for the
* {@param characterSubstitute}
*
* @param legacyText The text to make into an object
* @param characterSubstitute The character substitute
* @return A TextObject representing the legacy text.
*/
public static TextObject fromLegacy(String legacyText, char characterSubstitute) {
TextBuilder builder = TextBuilder.of("");
TextObject currentObject = new TextObject("");
StringBuilder text = new StringBuilder();
for (int i = 0; i < legacyText.length(); i++) {
char c = legacyText.charAt(i);
if (c == ChatColor.SECTION_SYMBOL || c == characterSubstitute) {
if ((i + 1) > legacyText.length() - 1) {
// do nothing.
continue;
}
// peek at the next character.
char peek = legacyText.charAt(i + 1);
if (ChatColor.isValid(peek)) {
i += 1; // if valid
if (text.length() > 0) {
// create a new text object
currentObject.setText(text.toString());
// append the current object.
builder.appendJson(currentObject);
// reset the current object.
currentObject = new TextObject("");
// reset the buffer
text.setLength(0);
}
ChatColor color = ChatColor.getByCharCode(peek);
switch (color) {
case OBFUSCATED:
currentObject.setObfuscated(true);
break;
case BOLD:
currentObject.setBold(true);
break;
case STRIKETHROUGH:
currentObject.setStrikethrough(true);
break;
case ITALIC:
currentObject.setItalic(true);
break;
case UNDERLINE:
currentObject.setUnderlined(true);
break;
case RESET:
// Reset everything.
currentObject.setColor(ChatColor.WHITE);
currentObject.setObfuscated(false);
currentObject.setBold(false);
currentObject.setItalic(false);
currentObject.setUnderlined(false);
currentObject.setStrikethrough(false);
break;
default:
// emulate Minecraft's behavior of dropping styles that do not yet have an object.
currentObject = new TextObject("");
currentObject.setColor(color);
break;
}
} else {
text.append(c);
}
} else {
text.append(c);
}
}
// whatever we were working on when the loop exited
{
currentObject.setText(text.toString());
builder.appendJson(currentObject);
}
return builder.build();
}
public static String toLegacy(TextObject object) {
return LegacyText.toLegacy(object, ChatColor.SECTION_SYMBOL);
}
/**
* Takes an {@link TextObject} and transforms it into a legacy string.
*
* @param textObject - The {@link TextObject} to transform to.
* @param charSubstitute - The substitute character to use if you do not want to use {@link ChatColor#SECTION_SYMBOL}
* @return A legacy string representation of a text object
*/
public static String toLegacy(TextObject textObject, char charSubstitute) {
StringBuilder builder = new StringBuilder();
if (textObject.getColor() != null) {
builder.append(charSubstitute).append(textObject.getColor().charCode());
}
if (textObject.isObfuscated()) {
builder.append(charSubstitute).append(ChatColor.OBFUSCATED.charCode());
}
if (textObject.isBold()) {
builder.append(charSubstitute).append(ChatColor.BOLD.charCode());
}
if (textObject.isStrikethrough()) {
builder.append(charSubstitute).append(ChatColor.STRIKETHROUGH.charCode());
}
if (textObject.isUnderlined()) {
builder.append(charSubstitute).append(ChatColor.UNDERLINE.charCode());
}
if (textObject.isItalic()) {
builder.append(charSubstitute).append(ChatColor.ITALIC.charCode());
}
if (textObject.getColor() == ChatColor.RESET) {
builder.setLength(0);
builder.append(charSubstitute).append(ChatColor.RESET.charCode());
}
if (textObject.getText() != null && !textObject.getText().isEmpty()) {
builder.append(textObject.getText());
}
for (TextObject extra : textObject.getExtra()) {
builder.append(LegacyText.toLegacy(extra, charSubstitute));
}
return builder.toString();
}
}

View File

@ -1,112 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2018 Ryan Willette
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package club.thectm.minecraft.text;
import java.util.ArrayList;
import java.util.List;
public final class TextBuilder {
private TextObject root;
// The current text object. This will change when we append text for example
private TextObject current;
// The storage of the extra items
private List<TextObject> extra = new ArrayList<>();
private TextBuilder(TextObject root) {
this.root = root;
this.current = root;
}
public static TextBuilder of(String text) {
return new TextBuilder(new TextObject(text));
}
public static TextBuilder empty() {
return new TextBuilder(null);
}
public TextBuilder color(ChatColor color) {
this.current.setColor(color);
return this;
}
public TextBuilder italicize(boolean b) {
this.current.setItalic(b);
return this;
}
public TextBuilder bold(boolean b) {
this.current.setBold(b);
return this;
}
public TextBuilder underline(boolean b) {
this.current.setUnderlined(b);
return this;
}
public TextBuilder obfuscate(boolean b) {
this.current.setObfuscated(b);
return this;
}
public TextBuilder strikethrough(boolean b) {
this.current.setStrikethrough(b);
return this;
}
public TextBuilder clickEvent(ClickEvent clickEvent) {
this.current.setClickEvent(clickEvent);
return this;
}
public TextBuilder hoverEvent(HoverEvent hoverEvent) {
this.current.setHoverEvent(hoverEvent);
return this;
}
public TextBuilder append(String text) {
// essentially this completes what ever object we were on. No turning back!
return this.appendJson(new TextObject(text));
}
public TextBuilder appendJson(TextObject object) {
if (root == null) {
this.root = object;
this.current = object;
} else {
this.extra.add(this.current = object);
}
return this;
}
public TextObject build() {
// currently we're only adding the extras to the root.
this.root.setExtra(extra);
return this.root;
}
}

View File

@ -1,167 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2018 Ryan Willette
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package club.thectm.minecraft.text;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.ToString;
import java.util.ArrayList;
import java.util.List;
@Getter(AccessLevel.PROTECTED)
@ToString
public final class TextObject {
private String text;
private ChatColor color;
private ClickEvent clickEvent;
private HoverEvent hoverEvent;
private boolean italic, bold, underlined, obfuscated, strikethrough;
private List<TextObject> extra = new ArrayList<>();
public TextObject(String text) {
this.text = text;
}
public static TextObject fromJson(JsonObject object) {
if (object.has("text")) {
TextObject o = new TextObject(object.get("text").getAsString());
if (object.has("clickEvent")) {
o.setClickEvent(ClickEvent.fromJson(object.get("clickEvent").getAsJsonObject()));
}
if (object.has("hoverEvent")) {
o.setHoverEvent(HoverEvent.fromJson(object.get("hoverEvent").getAsJsonObject()));
}
if (object.has("color")) {
o.setColor(ChatColor.valueOf(object.get("color").getAsString().toUpperCase()));
}
if (object.has("obfuscated"))
o.setObfuscated(object.get("obfuscated").getAsBoolean());
if (object.has("italic"))
o.setItalic(object.get("italic").getAsBoolean());
if (object.has("bold"))
o.setBold(object.get("bold").getAsBoolean());
if (object.has("underlined"))
o.setUnderlined(object.get("underlined").getAsBoolean());
if (object.has("extra")) {
for (JsonElement extra : object.getAsJsonArray("extra")) {
if (extra.isJsonObject()) {
TextObject e = TextObject.fromJson(extra.getAsJsonObject());
if (e != null) {
o.extra.add(e);
}
}
}
}
return o;
}
// invalid object
return null;
}
public void setColor(ChatColor color) {
this.color = color;
}
public void setClickEvent(ClickEvent clickEvent) {
this.clickEvent = clickEvent;
}
public void setHoverEvent(HoverEvent hoverEvent) {
this.hoverEvent = hoverEvent;
}
public void setItalic(boolean italic) {
this.italic = italic;
}
public void setBold(boolean bold) {
this.bold = bold;
}
public void setUnderlined(boolean underlined) {
this.underlined = underlined;
}
public void setStrikethrough(boolean strikethrough) {
this.strikethrough = strikethrough;
}
public void setObfuscated(boolean obfuscated) {
this.obfuscated = obfuscated;
}
public void setExtra(List<TextObject> extra) {
this.extra.addAll(extra);
}
public void setText(String text) {
this.text = text;
}
public JsonObject toJson() {
JsonObject object = new JsonObject();
object.addProperty("text", text);
if (color != null)
object.addProperty("color", color.toJsonString());
if (clickEvent != null)
object.add("clickEvent", clickEvent.toJson());
if (hoverEvent != null)
object.add("hoverEvent", hoverEvent.toJson());
if (italic)
object.addProperty("italic", true);
if (bold)
object.addProperty("bold", true);
if (underlined)
object.addProperty("underlined", true);
if (obfuscated)
object.addProperty("obfuscated", true);
if (strikethrough)
object.addProperty("strikethrough", true);
if (!extra.isEmpty()) {
JsonArray array = new JsonArray();
for (TextObject ex : extra) {
array.add(ex.toJson());
}
object.add("extra", array);
}
return object;
}
}

View File

@ -251,7 +251,7 @@ public class Player extends LivingEntity {
// send death message to player
TextObject deathMessage;
if (lastDamageSource != null) {
deathMessage = lastDamageSource.buildDeathMessage();
deathMessage = lastDamageSource.buildDeathScreenMessage(this);
} else { // may happen if killed by the server without applying damage
deathMessage = TextBuilder.of("Killed by poor programming.").build();
}

View File

@ -10,8 +10,8 @@ import net.minestom.server.entity.Player;
*/
public class DamageType {
public static final DamageType VOID = new DamageType("void");
public static final DamageType GRAVITY = new DamageType("gravity");
public static final DamageType VOID = new DamageType("attack.outOfWorld");
public static final DamageType GRAVITY = new DamageType("attack.fall");
private final String identifier;
public DamageType(String identifier) {
@ -27,14 +27,14 @@ public class DamageType {
}
public TextObject buildChatMessage(Player killed) {
return TextBuilder.of(killed.getUsername() + " was killed by damage of type " + identifier).build();
return TextBuilder.ofTranslation("death."+identifier, TextBuilder.of(killed.getUsername()).build()).build();
}
public static DamageType fromPlayer(Player player) {
return new EntityDamage(player);
}
public TextObject buildDeathMessage() {
return TextBuilder.of("Killed by damage of type " + identifier).build();
public TextObject buildDeathScreenMessage(Player killed) {
return buildChatMessage(killed);
}
}