Minecraft 1.20.4

This commit is contained in:
LOOHP 2023-12-15 00:09:42 +00:00
parent f4ed5d0f53
commit 0a919303b6
25 changed files with 40022 additions and 13110 deletions

22
pom.xml
View File

@ -24,7 +24,7 @@
<groupId>com.loohp</groupId>
<artifactId>Limbo</artifactId>
<name>Limbo</name>
<version>0.7.6-ALPHA</version>
<version>0.7.7-ALPHA</version>
<description>Standalone Limbo Minecraft Server.</description>
<url>https://github.com/LOOHP/Limbo</url>
@ -136,7 +136,7 @@
</executions>
</plugin>
</plugins>
<finalName>${project.artifactId}-${project.version}-1.20.2</finalName>
<finalName>${project.artifactId}-${project.version}-1.20.4</finalName>
</build>
<profiles>
@ -210,6 +210,10 @@
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
<repository>
<id>sonatype-oss-snapshots1</id>
<url>https://s01.oss.sonatype.org/content/repositories/snapshots/</url>
</repository>
<repository>
<id>bungeecord-repo</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
@ -220,7 +224,7 @@
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.11</version>
<version>3.12.0</version>
</dependency>
<dependency>
<groupId>com.github.Querz</groupId>
@ -231,7 +235,7 @@
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
<version>2.10.1</version>
<scope>compile</scope>
</dependency>
<dependency>
@ -256,31 +260,31 @@
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-text-serializer-gson</artifactId>
<version>4.14.0</version>
<version>4.15.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-text-serializer-legacy</artifactId>
<version>4.14.0</version>
<version>4.15.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-text-serializer-plain</artifactId>
<version>4.14.0</version>
<version>4.15.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-api</artifactId>
<version>4.14.0</version>
<version>4.15.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-nbt</artifactId>
<version>4.14.0</version>
<version>4.15.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>

View File

@ -139,8 +139,8 @@ public final class Limbo {
//===========================
public final String SERVER_IMPLEMENTATION_VERSION = "1.20.2";
public final int SERVER_IMPLEMENTATION_PROTOCOL = 764;
public final String SERVER_IMPLEMENTATION_VERSION = "1.20.4";
public final int SERVER_IMPLEMENTATION_PROTOCOL = 765;
public final String LIMBO_IMPLEMENTATION_VERSION;
private final AtomicBoolean isRunning;
@ -555,7 +555,7 @@ public final class Limbo {
json.put("modinfo", modInfoJson);
TreeMap<String, Object> treeMap = new TreeMap<String, Object>(String.CASE_INSENSITIVE_ORDER);
TreeMap<String, Object> treeMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
treeMap.putAll(json);
Gson g = new GsonBuilder().create();

View File

@ -19,19 +19,19 @@
package com.loohp.limbo.events.player;
import com.loohp.limbo.network.protocol.packets.PacketPlayInResourcePackStatus.EnumResourcePackStatus;
import com.loohp.limbo.network.protocol.packets.ServerboundResourcePackPacket.Action;
import com.loohp.limbo.player.Player;
public class PlayerResourcePackStatusEvent extends PlayerEvent {
private EnumResourcePackStatus status;
private Action status;
public PlayerResourcePackStatusEvent(Player player, EnumResourcePackStatus status) {
public PlayerResourcePackStatusEvent(Player player, Action status) {
super(player);
this.status = status;
}
public EnumResourcePackStatus getStatus() {
public Action getStatus() {
return status;
}
}

View File

@ -63,8 +63,8 @@ import com.loohp.limbo.network.protocol.packets.PacketPlayInPickItem;
import com.loohp.limbo.network.protocol.packets.PacketPlayInPluginMessaging;
import com.loohp.limbo.network.protocol.packets.PacketPlayInPosition;
import com.loohp.limbo.network.protocol.packets.PacketPlayInPositionAndLook;
import com.loohp.limbo.network.protocol.packets.PacketPlayInResourcePackStatus;
import com.loohp.limbo.network.protocol.packets.PacketPlayInResourcePackStatus.EnumResourcePackStatus;
import com.loohp.limbo.network.protocol.packets.ServerboundResourcePackPacket;
import com.loohp.limbo.network.protocol.packets.ServerboundResourcePackPacket.Action;
import com.loohp.limbo.network.protocol.packets.PacketPlayInRotation;
import com.loohp.limbo.network.protocol.packets.PacketPlayInSetCreativeSlot;
import com.loohp.limbo.network.protocol.packets.PacketPlayInTabComplete;
@ -765,11 +765,11 @@ public class ClientConnection extends Thread {
Limbo.getInstance().getUnsafe().a(player, event.getSlot());
}
} else if (packetIn instanceof PacketPlayInResourcePackStatus) {
PacketPlayInResourcePackStatus rpcheck = (PacketPlayInResourcePackStatus) packetIn;
} else if (packetIn instanceof ServerboundResourcePackPacket) {
ServerboundResourcePackPacket rpcheck = (ServerboundResourcePackPacket) packetIn;
// Pass on result to the events
Limbo.getInstance().getEventsManager().callEvent(new PlayerResourcePackStatusEvent(player, rpcheck.getLoadedValue()));
if (rpcheck.getLoadedValue().equals(EnumResourcePackStatus.DECLINED) && properties.getResourcePackRequired()) {
Limbo.getInstance().getEventsManager().callEvent(new PlayerResourcePackStatusEvent(player, rpcheck.getAction()));
if (rpcheck.getAction().equals(Action.DECLINED) && properties.getResourcePackRequired()) {
player.disconnect(new TranslatableComponent("multiplayer.requiredTexturePrompt.disconnect"));
}
} else if (packetIn instanceof PacketPlayInPluginMessaging) {

View File

@ -27,32 +27,34 @@ import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
public class PacketPlayOutResourcePackSend extends PacketOut {
public class ClientboundResourcePackPushPacket extends PacketOut {
public static final int MAX_HASH_LENGTH = 40;
private String url;
private String hash;
private boolean isForced;
private boolean hasPromptMessage;
private Component promptMessage;
private final UUID id;
private final String url;
private final String hash;
private final boolean required;
private final Component prompt;
public PacketPlayOutResourcePackSend(String url, String hash, boolean isForced, boolean hasPromptMessage, Component promptMessage) {
public ClientboundResourcePackPushPacket(UUID id, String url, String hash, boolean required, Component promptMessage) {
if (hash.length() > MAX_HASH_LENGTH) {
throw new IllegalArgumentException("Hash is too long (max " + MAX_HASH_LENGTH + ", was " + hash.length() + ")");
}
this.id = id;
this.url = url;
this.hash = hash;
this.isForced = isForced;
this.hasPromptMessage = hasPromptMessage;
if (hasPromptMessage && promptMessage == null) {
throw new IllegalArgumentException("promptMessage cannot be null when hasPromptMessage is true");
}
this.promptMessage = promptMessage;
this.required = required;
this.prompt = promptMessage;
}
public String getURL() {
public UUID getId() {
return id;
}
public String getUrl() {
return url;
}
@ -60,16 +62,12 @@ public class PacketPlayOutResourcePackSend extends PacketOut {
return hash;
}
public boolean isForced() {
return isForced;
public boolean isRequired() {
return required;
}
public boolean hasPromptMessage() {
return hasPromptMessage;
}
public Component getPromptMessage() {
return promptMessage;
public Component getPrompt() {
return prompt;
}
@Override
@ -78,13 +76,17 @@ public class PacketPlayOutResourcePackSend extends PacketOut {
DataOutputStream output = new DataOutputStream(buffer);
output.writeByte(Packet.getPlayOut().get(getClass()));
DataTypeIO.writeUUID(output, id);
DataTypeIO.writeString(output, url, StandardCharsets.UTF_8);
DataTypeIO.writeString(output, hash, StandardCharsets.UTF_8);
output.writeBoolean(isForced);
output.writeBoolean(hasPromptMessage);
if (hasPromptMessage) {
DataTypeIO.writeString(output, GsonComponentSerializer.gson().serialize(promptMessage), StandardCharsets.UTF_8);
output.writeBoolean(required);
if (prompt == null) {
output.writeBoolean(false);
} else {
output.writeBoolean(true);
DataTypeIO.writeComponent(output, prompt);
}
return buffer.toByteArray();
}

View File

@ -21,12 +21,10 @@ package com.loohp.limbo.network.protocol.packets;
import com.loohp.limbo.utils.DataTypeIO;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class ClientboundSetActionBarTextPacket extends PacketOut {
@ -46,7 +44,7 @@ public class ClientboundSetActionBarTextPacket extends PacketOut {
DataOutputStream output = new DataOutputStream(buffer);
output.writeByte(Packet.getPlayOut().get(getClass()));
DataTypeIO.writeString(output, GsonComponentSerializer.gson().serialize(actionBar), StandardCharsets.UTF_8);
DataTypeIO.writeComponent(output, actionBar);
return buffer.toByteArray();
}

View File

@ -21,12 +21,10 @@ package com.loohp.limbo.network.protocol.packets;
import com.loohp.limbo.utils.DataTypeIO;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class ClientboundSetSubtitleTextPacket extends PacketOut {
@ -46,7 +44,7 @@ public class ClientboundSetSubtitleTextPacket extends PacketOut {
DataOutputStream output = new DataOutputStream(buffer);
output.writeByte(Packet.getPlayOut().get(getClass()));
DataTypeIO.writeString(output, GsonComponentSerializer.gson().serialize(subTitle), StandardCharsets.UTF_8);
DataTypeIO.writeComponent(output, subTitle);
return buffer.toByteArray();
}

View File

@ -21,12 +21,10 @@ package com.loohp.limbo.network.protocol.packets;
import com.loohp.limbo.utils.DataTypeIO;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class ClientboundSetTitleTextPacket extends PacketOut {
@ -46,7 +44,7 @@ public class ClientboundSetTitleTextPacket extends PacketOut {
DataOutputStream output = new DataOutputStream(buffer);
output.writeByte(Packet.getPlayOut().get(getClass()));
DataTypeIO.writeString(output, GsonComponentSerializer.gson().serialize(titleText), StandardCharsets.UTF_8);
DataTypeIO.writeComponent(output, titleText);
return buffer.toByteArray();
}

View File

@ -21,12 +21,10 @@ package com.loohp.limbo.network.protocol.packets;
import com.loohp.limbo.utils.DataTypeIO;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class ClientboundSystemChatPacket extends PacketOut {
@ -52,7 +50,7 @@ public class ClientboundSystemChatPacket extends PacketOut {
DataOutputStream output = new DataOutputStream(buffer);
output.writeByte(Packet.getPlayOut().get(getClass()));
DataTypeIO.writeString(output, GsonComponentSerializer.gson().serialize(message), StandardCharsets.UTF_8);
DataTypeIO.writeComponent(output, message);
output.writeBoolean(overlay);
return buffer.toByteArray();

View File

@ -21,12 +21,10 @@ package com.loohp.limbo.network.protocol.packets;
import com.loohp.limbo.utils.DataTypeIO;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class PacketLoginOutDisconnect extends PacketOut {
@ -46,7 +44,7 @@ public class PacketLoginOutDisconnect extends PacketOut {
DataOutputStream output = new DataOutputStream(buffer);
output.writeByte(Packet.getLoginOut().get(getClass()));
DataTypeIO.writeString(output, GsonComponentSerializer.gson().serialize(reason), StandardCharsets.UTF_8);
DataTypeIO.writeComponent(output, reason);
return buffer.toByteArray();
}

View File

@ -22,12 +22,10 @@ package com.loohp.limbo.network.protocol.packets;
import com.loohp.limbo.bossbar.KeyedBossBar;
import com.loohp.limbo.utils.DataTypeIO;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class PacketPlayOutBoss extends PacketOut {
@ -83,7 +81,7 @@ public class PacketPlayOutBoss extends PacketOut {
BossBar properties = bossBar.getProperties();
switch (action) {
case ADD: {
DataTypeIO.writeString(output, GsonComponentSerializer.gson().serialize(properties.name()), StandardCharsets.UTF_8);
DataTypeIO.writeComponent(output, properties.name());
output.writeFloat(properties.progress());
DataTypeIO.writeVarInt(output, properties.color().ordinal());
DataTypeIO.writeVarInt(output, properties.overlay().ordinal());
@ -98,7 +96,7 @@ public class PacketPlayOutBoss extends PacketOut {
break;
}
case UPDATE_NAME: {
DataTypeIO.writeString(output, GsonComponentSerializer.gson().serialize(properties.name()), StandardCharsets.UTF_8);
DataTypeIO.writeComponent(output, properties.name());
break;
}
case UPDATE_STYLE: {

View File

@ -21,12 +21,10 @@ package com.loohp.limbo.network.protocol.packets;
import com.loohp.limbo.utils.DataTypeIO;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class PacketPlayOutDisconnect extends PacketOut {
@ -46,7 +44,7 @@ public class PacketPlayOutDisconnect extends PacketOut {
DataOutputStream output = new DataOutputStream(buffer);
output.writeByte(Packet.getPlayOut().get(getClass()));
DataTypeIO.writeString(output, GsonComponentSerializer.gson().serialize(reason), StandardCharsets.UTF_8);
DataTypeIO.writeComponent(output, reason);
return buffer.toByteArray();
}

View File

@ -27,7 +27,6 @@ import com.loohp.limbo.utils.DataTypeIO;
import com.loohp.limbo.utils.Rotation3f;
import com.loohp.limbo.world.BlockPosition;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
@ -129,7 +128,7 @@ public class PacketPlayOutEntityMetadata extends PacketOut {
output.writeByte((byte) watch.getValue());
break;
case CHAT:
DataTypeIO.writeString(output, GsonComponentSerializer.gson().serialize((Component) watch.getValue()), StandardCharsets.UTF_8);
DataTypeIO.writeComponent(output, (Component) watch.getValue());
break;
//case DIRECTION:
// break;

View File

@ -25,8 +25,8 @@ import java.io.IOException;
public class PacketPlayOutGameState extends PacketOut {
private int reason;
private float value;
private final int reason;
private final float value;
public PacketPlayOutGameState(int reason, float value) {
this.reason = reason;

View File

@ -23,12 +23,10 @@ import com.loohp.limbo.registry.Registry;
import com.loohp.limbo.utils.DataTypeIO;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class PacketPlayOutOpenWindow extends PacketOut {
@ -63,7 +61,7 @@ public class PacketPlayOutOpenWindow extends PacketOut {
DataTypeIO.writeVarInt(output, containerId);
DataTypeIO.writeVarInt(output, Registry.MENU_REGISTRY.getId(type));
DataTypeIO.writeString(output, GsonComponentSerializer.gson().serialize(title), StandardCharsets.UTF_8);
DataTypeIO.writeComponent(output, title);
return buffer.toByteArray();
}

View File

@ -21,12 +21,10 @@ package com.loohp.limbo.network.protocol.packets;
import com.loohp.limbo.utils.DataTypeIO;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class PacketPlayOutPlayerListHeaderFooter extends PacketOut{
@ -53,8 +51,8 @@ public class PacketPlayOutPlayerListHeaderFooter extends PacketOut{
DataOutputStream output = new DataOutputStream(buffer);
output.writeByte(Packet.getPlayOut().get(getClass()));
DataTypeIO.writeString(output, GsonComponentSerializer.gson().serialize(header), StandardCharsets.UTF_8);
DataTypeIO.writeString(output, GsonComponentSerializer.gson().serialize(footer), StandardCharsets.UTF_8);
DataTypeIO.writeComponent(output, header);
DataTypeIO.writeComponent(output, footer);
return buffer.toByteArray();
}

View File

@ -21,7 +21,6 @@ package com.loohp.limbo.network.protocol.packets;
import com.loohp.limbo.utils.DataTypeIO;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
@ -74,7 +73,7 @@ public class PacketPlayOutTabComplete extends PacketOut {
DataTypeIO.writeString(output, match.getMatch(), StandardCharsets.UTF_8);
if (match.getTooltip().isPresent()) {
output.writeBoolean(true);
DataTypeIO.writeString(output, GsonComponentSerializer.gson().serialize(match.getTooltip().get()), StandardCharsets.UTF_8);
DataTypeIO.writeComponent(output, match.getTooltip().get());
} else {
output.writeBoolean(false);
}

View File

@ -23,43 +23,38 @@ import com.loohp.limbo.utils.DataTypeIO;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.UUID;
public class PacketPlayInResourcePackStatus extends PacketIn {
public class ServerboundResourcePackPacket extends PacketIn {
public static enum EnumResourcePackStatus {
SUCCESS,
DECLINED,
FAILED,
ACCEPTED;
}
public enum Action {
private EnumResourcePackStatus loaded;
SUCCESSFULLY_LOADED, DECLINED, FAILED_DOWNLOAD, ACCEPTED, DOWNLOADED, INVALID_URL, FAILED_RELOAD, DISCARDED;
public PacketPlayInResourcePackStatus(EnumResourcePackStatus loaded) {
this.loaded = loaded;
}
public PacketPlayInResourcePackStatus(DataInputStream in) throws IOException {
this(toLoadedValue(DataTypeIO.readVarInt(in)));
}
public EnumResourcePackStatus getLoadedValue() {
return loaded;
}
private static EnumResourcePackStatus toLoadedValue(int value) {
switch (value) {
case 0:
return EnumResourcePackStatus.SUCCESS;
case 1:
return EnumResourcePackStatus.DECLINED;
case 2:
return EnumResourcePackStatus.FAILED;
case 3:
return EnumResourcePackStatus.ACCEPTED;
default:
return EnumResourcePackStatus.FAILED;
public boolean isTerminal() {
return this != ACCEPTED && this != DOWNLOADED;
}
}
private final UUID id;
private final Action action;
public ServerboundResourcePackPacket(UUID id, Action action) {
this.id = id;
this.action = action;
}
public ServerboundResourcePackPacket(DataInputStream in) throws IOException {
this(DataTypeIO.readUUID(in), Action.values()[DataTypeIO.readVarInt(in)]);
}
public UUID getId() {
return id;
}
public Action getAction() {
return action;
}
}

View File

@ -51,7 +51,7 @@ import com.loohp.limbo.network.protocol.packets.PacketPlayOutNamedSoundEffect;
import com.loohp.limbo.network.protocol.packets.PacketPlayOutOpenWindow;
import com.loohp.limbo.network.protocol.packets.PacketPlayOutPlayerListHeaderFooter;
import com.loohp.limbo.network.protocol.packets.PacketPlayOutPositionAndLook;
import com.loohp.limbo.network.protocol.packets.PacketPlayOutResourcePackSend;
import com.loohp.limbo.network.protocol.packets.ClientboundResourcePackPushPacket;
import com.loohp.limbo.network.protocol.packets.PacketPlayOutRespawn;
import com.loohp.limbo.network.protocol.packets.PacketPlayOutStopSound;
import com.loohp.limbo.sounds.SoundEffect;
@ -369,7 +369,7 @@ public class Player extends LivingEntity implements CommandSender, InventoryHold
public void setResourcePack(String url, String hash, boolean forced, Component promptmessage) {
try {
PacketPlayOutResourcePackSend packsend = new PacketPlayOutResourcePackSend(url, hash, forced, promptmessage != null, promptmessage);
ClientboundResourcePackPushPacket packsend = new ClientboundResourcePackPushPacket(UUID.randomUUID(), url, hash, forced, promptmessage);
clientConnection.sendPacket(packsend);
} catch (IOException e) {
e.printStackTrace();

View File

@ -19,6 +19,7 @@
package com.loohp.limbo.utils;
import com.google.gson.JsonElement;
import com.loohp.limbo.inventory.ItemStack;
import com.loohp.limbo.location.BlockFace;
import com.loohp.limbo.location.MovingObjectPositionBlock;
@ -26,6 +27,8 @@ import com.loohp.limbo.location.Vector;
import com.loohp.limbo.registry.Registry;
import com.loohp.limbo.world.BlockPosition;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.querz.nbt.io.NBTInputStream;
import net.querz.nbt.io.NBTOutputStream;
import net.querz.nbt.tag.CompoundTag;
@ -272,4 +275,20 @@ public class DataTypeIO {
} while (value != 0);
}
public static Component readComponent(DataInputStream in) throws IOException {
// Do not use CompoundTag, as Mojang serializes a plaintext component as just a single StringTag
Tag<?> tag = readTag(in, Tag.class);
if (tag == null || tag instanceof EndTag) {
throw new IllegalArgumentException("Got end-tag when trying to read Component");
}
JsonElement json = NbtComponentSerializer.tagComponentToJson(tag);
return GsonComponentSerializer.gson().deserializeFromTree(json);
}
public static void writeComponent(DataOutputStream out, Component component) throws IOException {
JsonElement json = GsonComponentSerializer.gson().serializeToTree(component);
Tag<?> tag = NbtComponentSerializer.jsonComponentToTag(json);
writeTag(out, tag);
}
}

View File

@ -0,0 +1,385 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2023. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2023. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.utils;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.google.gson.internal.LazilyParsedNumber;
import net.querz.nbt.tag.ByteArrayTag;
import net.querz.nbt.tag.ByteTag;
import net.querz.nbt.tag.CompoundTag;
import net.querz.nbt.tag.DoubleTag;
import net.querz.nbt.tag.EndTag;
import net.querz.nbt.tag.FloatTag;
import net.querz.nbt.tag.IntArrayTag;
import net.querz.nbt.tag.IntTag;
import net.querz.nbt.tag.ListTag;
import net.querz.nbt.tag.LongArrayTag;
import net.querz.nbt.tag.LongTag;
import net.querz.nbt.tag.NumberTag;
import net.querz.nbt.tag.ShortTag;
import net.querz.nbt.tag.StringTag;
import net.querz.nbt.tag.Tag;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
/**
* Taken from <a href="https://github.com/GeyserMC/MCProtocolLib/blob/master/src/main/java/com/github/steveice10/mc/protocol/codec/NbtComponentSerializer.java">MCProtocolLib's NbtComponentSerializer</a>
*/
public class NbtComponentSerializer {
private static final Set<String> BOOLEAN_TYPES = new HashSet<>(Arrays.asList(
"interpret",
"bold",
"italic",
"underlined",
"strikethrough",
"obfuscated"
));
// Order is important
private static final List<Pair<String, String>> COMPONENT_TYPES = Arrays.asList(
new Pair<>("text", "text"),
new Pair<>("translatable", "translate"),
new Pair<>("score", "score"),
new Pair<>("selector", "selector"),
new Pair<>("keybind", "keybind"),
new Pair<>("nbt", "nbt")
);
private NbtComponentSerializer() {
}
public static JsonElement tagComponentToJson(Tag<?> tag) {
return convertToJson(null, tag);
}
public static Tag<?> jsonComponentToTag(JsonElement component) {
return convertToTag(component);
}
private static Tag<?> convertToTag(JsonElement element) {
if (element == null || element.isJsonNull()) {
return null;
} else if (element.isJsonObject()) {
final CompoundTag tag = new CompoundTag();
final JsonObject jsonObject = element.getAsJsonObject();
for (final Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
convertObjectEntry(entry.getKey(), entry.getValue(), tag);
}
addComponentType(jsonObject, tag);
return tag;
} else if (element.isJsonArray()) {
return convertJsonArray(element.getAsJsonArray());
} else if (element.isJsonPrimitive()) {
final JsonPrimitive primitive = element.getAsJsonPrimitive();
if (primitive.isString()) {
return new StringTag(primitive.getAsString());
} else if (primitive.isBoolean()) {
return new ByteTag((byte) (primitive.getAsBoolean() ? 1 : 0));
}
final Number number = primitive.getAsNumber();
if (number instanceof Integer) {
return new IntTag(number.intValue());
} else if (number instanceof Byte) {
return new ByteTag(number.byteValue());
} else if (number instanceof Short) {
return new ShortTag(number.shortValue());
} else if (number instanceof Long) {
return new LongTag(number.longValue());
} else if (number instanceof Double) {
return new DoubleTag(number.doubleValue());
} else if (number instanceof Float) {
return new FloatTag(number.floatValue());
} else if (number instanceof LazilyParsedNumber) {
// TODO: This might need better handling
return new IntTag(number.intValue());
}
return new IntTag(number.intValue()); // ???
}
throw new IllegalArgumentException("Unhandled json type " + element.getClass().getSimpleName() + " with value " + element.getAsString());
}
@SuppressWarnings({"rawtypes", "unchecked"})
private static ListTag<?> convertJsonArray(JsonArray array) {
// TODO Number arrays?
final ListTag listTag = ListTag.createUnchecked(EndTag.class);
boolean singleType = true;
for (final JsonElement entry : array) {
final Tag<?> convertedEntryTag = convertToTag(entry);
if (listTag.getTypeClass() != null && listTag.getTypeClass() != convertedEntryTag.getClass()) {
singleType = false;
break;
}
listTag.add(convertedEntryTag);
}
if (singleType) {
return listTag;
}
// Generally, vanilla-esque serializers should not produce this format, so it should be rare
// Lists are only used for lists of components ("extra" and "with")
final ListTag processedListTag = ListTag.createUnchecked(EndTag.class);
for (final JsonElement entry : array) {
final Tag<?> convertedTag = convertToTag(entry);
if (convertedTag instanceof CompoundTag) {
processedListTag.add(convertedTag);
continue;
}
// Wrap all entries in compound tags, as lists can only consist of one type of tag
final CompoundTag compoundTag = new CompoundTag();
compoundTag.put("type", new StringTag("text"));
if (convertedTag instanceof ListTag) {
compoundTag.put("text", new StringTag());
compoundTag.put("extra", convertedTag.clone());
} else {
compoundTag.put("text", new StringTag(stringValue(convertedTag)));
}
processedListTag.add(compoundTag);
}
return processedListTag;
}
/**
* Converts a json object entry to a tag entry.
*
* @param key key of the entry
* @param value value of the entry
* @param tag the resulting compound tag
*/
private static void convertObjectEntry(final String key, final JsonElement value, final CompoundTag tag) {
if ((key.equals("contents")) && value.isJsonObject()) {
// Store show_entity id as int array instead of uuid string
// Not really required, but we might as well make it more compact
final JsonObject hoverEvent = value.getAsJsonObject();
final JsonElement id = hoverEvent.get("id");
final UUID uuid;
if (id != null && id.isJsonPrimitive() && (uuid = parseUUID(id.getAsString())) != null) {
hoverEvent.remove("id");
final CompoundTag convertedTag = (CompoundTag) convertToTag(value);
convertedTag.put("id", new IntArrayTag(toIntArray(uuid)));
tag.put(key, convertedTag);
return;
}
}
tag.put(key, convertToTag(value));
}
private static void addComponentType(final JsonObject object, final CompoundTag tag) {
if (object.has("type")) {
return;
}
// Add the type to speed up deserialization and make DFU errors slightly more useful
for (final Pair<String, String> pair : COMPONENT_TYPES) {
if (object.has(pair.value)) {
tag.put("type", new StringTag(pair.key));
return;
}
}
}
private static JsonElement convertToJson(final String key, final Tag<?> tag) {
if (tag == null) {
return null;
} else if (tag instanceof CompoundTag) {
final JsonObject object = new JsonObject();
if (!"value".equals(key)) {
removeComponentType(object);
}
for (final Map.Entry<String, Tag<?>> entry : ((CompoundTag) tag).entrySet()) {
convertCompoundTagEntry(entry.getKey(), entry.getValue(), object);
}
return object;
} else if (tag instanceof ListTag<?>) {
final ListTag<?> list = (ListTag<?>) tag;
final JsonArray array = new JsonArray();
for (final Tag<?> listEntry : list) {
array.add(convertToJson(null, listEntry));
}
return array;
} else if (tag instanceof NumberTag<?>) {
if (key != null && BOOLEAN_TYPES.contains(key)) {
// Booleans don't have a direct representation in nbt
return new JsonPrimitive(((NumberTag<?>) tag).asByte() != 0);
}
if (tag instanceof ByteTag) {
return new JsonPrimitive(((ByteTag) tag).asByte());
} else if (tag instanceof ShortTag) {
return new JsonPrimitive(((ShortTag) tag).asShort());
} else if (tag instanceof IntTag) {
return new JsonPrimitive(((IntTag) tag).asInt());
} else if (tag instanceof LongTag) {
return new JsonPrimitive(((LongTag) tag).asLong());
} else if (tag instanceof FloatTag) {
return new JsonPrimitive(((FloatTag) tag).asFloat());
} else if (tag instanceof DoubleTag) {
return new JsonPrimitive(((DoubleTag) tag).asDouble());
}
return new JsonPrimitive(((NumberTag<?>) tag).asDouble());
} else if (tag instanceof StringTag) {
return new JsonPrimitive(((StringTag) tag).getValue());
} else if (tag instanceof ByteArrayTag) {
final ByteArrayTag arrayTag = (ByteArrayTag) tag;
final JsonArray array = new JsonArray();
for (final byte num : arrayTag.getValue()) {
array.add(num);
}
return array;
} else if (tag instanceof IntArrayTag) {
final IntArrayTag arrayTag = (IntArrayTag) tag;
final JsonArray array = new JsonArray();
for (final int num : arrayTag.getValue()) {
array.add(num);
}
return array;
} else if (tag instanceof LongArrayTag) {
final LongArrayTag arrayTag = (LongArrayTag) tag;
final JsonArray array = new JsonArray();
for (final long num : arrayTag.getValue()) {
array.add(num);
}
return array;
}
throw new IllegalArgumentException("Unhandled tag type " + tag.getClass().getSimpleName());
}
private static void convertCompoundTagEntry(final String key, final Tag<?> tag, final JsonObject object) {
if ((key.equals("contents")) && tag instanceof CompoundTag) {
// Back to a UUID string
final CompoundTag showEntity = (CompoundTag) tag;
final Tag<?> idTag = showEntity.get("id");
if (idTag instanceof IntArrayTag) {
showEntity.remove("id");
final JsonObject convertedElement = (JsonObject) convertToJson(key, tag);
final UUID uuid = fromIntArray(((IntArrayTag) idTag).getValue());
convertedElement.addProperty("id", uuid.toString());
object.add(key, convertedElement);
return;
}
}
// "":1 is a valid tag, but not a valid json component
object.add(key.isEmpty() ? "text" : key, convertToJson(key, tag));
}
private static void removeComponentType(final JsonObject object) {
final JsonElement type = object.remove("type");
if (type == null || !type.isJsonPrimitive()) {
return;
}
// Remove the other fields
final String typeString = type.getAsString();
for (final Pair<String, String> pair : COMPONENT_TYPES) {
if (!pair.key.equals(typeString)) {
object.remove(pair.value);
}
}
}
// Last adopted from https://github.com/ViaVersion/ViaVersion/blob/8e38e25cbad1798abb628b4994f4047eaf64640d/common/src/main/java/com/viaversion/viaversion/util/UUIDUtil.java
public static UUID fromIntArray(final int[] parts) {
if (parts.length != 4) {
return new UUID(0, 0);
}
return new UUID((long) parts[0] << 32 | (parts[1] & 0xFFFFFFFFL), (long) parts[2] << 32 | (parts[3] & 0xFFFFFFFFL));
}
public static int[] toIntArray(final UUID uuid) {
return toIntArray(uuid.getMostSignificantBits(), uuid.getLeastSignificantBits());
}
public static int[] toIntArray(final long msb, final long lsb) {
return new int[]{(int) (msb >> 32), (int) msb, (int) (lsb >> 32), (int) lsb};
}
public static UUID parseUUID(final String uuidString) {
try {
return UUID.fromString(uuidString);
} catch (final IllegalArgumentException e) {
return null;
}
}
// Last adopted from https://github.com/ViaVersion/ViaNBT/commit/ad8ac024c48c2fc25e18dc689b3ca62602420ab9
private static String stringValue(Tag<?> tag) {
if (tag instanceof ByteArrayTag) {
return Arrays.toString(((ByteArrayTag) tag).getValue());
} else if (tag instanceof ByteTag) {
return Byte.toString(((ByteTag) tag).asByte());
} else if (tag instanceof DoubleTag) {
return Double.toString(((DoubleTag) tag).asDouble());
} else if (tag instanceof FloatTag) {
return Float.toString(((FloatTag) tag).asFloat());
} else if (tag instanceof IntArrayTag) {
return Arrays.toString(((IntArrayTag) tag).getValue());
} else if (tag instanceof IntTag) {
return Integer.toString(((IntTag) tag).asInt());
} else if (tag instanceof LongArrayTag) {
return Arrays.toString(((LongArrayTag) tag).getValue());
} else if (tag instanceof LongTag) {
return Long.toString(((LongTag) tag).asLong());
} else if (tag instanceof ShortTag) {
return Short.toString(((ShortTag) tag).asShort());
} else if (tag instanceof StringTag) {
return ((StringTag) tag).getValue();
} else {
return tag.valueToString();
}
}
private static class Pair<K, V> {
private final K key;
private final V value;
private Pair(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -20,56 +20,56 @@
"ClientboundFinishConfigurationPacket": "0x02"
},
"PlayIn": {
"0x14": "PacketPlayInKeepAlive",
"0x15": "PacketPlayInKeepAlive",
"0x04": "ServerboundChatCommandPacket",
"0x05": "PacketPlayInChat",
"0x16": "PacketPlayInPosition",
"0x17": "PacketPlayInPositionAndLook",
"0x18": "PacketPlayInRotation",
"0x0F": "PacketPlayInPluginMessaging",
"0x17": "PacketPlayInPosition",
"0x18": "PacketPlayInPositionAndLook",
"0x19": "PacketPlayInRotation",
"0x10": "PacketPlayInPluginMessaging",
"0x0A": "PacketPlayInTabComplete",
"0x2B": "PacketPlayInHeldItemChange",
"0x27": "PacketPlayInResourcePackStatus",
"0x34": "PacketPlayInUseItem",
"0x35": "PacketPlayInBlockPlace",
"0x2E": "PacketPlayInSetCreativeSlot",
"0x2C": "PacketPlayInHeldItemChange",
"0x28": "ServerboundResourcePackPacket",
"0x35": "PacketPlayInUseItem",
"0x36": "PacketPlayInBlockPlace",
"0x2F": "PacketPlayInSetCreativeSlot",
"0x0D": "PacketPlayInWindowClick",
"0x0E": "PacketPlayInCloseWindow",
"0x1C": "PacketPlayInPickItem",
"0x20": "PacketPlayInBlockDig",
"0x26": "PacketPlayInItemName"
"0x1D": "PacketPlayInPickItem",
"0x21": "PacketPlayInBlockDig",
"0x27": "PacketPlayInItemName"
},
"PlayOut": {
"PacketPlayOutLogin": "0x29",
"PacketPlayOutPositionAndLook": "0x3E",
"PacketPlayOutSpawnPosition": "0x52",
"ClientboundSystemChatPacket": "0x67",
"PacketPlayOutSpawnPosition": "0x54",
"ClientboundSystemChatPacket": "0x69",
"PacketPlayOutPlayerAbilities": "0x36",
"ClientboundLevelChunkWithLightPacket": "0x25",
"PacketPlayOutUnloadChunk": "0x1F",
"PacketPlayOutKeepAlive": "0x24",
"PacketPlayOutKeepAlive": "0x15",
"PacketPlayOutPlayerInfo": "0x3C",
"PacketPlayOutUpdateViewPosition": "0x50",
"PacketPlayOutUpdateViewPosition": "0x52",
"PacketPlayOutDisconnect": "0x1B",
"PacketPlayOutPluginMessaging": "0x18",
"PacketPlayOutTabComplete": "0x10",
"PacketPlayOutDeclareCommands": "0x11",
"PacketPlayOutRespawn": "0x43",
"PacketPlayOutRespawn": "0x45",
"PacketPlayOutGameState": "0x20",
"PacketPlayOutEntityDestroy": "0x40",
"PacketPlayOutEntityMetadata": "0x54",
"PacketPlayOutEntityMetadata": "0x56",
"PacketPlayOutSpawnEntity": "0x01",
"PacketPlayOutHeldItemChange": "0x4F",
"PacketPlayOutPlayerListHeaderFooter": "0x68",
"PacketPlayOutResourcePackSend": "0x42",
"ClientboundSetTitlesAnimationPacket": "0x62",
"ClientboundSetTitleTextPacket": "0x61",
"ClientboundSetSubtitleTextPacket": "0x5F",
"ClientboundSetActionBarTextPacket": "0x48",
"PacketPlayOutHeldItemChange": "0x51",
"PacketPlayOutPlayerListHeaderFooter": "0x6A",
"ClientboundResourcePackPushPacket": "0x44",
"ClientboundSetTitlesAnimationPacket": "0x64",
"ClientboundSetTitleTextPacket": "0x63",
"ClientboundSetSubtitleTextPacket": "0x61",
"ClientboundSetActionBarTextPacket": "0x4A",
"ClientboundClearTitlesPacket": "0x0F",
"PacketPlayOutBoss": "0x0A",
"PacketPlayOutNamedSoundEffect": "0x64",
"PacketPlayOutStopSound": "0x66",
"PacketPlayOutNamedSoundEffect": "0x66",
"PacketPlayOutStopSound": "0x68",
"PacketPlayOutWindowItems": "0x13",
"PacketPlayOutSetSlot": "0x15",
"PacketPlayOutOpenWindow": "0x31",

File diff suppressed because it is too large Load Diff