Added json message & scoreboard API

This commit is contained in:
TheMode 2019-09-10 06:59:15 +02:00
parent 879f9e7c42
commit 0fc6234b72
22 changed files with 1105 additions and 53 deletions

View File

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

View File

@ -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();
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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() {

View File

@ -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), "");
}
}

View File

@ -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();

View File

@ -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)

View File

@ -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;

View File

@ -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)));
}

View File

@ -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);

View File

@ -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());

View File

@ -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

View File

@ -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);
}
}

View File

@ -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:

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -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