mirror of https://github.com/Minestom/Minestom.git
Added json message & scoreboard API
This commit is contained in:
parent
879f9e7c42
commit
0fc6234b72
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* Utilities for Minecraft.
|
||||
*/
|
||||
package club.thectm.minecraft;
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
* 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();
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
|
@ -104,10 +104,10 @@ public class Main {
|
|||
|
||||
// Keep Alive Handling
|
||||
for (Player player : getConnectionManager().getOnlinePlayers()) {
|
||||
if (System.currentTimeMillis() - player.getLastKeepAlive() > 20000) {
|
||||
long id = System.currentTimeMillis();
|
||||
player.refreshKeepAlive(id);
|
||||
KeepAlivePacket keepAlivePacket = new KeepAlivePacket(id);
|
||||
long time = System.currentTimeMillis();
|
||||
if (time - player.getLastKeepAlive() > 20000) {
|
||||
player.refreshKeepAlive(time);
|
||||
KeepAlivePacket keepAlivePacket = new KeepAlivePacket(time);
|
||||
player.getPlayerConnection().sendPacket(keepAlivePacket);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package fr.themode.minestom.bossbar;
|
||||
|
||||
import fr.themode.minestom.Viewable;
|
||||
import fr.themode.minestom.chat.Chat;
|
||||
import fr.themode.minestom.entity.Player;
|
||||
import fr.themode.minestom.net.packet.server.play.BossBarPacket;
|
||||
|
||||
|
@ -22,7 +21,7 @@ public class BossBar implements Viewable {
|
|||
private byte flags;
|
||||
|
||||
public BossBar(String title, BarColor color, BarDivision division) {
|
||||
this.title = Chat.rawText(title);
|
||||
this.title = title;
|
||||
this.color = color;
|
||||
this.division = division;
|
||||
}
|
||||
|
@ -51,7 +50,7 @@ public class BossBar implements Viewable {
|
|||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = Chat.rawText(title);
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public float getProgress() {
|
||||
|
|
|
@ -1,15 +1,33 @@
|
|||
package fr.themode.minestom.chat;
|
||||
|
||||
import club.thectm.minecraft.text.LegacyText;
|
||||
import club.thectm.minecraft.text.TextObject;
|
||||
|
||||
/**
|
||||
* Thank for the minecraft-text library made by rbrick:
|
||||
* https://github.com/ctmclub/minecraft-text
|
||||
*/
|
||||
public class Chat {
|
||||
|
||||
public static final char COLOR_CHAR = (char) 0xA7; // Represent the character '§'
|
||||
|
||||
public static String rawText(String text) {
|
||||
return "{\"text\": \"" + text + "\"}";
|
||||
public static TextObject legacyText(String text, char colorChar) {
|
||||
return LegacyText.fromLegacy(text, colorChar);
|
||||
}
|
||||
|
||||
/**
|
||||
* Different types of chat message:
|
||||
* Colored one (simplest)
|
||||
* Colored + event (hover/click)
|
||||
*/
|
||||
public static TextObject legacyText(String text) {
|
||||
return legacyText(text, COLOR_CHAR);
|
||||
}
|
||||
|
||||
public static String legacyTextString(String text, char colorChar) {
|
||||
return legacyText(text, colorChar).toJson().toString();
|
||||
}
|
||||
|
||||
public static String legacyTextString(String text) {
|
||||
return legacyText(text).toJson().toString();
|
||||
}
|
||||
|
||||
public static String uncoloredLegacyText(String text) {
|
||||
return text.replace(String.valueOf(COLOR_CHAR), "");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ public abstract class Entity implements Viewable, DataContainer {
|
|||
protected static final byte METADATA_OPTCHAT = 5;
|
||||
protected static final byte METADATA_SLOT = 6;
|
||||
protected static final byte METADATA_BOOLEAN = 7;
|
||||
protected static final byte METADATA_POSE = 18;
|
||||
|
||||
protected Instance instance;
|
||||
protected Position position;
|
||||
|
@ -522,7 +523,9 @@ public abstract class Entity implements Viewable, DataContainer {
|
|||
|
||||
public void refreshSneaking(boolean sneaking) {
|
||||
this.crouched = sneaking;
|
||||
this.pose = sneaking ? Pose.SNEAKING : Pose.STANDING;
|
||||
sendMetadataIndex(0);
|
||||
sendMetadataIndex(6);
|
||||
}
|
||||
|
||||
public void refreshSprinting(boolean sprinting) {
|
||||
|
@ -575,6 +578,7 @@ public abstract class Entity implements Viewable, DataContainer {
|
|||
fillMetadataIndex(packet, 0);
|
||||
fillMetadataIndex(packet, 1);
|
||||
fillMetadataIndex(packet, 5);
|
||||
fillMetadataIndex(packet, 6);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -602,6 +606,9 @@ public abstract class Entity implements Viewable, DataContainer {
|
|||
case 5:
|
||||
fillNoGravityMetaData(packet);
|
||||
break;
|
||||
case 6:
|
||||
fillPoseMetaData(packet);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -646,6 +653,12 @@ public abstract class Entity implements Viewable, DataContainer {
|
|||
packet.putBoolean(noGravity);
|
||||
}
|
||||
|
||||
private void fillPoseMetaData(Packet packet) {
|
||||
packet.putByte((byte) 6);
|
||||
packet.putByte(METADATA_POSE);
|
||||
Utils.writeVarInt(packet, pose.ordinal());
|
||||
}
|
||||
|
||||
protected void sendSynchronization() {
|
||||
EntityTeleportPacket entityTeleportPacket = new EntityTeleportPacket();
|
||||
entityTeleportPacket.entityId = getEntityId();
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package fr.themode.minestom.entity;
|
||||
|
||||
import club.thectm.minecraft.text.TextObject;
|
||||
import com.google.gson.JsonObject;
|
||||
import fr.themode.minestom.Main;
|
||||
import fr.themode.minestom.bossbar.BossBar;
|
||||
import fr.themode.minestom.chat.Chat;
|
||||
|
@ -18,11 +20,11 @@ import fr.themode.minestom.net.packet.client.ClientPlayPacket;
|
|||
import fr.themode.minestom.net.packet.server.ServerPacket;
|
||||
import fr.themode.minestom.net.packet.server.play.*;
|
||||
import fr.themode.minestom.net.player.PlayerConnection;
|
||||
import fr.themode.minestom.scoreboard.Scoreboard;
|
||||
import fr.themode.minestom.utils.*;
|
||||
import fr.themode.minestom.world.Dimension;
|
||||
import fr.themode.minestom.world.LevelType;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
@ -124,30 +126,10 @@ public class Player extends LivingEntity {
|
|||
}
|
||||
});
|
||||
|
||||
final String dataFileName = "C:\\Users\\themo\\OneDrive\\Bureau\\Minestom data\\" + getUsername() + ".dat";
|
||||
|
||||
setEventCallback(PlayerDisconnectEvent.class, event -> {
|
||||
saveData(new File(dataFileName), () -> {
|
||||
System.out.println("SAVE DONE");
|
||||
});
|
||||
});
|
||||
|
||||
setEventCallback(PlayerBlockPlaceEvent.class, event -> {
|
||||
if (getData() != null) {
|
||||
int value = getData().getOrDefault("test", 0);
|
||||
getData().set("test", value + 1, Integer.class);
|
||||
System.out.println("OLD DATA VALUE: " + value);
|
||||
}
|
||||
|
||||
if (event.getHand() != Hand.MAIN)
|
||||
return;
|
||||
|
||||
/*sendMessage("Save chunk data...");
|
||||
long time = System.currentTimeMillis();
|
||||
getInstance().saveToFolder(() -> {
|
||||
sendMessage("Saved in " + (System.currentTimeMillis() - time) + " ms");
|
||||
});*/
|
||||
|
||||
for (Player player : instance.getPlayers()) {
|
||||
if (player != this)
|
||||
player.teleport(getPosition());
|
||||
|
@ -160,7 +142,6 @@ public class Player extends LivingEntity {
|
|||
|
||||
setEventCallback(PlayerLoginEvent.class, event -> {
|
||||
event.setSpawningInstance(instanceContainer);
|
||||
loadData(new File(dataFileName));
|
||||
});
|
||||
|
||||
setEventCallback(PlayerSpawnEvent.class, event -> {
|
||||
|
@ -190,11 +171,11 @@ public class Player extends LivingEntity {
|
|||
TeamsPacket teamsPacket = new TeamsPacket();
|
||||
teamsPacket.teamName = "TEAMNAME" + new Random().nextInt(100);
|
||||
teamsPacket.action = TeamsPacket.Action.CREATE_TEAM;
|
||||
teamsPacket.teamDisplayName = Chat.rawText("WOWdisplay");
|
||||
teamsPacket.teamDisplayName = "WOWdisplay";
|
||||
teamsPacket.nameTagVisibility = "always";
|
||||
teamsPacket.teamColor = 2;
|
||||
teamsPacket.teamPrefix = Chat.rawText("pre");
|
||||
teamsPacket.teamSuffix = Chat.rawText("suf");
|
||||
teamsPacket.teamPrefix = "pre";
|
||||
teamsPacket.teamSuffix = "suf";
|
||||
teamsPacket.collisionRule = "never";
|
||||
teamsPacket.entities = new String[]{getUsername()};
|
||||
sendPacketToViewersAndSelf(teamsPacket);
|
||||
|
@ -203,6 +184,13 @@ public class Player extends LivingEntity {
|
|||
heal();
|
||||
|
||||
setExp(0.9f);
|
||||
|
||||
Scoreboard scoreboard = new Scoreboard("Scoreboard Title");
|
||||
for (int i = 0; i < 15; i++) {
|
||||
scoreboard.createLine(new Scoreboard.ScoreboardLine("id" + i, "Hey guys " + i, i));
|
||||
}
|
||||
scoreboard.addViewer(this);
|
||||
scoreboard.updateLineContent("id3", "I HAVE BEEN UPDATED &2TEST");
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -410,7 +398,6 @@ public class Player extends LivingEntity {
|
|||
if (chunk != null) {
|
||||
viewableChunks.add(chunk);
|
||||
chunk.addViewer(this);
|
||||
chunk.packetUpdated = true;
|
||||
}
|
||||
boolean isLast = counter.get() == length - 1;
|
||||
if (isLast) {
|
||||
|
@ -450,11 +437,24 @@ public class Player extends LivingEntity {
|
|||
sendPacketToViewersAndSelf(breakAnimationPacket);
|
||||
}
|
||||
|
||||
// Use legacy color formatting
|
||||
public void sendMessage(String message) {
|
||||
ChatMessagePacket chatMessagePacket = new ChatMessagePacket(Chat.rawText(message), ChatMessagePacket.Position.CHAT);
|
||||
sendMessage(Chat.legacyText(message));
|
||||
}
|
||||
|
||||
public void sendMessage(String message, char colorChar) {
|
||||
sendMessage(Chat.legacyText(message, colorChar));
|
||||
}
|
||||
|
||||
public void sendMessage(JsonObject jsonObject) {
|
||||
ChatMessagePacket chatMessagePacket = new ChatMessagePacket(jsonObject.toString(), ChatMessagePacket.Position.CHAT);
|
||||
playerConnection.sendPacket(chatMessagePacket);
|
||||
}
|
||||
|
||||
public void sendMessage(TextObject textObject) {
|
||||
sendMessage(textObject.toJson());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void damage(float value) {
|
||||
if (getGameMode() == GameMode.CREATIVE)
|
||||
|
|
|
@ -197,7 +197,6 @@ public class InstanceContainer extends Instance {
|
|||
public void sendChunk(Player player, Chunk chunk) {
|
||||
Packet data = chunk.getFullDataPacket();
|
||||
if (data == null || !chunk.packetUpdated) {
|
||||
System.out.println("UPDATE CHUNK");
|
||||
PacketWriterUtils.writeCallbackPacket(chunk.getFreshFullDataPacket(), buffer -> {
|
||||
chunk.setFullDataPacket(buffer);
|
||||
chunk.packetUpdated = true;
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
package fr.themode.minestom.listener;
|
||||
|
||||
import fr.themode.minestom.Main;
|
||||
import fr.themode.minestom.chat.Chat;
|
||||
import fr.themode.minestom.entity.Player;
|
||||
import fr.themode.minestom.net.packet.client.play.ClientChatMessagePacket;
|
||||
|
||||
public class ChatMessageListener {
|
||||
|
||||
public static void listener(ClientChatMessagePacket packet, Player player) {
|
||||
String message = packet.message;
|
||||
String message = Chat.uncoloredLegacyText(packet.message);
|
||||
Main.getConnectionManager().getOnlinePlayers().forEach(p -> p.sendMessage(String.format("<%s> %s", player.getUsername(), message)));
|
||||
|
||||
}
|
||||
|
|
|
@ -91,8 +91,8 @@ public class AdvancementsPacket implements ServerPacket {
|
|||
public float y;
|
||||
|
||||
private void write(PacketWriter writer) {
|
||||
writer.writeSizedString(Chat.rawText(title));
|
||||
writer.writeSizedString(Chat.rawText(description));
|
||||
writer.writeSizedString(Chat.legacyTextString(title));
|
||||
writer.writeSizedString(Chat.legacyTextString(description));
|
||||
writer.writeItemStack(icon);
|
||||
writer.writeVarInt(frameType.ordinal());
|
||||
writer.writeInt(flags);
|
||||
|
|
|
@ -2,6 +2,7 @@ package fr.themode.minestom.net.packet.server.play;
|
|||
|
||||
import fr.themode.minestom.bossbar.BarColor;
|
||||
import fr.themode.minestom.bossbar.BarDivision;
|
||||
import fr.themode.minestom.chat.Chat;
|
||||
import fr.themode.minestom.net.packet.PacketWriter;
|
||||
import fr.themode.minestom.net.packet.server.ServerPacket;
|
||||
|
||||
|
@ -26,7 +27,7 @@ public class BossBarPacket implements ServerPacket {
|
|||
|
||||
switch (action) {
|
||||
case ADD:
|
||||
writer.writeSizedString(title);
|
||||
writer.writeSizedString(Chat.legacyTextString(title));
|
||||
writer.writeFloat(health);
|
||||
writer.writeVarInt(color.ordinal());
|
||||
writer.writeVarInt(division.ordinal());
|
||||
|
@ -39,7 +40,7 @@ public class BossBarPacket implements ServerPacket {
|
|||
writer.writeFloat(health);
|
||||
break;
|
||||
case UPDATE_TITLE:
|
||||
writer.writeSizedString(title);
|
||||
writer.writeSizedString(Chat.legacyTextString(title));
|
||||
break;
|
||||
case UPDATE_STYLE:
|
||||
writer.writeVarInt(color.ordinal());
|
||||
|
|
|
@ -10,7 +10,7 @@ public class DisconnectPacket implements ServerPacket {
|
|||
|
||||
@Override
|
||||
public void write(PacketWriter writer) {
|
||||
writer.writeSizedString(Chat.rawText(message));
|
||||
writer.writeSizedString(Chat.legacyTextString(message));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package fr.themode.minestom.net.packet.server.play;
|
||||
|
||||
import fr.themode.minestom.chat.Chat;
|
||||
import fr.themode.minestom.net.packet.PacketWriter;
|
||||
import fr.themode.minestom.net.packet.server.ServerPacket;
|
||||
|
||||
|
@ -16,7 +17,7 @@ public class ScoreboardObjectivePacket implements ServerPacket {
|
|||
writer.writeByte(mode);
|
||||
|
||||
if (mode == 0 || mode == 2) {
|
||||
writer.writeSizedString(objectiveValue);
|
||||
writer.writeSizedString(Chat.legacyTextString(objectiveValue));
|
||||
writer.writeVarInt(type);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package fr.themode.minestom.net.packet.server.play;
|
||||
|
||||
import fr.themode.minestom.chat.Chat;
|
||||
import fr.themode.minestom.net.packet.PacketWriter;
|
||||
import fr.themode.minestom.net.packet.server.ServerPacket;
|
||||
|
||||
|
@ -26,13 +27,13 @@ public class TeamsPacket implements ServerPacket {
|
|||
switch (action) {
|
||||
case CREATE_TEAM:
|
||||
case UPDATE_TEAM_INFO:
|
||||
writer.writeSizedString(teamDisplayName);
|
||||
writer.writeSizedString(Chat.legacyTextString(teamDisplayName));
|
||||
writer.writeByte(friendlyFlags);
|
||||
writer.writeSizedString(nameTagVisibility);
|
||||
writer.writeSizedString(collisionRule);
|
||||
writer.writeVarInt(teamColor);
|
||||
writer.writeSizedString(teamPrefix);
|
||||
writer.writeSizedString(teamSuffix);
|
||||
writer.writeSizedString(Chat.legacyTextString(teamPrefix));
|
||||
writer.writeSizedString(Chat.legacyTextString(teamSuffix));
|
||||
break;
|
||||
case REMOVE_TEAM:
|
||||
|
||||
|
|
|
@ -0,0 +1,236 @@
|
|||
package fr.themode.minestom.scoreboard;
|
||||
|
||||
import fr.themode.minestom.Viewable;
|
||||
import fr.themode.minestom.chat.Chat;
|
||||
import fr.themode.minestom.entity.Player;
|
||||
import fr.themode.minestom.net.packet.server.play.DisplayScoreboardPacket;
|
||||
import fr.themode.minestom.net.packet.server.play.ScoreboardObjectivePacket;
|
||||
import fr.themode.minestom.net.packet.server.play.UpdateScorePacket;
|
||||
import fr.themode.minestom.net.player.PlayerConnection;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
// TODO update tick
|
||||
public class Scoreboard implements Viewable {
|
||||
|
||||
private static final AtomicInteger counter = new AtomicInteger();
|
||||
private static final String SCOREBOARD_PREFIX = "sb-";
|
||||
private static final String TEAM_PREFIX = "sbt-";
|
||||
private static final int MAX_LINES_COUNT = 15;
|
||||
|
||||
private Set<Player> viewers = new CopyOnWriteArraySet<>();
|
||||
|
||||
private ConcurrentLinkedQueue<ScoreboardLine> lines = new ConcurrentLinkedQueue<>();
|
||||
private LinkedList<Integer> availableColors = new LinkedList<>();
|
||||
|
||||
private String objectiveName;
|
||||
|
||||
private String title;
|
||||
|
||||
public Scoreboard(String title) {
|
||||
this.title = title;
|
||||
|
||||
this.objectiveName = SCOREBOARD_PREFIX + counter.incrementAndGet();
|
||||
|
||||
// Fill available colors for entities name showed in scoreboard
|
||||
for (int i = 0; i < 16; i++) {
|
||||
availableColors.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
public void createLine(ScoreboardLine scoreboardLine) {
|
||||
synchronized (lines) {
|
||||
if (lines.size() >= MAX_LINES_COUNT)
|
||||
throw new IllegalStateException("You cannot have more than " + MAX_LINES_COUNT + " lines");
|
||||
if (lines.contains(scoreboardLine))
|
||||
throw new IllegalArgumentException("You cannot add two times the same ScoreboardLine");
|
||||
|
||||
// Check ID duplication
|
||||
for (ScoreboardLine line : lines) {
|
||||
if (line.id.equals(scoreboardLine.id))
|
||||
throw new IllegalArgumentException("You cannot add two ScoreboardLine with the same id");
|
||||
}
|
||||
|
||||
// Setup line
|
||||
scoreboardLine.retrieveName(availableColors);
|
||||
scoreboardLine.createTeam();
|
||||
|
||||
// Finally add the line in cache
|
||||
this.lines.add(scoreboardLine);
|
||||
|
||||
// Send to current viewers
|
||||
sendPacketsToViewers(scoreboardLine.team.getCreationPacket(), scoreboardLine.getScoreCreationPacket(objectiveName));
|
||||
}
|
||||
}
|
||||
|
||||
public void updateLineContent(String id, String content) {
|
||||
for (ScoreboardLine line : lines) {
|
||||
if (line.id.equals(id)) {
|
||||
line.refreshContent(content);
|
||||
sendPacketToViewers(line.team.updatePrefix(content));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void updateLineScore(String id, int score) {
|
||||
for (ScoreboardLine line : lines) {
|
||||
if (line.id.equals(id)) {
|
||||
line.line = score;
|
||||
sendPacketToViewers(line.getLineScoreUpdatePacket(objectiveName, score));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ScoreboardLine getLine(String id) {
|
||||
for (ScoreboardLine line : lines) {
|
||||
if (line.id.equals(id))
|
||||
return line;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void removeLine(String id) {
|
||||
synchronized (lines) {
|
||||
Iterator<ScoreboardLine> iterator = lines.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
ScoreboardLine line = iterator.next();
|
||||
if (line.id.equals(id)) {
|
||||
|
||||
// Remove the line for current viewers
|
||||
sendPacketsToViewers(line.getScoreCreationPacket(objectiveName), line.team.getDestructionPacket());
|
||||
|
||||
line.returnName(availableColors);
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addViewer(Player player) {
|
||||
this.viewers.add(player);
|
||||
PlayerConnection playerConnection = player.getPlayerConnection();
|
||||
|
||||
ScoreboardObjectivePacket scoreboardObjectivePacket = new ScoreboardObjectivePacket();
|
||||
scoreboardObjectivePacket.objectiveName = objectiveName;
|
||||
scoreboardObjectivePacket.mode = 0; // Create scoreboard
|
||||
scoreboardObjectivePacket.objectiveValue = title;
|
||||
scoreboardObjectivePacket.type = 0; // Type integer
|
||||
|
||||
DisplayScoreboardPacket displayScoreboardPacket = new DisplayScoreboardPacket();
|
||||
displayScoreboardPacket.position = 1; // Sidebar
|
||||
displayScoreboardPacket.scoreName = objectiveName;
|
||||
|
||||
playerConnection.sendPacket(scoreboardObjectivePacket); // Creative objective
|
||||
playerConnection.sendPacket(displayScoreboardPacket); // Show sidebar scoreboard (wait for scores packet)
|
||||
|
||||
for (ScoreboardLine line : lines) {
|
||||
playerConnection.sendPacket(line.team.getCreationPacket());
|
||||
playerConnection.sendPacket(line.getScoreCreationPacket(objectiveName));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeViewer(Player player) {
|
||||
this.viewers.remove(player);
|
||||
PlayerConnection playerConnection = player.getPlayerConnection();
|
||||
ScoreboardObjectivePacket scoreboardObjectivePacket = new ScoreboardObjectivePacket();
|
||||
scoreboardObjectivePacket.objectiveName = objectiveName;
|
||||
scoreboardObjectivePacket.mode = 1; // Remove
|
||||
playerConnection.sendPacket(scoreboardObjectivePacket);
|
||||
|
||||
for (ScoreboardLine line : lines) {
|
||||
playerConnection.sendPacket(line.getScoreDestructionPacket(objectiveName)); // Is it necessary?
|
||||
playerConnection.sendPacket(line.team.getDestructionPacket());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Player> getViewers() {
|
||||
return viewers;
|
||||
}
|
||||
|
||||
public static class ScoreboardLine {
|
||||
|
||||
private String id; // ID used to modify the line later
|
||||
private String content;
|
||||
private int line;
|
||||
|
||||
private String teamName;
|
||||
private int colorName; // Name of the score (entityName) which is essentially an ID
|
||||
private String entityName;
|
||||
private Team team;
|
||||
|
||||
public ScoreboardLine(String id, String content, int line) {
|
||||
this.id = id;
|
||||
this.content = content;
|
||||
this.line = line;
|
||||
|
||||
this.teamName = TEAM_PREFIX + counter.incrementAndGet();
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return team == null ? content : team.getPrefix();
|
||||
}
|
||||
|
||||
public int getLine() {
|
||||
return line;
|
||||
}
|
||||
|
||||
private void retrieveName(LinkedList<Integer> colors) {
|
||||
synchronized (colors) {
|
||||
this.colorName = colors.pollFirst();
|
||||
}
|
||||
}
|
||||
|
||||
private void createTeam() {
|
||||
this.entityName = Chat.COLOR_CHAR + Integer.toHexString(colorName);
|
||||
|
||||
this.team = new Team(teamName, content, "", entityName);
|
||||
}
|
||||
|
||||
private void returnName(LinkedList<Integer> colors) {
|
||||
synchronized (colors) {
|
||||
colors.add(colorName);
|
||||
}
|
||||
}
|
||||
|
||||
private UpdateScorePacket getScoreCreationPacket(String objectiveName) {
|
||||
UpdateScorePacket updateScorePacket = new UpdateScorePacket();
|
||||
updateScorePacket.entityName = entityName;
|
||||
updateScorePacket.action = 0; // Create/Update
|
||||
updateScorePacket.objectiveName = objectiveName;
|
||||
updateScorePacket.value = line;
|
||||
return updateScorePacket;
|
||||
}
|
||||
|
||||
private UpdateScorePacket getScoreDestructionPacket(String objectiveName) {
|
||||
UpdateScorePacket updateScorePacket = new UpdateScorePacket();
|
||||
updateScorePacket.entityName = entityName;
|
||||
updateScorePacket.action = 1; // Remove
|
||||
updateScorePacket.objectiveName = objectiveName;
|
||||
return updateScorePacket;
|
||||
}
|
||||
|
||||
private UpdateScorePacket getLineScoreUpdatePacket(String objectiveName, int score) {
|
||||
UpdateScorePacket updateScorePacket = getScoreCreationPacket(objectiveName);
|
||||
updateScorePacket.value = score;
|
||||
return updateScorePacket;
|
||||
}
|
||||
|
||||
private void refreshContent(String content) {
|
||||
this.team.refreshPrefix(content);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
package fr.themode.minestom.scoreboard;
|
||||
|
||||
import fr.themode.minestom.net.packet.server.play.TeamsPacket;
|
||||
|
||||
public class Team {
|
||||
|
||||
private String teamName;
|
||||
private String prefix, suffix;
|
||||
private String entityName;
|
||||
|
||||
private String teamDisplayName = "displaynametest";
|
||||
private byte friendlyFlags = 0x00;
|
||||
private String nameTagVisibility = "never";
|
||||
private String collisionRule = "never";
|
||||
private int teamColor = 2;
|
||||
|
||||
|
||||
protected Team(String teamName, String prefix, String suffix, String entityName) {
|
||||
this.teamName = teamName;
|
||||
this.prefix = prefix;
|
||||
this.suffix = suffix;
|
||||
this.entityName = entityName;
|
||||
}
|
||||
|
||||
protected TeamsPacket getCreationPacket() {
|
||||
TeamsPacket teamsPacket = new TeamsPacket();
|
||||
teamsPacket.teamName = teamName;
|
||||
teamsPacket.action = TeamsPacket.Action.CREATE_TEAM;
|
||||
teamsPacket.teamDisplayName = teamDisplayName;
|
||||
teamsPacket.friendlyFlags = friendlyFlags;
|
||||
teamsPacket.nameTagVisibility = nameTagVisibility;
|
||||
teamsPacket.collisionRule = collisionRule;
|
||||
teamsPacket.teamColor = teamColor;
|
||||
teamsPacket.teamPrefix = prefix;
|
||||
teamsPacket.teamSuffix = suffix;
|
||||
teamsPacket.entities = new String[]{entityName};
|
||||
return teamsPacket;
|
||||
}
|
||||
|
||||
protected TeamsPacket getDestructionPacket() {
|
||||
TeamsPacket teamsPacket = new TeamsPacket();
|
||||
teamsPacket.teamName = teamName;
|
||||
teamsPacket.action = TeamsPacket.Action.REMOVE_TEAM;
|
||||
return teamsPacket;
|
||||
}
|
||||
|
||||
protected TeamsPacket updatePrefix(String prefix) {
|
||||
TeamsPacket teamsPacket = new TeamsPacket();
|
||||
teamsPacket.teamName = teamName;
|
||||
teamsPacket.action = TeamsPacket.Action.UPDATE_TEAM_INFO;
|
||||
teamsPacket.teamDisplayName = teamDisplayName;
|
||||
teamsPacket.friendlyFlags = friendlyFlags;
|
||||
teamsPacket.nameTagVisibility = nameTagVisibility;
|
||||
teamsPacket.collisionRule = collisionRule;
|
||||
teamsPacket.teamColor = teamColor;
|
||||
teamsPacket.teamPrefix = prefix;
|
||||
teamsPacket.teamSuffix = suffix;
|
||||
return teamsPacket;
|
||||
}
|
||||
|
||||
protected String getEntityName() {
|
||||
return entityName;
|
||||
}
|
||||
|
||||
protected String getPrefix() {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
protected void refreshPrefix(String prefix) {
|
||||
this.prefix = prefix;
|
||||
}
|
||||
}
|
|
@ -121,7 +121,7 @@ public class Utils {
|
|||
if (itemStack.getDisplayName() != null) {
|
||||
packet.putByte((byte) 0x08);
|
||||
packet.putString("Name");
|
||||
packet.putString(Chat.rawText(itemStack.getDisplayName()));
|
||||
packet.putString(Chat.legacyTextString(itemStack.getDisplayName()));
|
||||
}
|
||||
|
||||
// TODO lore
|
||||
|
|
Loading…
Reference in New Issue