diff --git a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneLogger.java b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneLogger.java index 3a34920ce..c19fa1bc4 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneLogger.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/geyser/platform/standalone/GeyserStandaloneLogger.java @@ -29,6 +29,7 @@ import lombok.extern.slf4j.Slf4j; import net.minecrell.terminalconsole.SimpleTerminalConsole; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.config.Configurator; +import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserLogger; import org.geysermc.geyser.command.GeyserCommandSource; @@ -84,7 +85,12 @@ public class GeyserStandaloneLogger extends SimpleTerminalConsole implements Gey @Override public void debug(String message) { - log.debug(ChatColor.GRAY + message); + log.debug(ChatColor.GRAY + "{}", message); + } + + @Override + public void debug(@Nullable Object object) { + log.debug("{}", object); } @Override diff --git a/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyLogger.java b/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyLogger.java index 10f414b51..550ee9106 100644 --- a/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyLogger.java +++ b/bootstrap/viaproxy/src/main/java/org/geysermc/geyser/platform/viaproxy/GeyserViaProxyLogger.java @@ -26,6 +26,7 @@ package org.geysermc.geyser.platform.viaproxy; import net.raphimc.viaproxy.cli.ConsoleFormatter; import org.apache.logging.log4j.Logger; +import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.GeyserLogger; import org.geysermc.geyser.command.GeyserCommandSource; @@ -75,6 +76,13 @@ public class GeyserViaProxyLogger implements GeyserLogger, GeyserCommandSource { } } + @Override + public void debug(@Nullable Object object) { + if (this.debug) { + this.logger.debug(ConsoleFormatter.convert(String.valueOf(object))); + } + } + @Override public void setDebug(boolean debug) { this.debug = debug; diff --git a/core/src/main/java/org/geysermc/geyser/GeyserLogger.java b/core/src/main/java/org/geysermc/geyser/GeyserLogger.java index aa79e3630..f7baf9767 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserLogger.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserLogger.java @@ -99,7 +99,10 @@ public interface GeyserLogger extends GeyserCommandSource { * @param object the object to log */ default void debug(@Nullable Object object) { - debug(String.valueOf(object)); + if (isDebug()) { + // Don't create String object by default + info(String.valueOf(object)); + } } /** diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java index b50d172e0..059d1c548 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java @@ -43,6 +43,7 @@ import java.nio.file.Path; import java.util.List; import java.util.UUID; +@ConfigSerializable public interface GeyserConfig { BedrockConfig bedrock(); diff --git a/core/src/main/java/org/geysermc/geyser/pack/GeyserResourcePackManifest.java b/core/src/main/java/org/geysermc/geyser/pack/GeyserResourcePackManifest.java index 25a0f0ee0..cfd8709af 100644 --- a/core/src/main/java/org/geysermc/geyser/pack/GeyserResourcePackManifest.java +++ b/core/src/main/java/org/geysermc/geyser/pack/GeyserResourcePackManifest.java @@ -25,11 +25,10 @@ package org.geysermc.geyser.pack; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.geyser.api.pack.ResourcePackManifest; @@ -37,15 +36,14 @@ import java.io.IOException; import java.util.Collection; import java.util.UUID; -public record GeyserResourcePackManifest(@JsonProperty("format_version") int formatVersion, Header header, Collection modules, Collection dependencies) implements ResourcePackManifest { +public record GeyserResourcePackManifest(@SerializedName("format_version") int formatVersion, Header header, Collection modules, Collection dependencies) implements ResourcePackManifest { - public record Header(UUID uuid, Version version, String name, String description, @JsonProperty("min_engine_version") Version minimumSupportedMinecraftVersion) implements ResourcePackManifest.Header { } + public record Header(UUID uuid, Version version, String name, String description, @SerializedName("min_engine_version") Version minimumSupportedMinecraftVersion) implements ResourcePackManifest.Header { } public record Module(UUID uuid, Version version, String type, String description) implements ResourcePackManifest.Module { } public record Dependency(UUID uuid, Version version) implements ResourcePackManifest.Dependency { } - @JsonDeserialize(using = Version.VersionDeserializer.class) public record Version(int major, int minor, int patch) implements ResourcePackManifest.Version { @Override @@ -53,11 +51,17 @@ public record GeyserResourcePackManifest(@JsonProperty("format_version") int for return major + "." + minor + "." + patch; } - public static class VersionDeserializer extends JsonDeserializer { + public static class VersionDeserializer extends TypeAdapter { @Override - public Version deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - int[] version = ctxt.readValue(p, int[].class); - return new Version(version[0], version[1], version[2]); + public void write(JsonWriter jsonWriter, Version version) throws IOException { + } + + @Override + public Version read(JsonReader jsonReader) throws IOException { + jsonReader.beginArray(); + Version version = new Version(jsonReader.nextInt(), jsonReader.nextInt(), jsonReader.nextInt()); + jsonReader.endArray(); + return version; } } } diff --git a/core/src/main/java/org/geysermc/geyser/ping/GeyserLegacyPingPassthrough.java b/core/src/main/java/org/geysermc/geyser/ping/GeyserLegacyPingPassthrough.java index 27b405348..ed08801d9 100644 --- a/core/src/main/java/org/geysermc/geyser/ping/GeyserLegacyPingPassthrough.java +++ b/core/src/main/java/org/geysermc/geyser/ping/GeyserLegacyPingPassthrough.java @@ -25,8 +25,7 @@ package org.geysermc.geyser.ping; -import com.fasterxml.jackson.core.JsonParseException; -import com.fasterxml.jackson.databind.JsonMappingException; +import com.google.gson.JsonSyntaxException; import io.netty.handler.codec.haproxy.HAProxyCommand; import io.netty.handler.codec.haproxy.HAProxyProxiedProtocol; import io.netty.util.NetUtil; @@ -34,9 +33,19 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.nbt.util.VarInts; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.network.GameProtocol; +import org.geysermc.geyser.util.JsonUtils; -import java.io.*; -import java.net.*; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.EOFException; +import java.io.IOException; +import java.net.ConnectException; +import java.net.Inet4Address; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketTimeoutException; +import java.net.UnknownHostException; import java.util.concurrent.TimeUnit; public class GeyserLegacyPingPassthrough implements IGeyserPingPassthrough, Runnable { @@ -130,11 +139,11 @@ public class GeyserLegacyPingPassthrough implements IGeyserPingPassthrough, Runn } } - this.pingInfo = GeyserImpl.JSON_MAPPER.readValue(buffer, GeyserPingInfo.class); + this.pingInfo = JsonUtils.fromJson(buffer, GeyserPingInfo.class); } catch (SocketTimeoutException | ConnectException ex) { this.pingInfo = null; this.geyser.getLogger().debug("Connection timeout for ping passthrough."); - } catch (JsonParseException | JsonMappingException ex) { + } catch (JsonSyntaxException ex) { this.geyser.getLogger().error("Failed to parse json when pinging server!", ex); } catch (EOFException e) { this.pingInfo = null; diff --git a/core/src/main/java/org/geysermc/geyser/registry/loader/BiomeIdentifierRegistryLoader.java b/core/src/main/java/org/geysermc/geyser/registry/loader/BiomeIdentifierRegistryLoader.java index eb091f273..78d729605 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/loader/BiomeIdentifierRegistryLoader.java +++ b/core/src/main/java/org/geysermc/geyser/registry/loader/BiomeIdentifierRegistryLoader.java @@ -30,10 +30,10 @@ import com.google.gson.reflect.TypeToken; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.util.JsonUtils; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.lang.reflect.Type; import java.util.Map; @@ -50,7 +50,7 @@ public class BiomeIdentifierRegistryLoader implements RegistryLoader biomeEntries; try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow("mappings/biomes.json")) { - biomeEntries = GeyserImpl.GSON.fromJson(new InputStreamReader(stream), biomeEntriesType); + biomeEntries = JsonUtils.fromJson(stream, biomeEntriesType); } catch (IOException e) { throw new AssertionError("Unable to load Bedrock runtime biomes", e); } diff --git a/core/src/main/java/org/geysermc/geyser/registry/loader/ResourcePackLoader.java b/core/src/main/java/org/geysermc/geyser/registry/loader/ResourcePackLoader.java index 800a3d22c..537c41ed9 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/loader/ResourcePackLoader.java +++ b/core/src/main/java/org/geysermc/geyser/registry/loader/ResourcePackLoader.java @@ -25,6 +25,8 @@ package org.geysermc.geyser.registry.loader; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.event.lifecycle.GeyserLoadResourcePacksEvent; import org.geysermc.geyser.api.pack.ResourcePack; @@ -55,6 +57,9 @@ import java.util.zip.ZipFile; * Loads {@link ResourcePack}s within a {@link Path} directory, firing the {@link GeyserLoadResourcePacksEvent}. */ public class ResourcePackLoader implements RegistryLoader> { + private static final Gson GSON = new GsonBuilder() + .registerTypeAdapter(GeyserResourcePackManifest.Version.class, new GeyserResourcePackManifest.Version.VersionDeserializer()) + .create(); static final PathMatcher PACK_MATCHER = FileSystems.getDefault().getPathMatcher("glob:**.{zip,mcpack}"); @@ -135,7 +140,7 @@ public class ResourcePackLoader implements RegistryLoader consumer) { - JsonNode mappingsRoot = getMappingsRoot(file); + JsonObject mappingsRoot = getMappingsRoot(file); if (mappingsRoot == null) { return; @@ -138,7 +140,7 @@ public class MappingsConfigReader { } public void readBlockMappingsFromJson(Path file, BiConsumer consumer) { - JsonNode mappingsRoot = getMappingsRoot(file); + JsonObject mappingsRoot = getMappingsRoot(file); if (mappingsRoot == null) { return; diff --git a/core/src/main/java/org/geysermc/geyser/registry/mappings/versions/MappingsReader.java b/core/src/main/java/org/geysermc/geyser/registry/mappings/versions/MappingsReader.java index b2bdd5a01..43d9a23d9 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/mappings/versions/MappingsReader.java +++ b/core/src/main/java/org/geysermc/geyser/registry/mappings/versions/MappingsReader.java @@ -25,7 +25,7 @@ package org.geysermc.geyser.registry.mappings.versions; -import com.fasterxml.jackson.databind.JsonNode; +import com.google.gson.JsonObject; import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.api.item.custom.CustomItemData; import org.geysermc.geyser.api.item.custom.CustomRenderOffsets; @@ -36,14 +36,14 @@ import java.nio.file.Path; import java.util.function.BiConsumer; public abstract class MappingsReader { - public abstract void readItemMappings(Path file, JsonNode mappingsRoot, BiConsumer consumer); - public abstract void readBlockMappings(Path file, JsonNode mappingsRoot, BiConsumer consumer); + public abstract void readItemMappings(Path file, JsonObject mappingsRoot, BiConsumer consumer); + public abstract void readBlockMappings(Path file, JsonObject mappingsRoot, BiConsumer consumer); - public abstract CustomItemData readItemMappingEntry(JsonNode node) throws InvalidCustomMappingsFileException; - public abstract CustomBlockMapping readBlockMappingEntry(String identifier, JsonNode node) throws InvalidCustomMappingsFileException; + public abstract CustomItemData readItemMappingEntry(JsonObject node) throws InvalidCustomMappingsFileException; + public abstract CustomBlockMapping readBlockMappingEntry(String identifier, JsonObject node) throws InvalidCustomMappingsFileException; - protected @Nullable CustomRenderOffsets fromJsonNode(JsonNode node) { - if (node == null || !node.isObject()) { + protected @Nullable CustomRenderOffsets fromJsonObject(JsonObject node) { + if (node == null) { return null; } @@ -53,9 +53,8 @@ public abstract class MappingsReader { ); } - protected CustomRenderOffsets.@Nullable Hand getHandOffsets(JsonNode node, String hand) { - JsonNode tmpNode = node.get(hand); - if (tmpNode == null || !tmpNode.isObject()) { + protected CustomRenderOffsets.@Nullable Hand getHandOffsets(JsonObject node, String hand) { + if (!(node.get(hand) instanceof JsonObject tmpNode)) { return null; } @@ -65,9 +64,8 @@ public abstract class MappingsReader { ); } - protected CustomRenderOffsets.@Nullable Offset getPerspectiveOffsets(JsonNode node, String perspective) { - JsonNode tmpNode = node.get(perspective); - if (tmpNode == null || !tmpNode.isObject()) { + protected CustomRenderOffsets.@Nullable Offset getPerspectiveOffsets(JsonObject node, String perspective) { + if (!(node.get(perspective) instanceof JsonObject tmpNode)) { return null; } @@ -78,9 +76,8 @@ public abstract class MappingsReader { ); } - protected CustomRenderOffsets.@Nullable OffsetXYZ getOffsetXYZ(JsonNode node, String offsetType) { - JsonNode tmpNode = node.get(offsetType); - if (tmpNode == null || !tmpNode.isObject()) { + protected CustomRenderOffsets.@Nullable OffsetXYZ getOffsetXYZ(JsonObject node, String offsetType) { + if (!(node.get(offsetType) instanceof JsonObject tmpNode)) { return null; } @@ -89,9 +86,9 @@ public abstract class MappingsReader { } return new CustomRenderOffsets.OffsetXYZ( - tmpNode.get("x").floatValue(), - tmpNode.get("y").floatValue(), - tmpNode.get("z").floatValue() + tmpNode.get("x").getAsFloat(), + tmpNode.get("y").getAsFloat(), + tmpNode.get("z").getAsFloat() ); } } diff --git a/core/src/main/java/org/geysermc/geyser/registry/mappings/versions/MappingsReader_v1.java b/core/src/main/java/org/geysermc/geyser/registry/mappings/versions/MappingsReader_v1.java index b5e25a4ba..bbde78d03 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/mappings/versions/MappingsReader_v1.java +++ b/core/src/main/java/org/geysermc/geyser/registry/mappings/versions/MappingsReader_v1.java @@ -25,8 +25,10 @@ package org.geysermc.geyser.registry.mappings.versions; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ArrayNode; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import org.checkerframework.checker.nullness.qual.Nullable; @@ -34,9 +36,14 @@ import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.block.custom.CustomBlockData; import org.geysermc.geyser.api.block.custom.CustomBlockPermutation; import org.geysermc.geyser.api.block.custom.CustomBlockState; -import org.geysermc.geyser.api.block.custom.component.*; +import org.geysermc.geyser.api.block.custom.component.BoxComponent; +import org.geysermc.geyser.api.block.custom.component.CustomBlockComponents; +import org.geysermc.geyser.api.block.custom.component.GeometryComponent; +import org.geysermc.geyser.api.block.custom.component.MaterialInstance; +import org.geysermc.geyser.api.block.custom.component.PlacementConditions; import org.geysermc.geyser.api.block.custom.component.PlacementConditions.BlockFilterType; import org.geysermc.geyser.api.block.custom.component.PlacementConditions.Face; +import org.geysermc.geyser.api.block.custom.component.TransformationComponent; import org.geysermc.geyser.api.item.custom.CustomItemData; import org.geysermc.geyser.api.item.custom.CustomItemOptions; import org.geysermc.geyser.api.util.CreativeCategory; @@ -57,7 +64,13 @@ import org.geysermc.geyser.util.MathUtils; import org.geysermc.geyser.util.MinecraftKey; import java.nio.file.Path; -import java.util.*; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.Predicate; @@ -68,7 +81,7 @@ import java.util.stream.Collectors; */ public class MappingsReader_v1 extends MappingsReader { @Override - public void readItemMappings(Path file, JsonNode mappingsRoot, BiConsumer consumer) { + public void readItemMappings(Path file, JsonObject mappingsRoot, BiConsumer consumer) { this.readItemMappingsV1(file, mappingsRoot, consumer); } @@ -76,24 +89,24 @@ public class MappingsReader_v1 extends MappingsReader { * Read item block from a JSON node * * @param file The path to the file - * @param mappingsRoot The {@link JsonNode} containing the mappings + * @param mappingsRoot The {@link JsonObject} containing the mappings * @param consumer The consumer to accept the mappings - * @see #readBlockMappingsV1(Path, JsonNode, BiConsumer) + * @see #readBlockMappingsV1(Path, JsonObject, BiConsumer) */ @Override - public void readBlockMappings(Path file, JsonNode mappingsRoot, BiConsumer consumer) { + public void readBlockMappings(Path file, JsonObject mappingsRoot, BiConsumer consumer) { this.readBlockMappingsV1(file, mappingsRoot, consumer); } - public void readItemMappingsV1(Path file, JsonNode mappingsRoot, BiConsumer consumer) { - JsonNode itemsNode = mappingsRoot.get("items"); + public void readItemMappingsV1(Path file, JsonObject mappingsRoot, BiConsumer consumer) { + JsonObject itemsNode = mappingsRoot.getAsJsonObject("items"); - if (itemsNode != null && itemsNode.isObject()) { - itemsNode.fields().forEachRemaining(entry -> { - if (entry.getValue().isArray()) { - entry.getValue().forEach(data -> { + if (itemsNode != null) { + itemsNode.entrySet().forEach(entry -> { + if (entry.getValue() instanceof JsonArray array) { + array.forEach(data -> { try { - CustomItemData customItemData = this.readItemMappingEntry(data); + CustomItemData customItemData = this.readItemMappingEntry((JsonObject) data); consumer.accept(entry.getKey(), customItemData); } catch (InvalidCustomMappingsFileException e) { GeyserImpl.getInstance().getLogger().error("Error in registering items for custom mapping file: " + file.toString(), e); @@ -108,19 +121,17 @@ public class MappingsReader_v1 extends MappingsReader { * Read block mappings from a JSON node * * @param file The path to the file - * @param mappingsRoot The {@link JsonNode} containing the mappings + * @param mappingsRoot The {@link JsonObject} containing the mappings * @param consumer The consumer to accept the mappings - * @see #readBlockMappings(Path, JsonNode, BiConsumer) + * @see #readBlockMappings(Path, JsonObject, BiConsumer) */ - public void readBlockMappingsV1(Path file, JsonNode mappingsRoot, BiConsumer consumer) { - JsonNode blocksNode = mappingsRoot.get("blocks"); - - if (blocksNode != null && blocksNode.isObject()) { - blocksNode.fields().forEachRemaining(entry -> { - if (entry.getValue().isObject()) { + public void readBlockMappingsV1(Path file, JsonObject mappingsRoot, BiConsumer consumer) { + if (mappingsRoot.get("blocks") instanceof JsonObject blocksNode) { + blocksNode.entrySet().forEach(entry -> { + if (entry.getValue() instanceof JsonObject jsonObject) { try { String identifier = MinecraftKey.key(entry.getKey()).asString(); - CustomBlockMapping customBlockMapping = this.readBlockMappingEntry(identifier, entry.getValue()); + CustomBlockMapping customBlockMapping = this.readBlockMappingEntry(identifier, jsonObject); consumer.accept(identifier, customBlockMapping); } catch (Exception e) { GeyserImpl.getInstance().getLogger().error("Error in registering blocks for custom mapping file: " + file.toString()); @@ -131,85 +142,85 @@ public class MappingsReader_v1 extends MappingsReader { } } - private CustomItemOptions readItemCustomItemOptions(JsonNode node) { + private CustomItemOptions readItemCustomItemOptions(JsonObject node) { CustomItemOptions.Builder customItemOptions = CustomItemOptions.builder(); - JsonNode customModelData = node.get("custom_model_data"); - if (customModelData != null && customModelData.isInt()) { - customItemOptions.customModelData(customModelData.asInt()); + JsonElement customModelData = node.get("custom_model_data"); + if (customModelData != null && customModelData.isJsonPrimitive()) { + customItemOptions.customModelData(customModelData.getAsInt()); } - JsonNode damagePredicate = node.get("damage_predicate"); - if (damagePredicate != null && damagePredicate.isInt()) { - customItemOptions.damagePredicate(damagePredicate.asInt()); + JsonElement damagePredicate = node.get("damage_predicate"); + if (damagePredicate != null && damagePredicate.isJsonPrimitive()) { + customItemOptions.damagePredicate(damagePredicate.getAsInt()); } - JsonNode unbreakable = node.get("unbreakable"); - if (unbreakable != null && unbreakable.isBoolean()) { - customItemOptions.unbreakable(unbreakable.asBoolean()); + JsonElement unbreakable = node.get("unbreakable"); + if (unbreakable != null && unbreakable.isJsonPrimitive()) { + customItemOptions.unbreakable(unbreakable.getAsBoolean()); } - JsonNode defaultItem = node.get("default"); - if (defaultItem != null && defaultItem.isBoolean()) { - customItemOptions.defaultItem(defaultItem.asBoolean()); + JsonElement defaultItem = node.get("default"); + if (defaultItem != null && defaultItem.isJsonPrimitive()) { + customItemOptions.defaultItem(defaultItem.getAsBoolean()); } return customItemOptions.build(); } @Override - public CustomItemData readItemMappingEntry(JsonNode node) throws InvalidCustomMappingsFileException { - if (node == null || !node.isObject()) { + public CustomItemData readItemMappingEntry(JsonObject node) throws InvalidCustomMappingsFileException { + if (node == null) { throw new InvalidCustomMappingsFileException("Invalid item mappings entry"); } - JsonNode name = node.get("name"); - if (name == null || !name.isTextual() || name.asText().isEmpty()) { + JsonElement name = node.get("name"); + if (name == null || !name.isJsonPrimitive() || name.getAsString().isEmpty()) { throw new InvalidCustomMappingsFileException("An item entry has no name"); } CustomItemData.Builder customItemData = CustomItemData.builder() - .name(name.asText()) + .name(name.getAsString()) .customItemOptions(this.readItemCustomItemOptions(node)); //The next entries are optional if (node.has("display_name")) { - customItemData.displayName(node.get("display_name").asText()); + customItemData.displayName(node.get("display_name").getAsString()); } if (node.has("icon")) { - customItemData.icon(node.get("icon").asText()); + customItemData.icon(node.get("icon").getAsString()); } if (node.has("creative_category")) { - customItemData.creativeCategory(node.get("creative_category").asInt()); + customItemData.creativeCategory(node.get("creative_category").getAsInt()); } if (node.has("creative_group")) { - customItemData.creativeGroup(node.get("creative_group").asText()); + customItemData.creativeGroup(node.get("creative_group").getAsString()); } if (node.has("allow_offhand")) { - customItemData.allowOffhand(node.get("allow_offhand").asBoolean()); + customItemData.allowOffhand(node.get("allow_offhand").getAsBoolean()); } if (node.has("display_handheld")) { - customItemData.displayHandheld(node.get("display_handheld").asBoolean()); + customItemData.displayHandheld(node.get("display_handheld").getAsBoolean()); } if (node.has("texture_size")) { - customItemData.textureSize(node.get("texture_size").asInt()); + customItemData.textureSize(node.get("texture_size").getAsInt()); } if (node.has("render_offsets")) { - JsonNode tmpNode = node.get("render_offsets"); + JsonObject tmpNode = node.getAsJsonObject("render_offsets"); - customItemData.renderOffsets(fromJsonNode(tmpNode)); + customItemData.renderOffsets(fromJsonObject(tmpNode)); } - if (node.get("tags") instanceof ArrayNode tags) { + if (node.get("tags") instanceof JsonArray tags) { Set tagsSet = new ObjectOpenHashSet<>(); - tags.forEach(tag -> tagsSet.add(tag.asText())); + tags.forEach(tag -> tagsSet.add(tag.getAsString())); customItemData.tags(tagsSet); } @@ -220,26 +231,26 @@ public class MappingsReader_v1 extends MappingsReader { * Read a block mapping entry from a JSON node and Java identifier * * @param identifier The Java identifier of the block - * @param node The {@link JsonNode} containing the block mapping entry + * @param node The {@link JsonObject} containing the block mapping entry * @return The {@link CustomBlockMapping} record to be read by {@link org.geysermc.geyser.registry.populator.CustomBlockRegistryPopulator} * @throws InvalidCustomMappingsFileException If the JSON node is invalid */ @Override - public CustomBlockMapping readBlockMappingEntry(String identifier, JsonNode node) throws InvalidCustomMappingsFileException { - if (node == null || !node.isObject()) { + public CustomBlockMapping readBlockMappingEntry(String identifier, JsonObject node) throws InvalidCustomMappingsFileException { + if (node == null) { throw new InvalidCustomMappingsFileException("Invalid block mappings entry:" + node); } - String name = node.get("name").asText(); + String name = node.get("name").getAsString(); if (name == null || name.isEmpty()) { throw new InvalidCustomMappingsFileException("A block entry has no name"); } - boolean includedInCreativeInventory = node.has("included_in_creative_inventory") && node.get("included_in_creative_inventory").asBoolean(); + boolean includedInCreativeInventory = node.has("included_in_creative_inventory") && node.get("included_in_creative_inventory").getAsBoolean(); CreativeCategory creativeCategory = CreativeCategory.NONE; if (node.has("creative_category")) { - String categoryName = node.get("creative_category").asText(); + String categoryName = node.get("creative_category").getAsString(); try { creativeCategory = CreativeCategory.valueOf(categoryName.toUpperCase()); } catch (IllegalArgumentException e) { @@ -249,11 +260,11 @@ public class MappingsReader_v1 extends MappingsReader { String creativeGroup = ""; if (node.has("creative_group")) { - creativeGroup = node.get("creative_group").asText(); + creativeGroup = node.get("creative_group").getAsString(); } // If this is true, we will only register the states the user has specified rather than all the possible block states - boolean onlyOverrideStates = node.has("only_override_states") && node.get("only_override_states").asBoolean(); + boolean onlyOverrideStates = node.has("only_override_states") && node.get("only_override_states").getAsBoolean(); // Create the data for the overall block CustomBlockData.Builder customBlockDataBuilder = new GeyserCustomBlockData.Builder() @@ -273,12 +284,9 @@ public class MappingsReader_v1 extends MappingsReader { Map componentsMap = new LinkedHashMap<>(); - JsonNode stateOverrides = node.get("state_overrides"); - if (stateOverrides != null && stateOverrides.isObject()) { + if (node.get("state_overrides") instanceof JsonObject stateOverrides) { // Load components for specific Java block states - Iterator> fields = stateOverrides.fields(); - while (fields.hasNext()) { - Map.Entry overrideEntry = fields.next(); + for (Map.Entry overrideEntry : stateOverrides.entrySet()) { String state = identifier + "[" + overrideEntry.getKey() + "]"; if (!BlockRegistries.JAVA_IDENTIFIER_TO_ID.get().containsKey(state)) { throw new InvalidCustomMappingsFileException("Unknown Java block state: " + state + " for state_overrides."); @@ -358,12 +366,12 @@ public class MappingsReader_v1 extends MappingsReader { /** * Creates a {@link CustomBlockComponents} object for the passed state override or base block node, Java block state identifier, and custom block name * - * @param node the state override or base block {@link JsonNode} + * @param element the state override or base block {@link JsonObject} * @param stateKey the Java block state identifier * @param name the name of the custom block * @return the {@link CustomBlockComponents} object */ - private CustomBlockComponentsMapping createCustomBlockComponentsMapping(JsonNode node, String stateKey, String name) { + private CustomBlockComponentsMapping createCustomBlockComponentsMapping(JsonElement element, String stateKey, String name) { // This is needed to find the correct selection box for the given block int id = BlockRegistries.JAVA_IDENTIFIER_TO_ID.getOrDefault(stateKey, -1); BoxComponent boxComponent = createBoxComponent(id); @@ -372,7 +380,7 @@ public class MappingsReader_v1 extends MappingsReader { .collisionBox(boxComponent) .selectionBox(boxComponent); - if (node == null) { + if (!(element instanceof JsonObject node)) { // No other components were defined return new CustomBlockComponentsMapping(builder.build(), extendedBoxComponent); } @@ -394,28 +402,28 @@ public class MappingsReader_v1 extends MappingsReader { // We set this to max value by default so that we may dictate the correct destroy time ourselves float destructibleByMining = Float.MAX_VALUE; if (node.has("destructible_by_mining")) { - destructibleByMining = node.get("destructible_by_mining").floatValue(); + destructibleByMining = node.get("destructible_by_mining").getAsFloat(); } builder.destructibleByMining(destructibleByMining); if (node.has("geometry")) { - if (node.get("geometry").isTextual()) { + if (node.get("geometry").isJsonPrimitive()) { builder.geometry(new GeyserGeometryComponent.Builder() - .identifier(node.get("geometry").asText()) + .identifier(node.get("geometry").getAsString()) .build()); } else { - JsonNode geometry = node.get("geometry"); + JsonObject geometry = node.getAsJsonObject("geometry"); GeometryComponent.Builder geometryBuilder = new GeyserGeometryComponent.Builder(); if (geometry.has("identifier")) { - geometryBuilder.identifier(geometry.get("identifier").asText()); + geometryBuilder.identifier(geometry.get("identifier").getAsString()); } if (geometry.has("bone_visibility")) { - JsonNode boneVisibility = geometry.get("bone_visibility"); - if (boneVisibility.isObject()) { + if (geometry.get("bone_visibility") instanceof JsonObject boneVisibility) { Map boneVisibilityMap = new Object2ObjectOpenHashMap<>(); - boneVisibility.fields().forEachRemaining(entry -> { + boneVisibility.entrySet().forEach(entry -> { String key = entry.getKey(); - String value = entry.getValue().isBoolean() ? (entry.getValue().asBoolean() ? "1" : "0") : entry.getValue().asText(); + String value = entry.getValue() instanceof JsonPrimitive primitive && primitive.isBoolean() + ? (entry.getValue().getAsBoolean() ? "1" : "0") : entry.getValue().getAsString(); boneVisibilityMap.put(key, value); }); geometryBuilder.boneVisibility(boneVisibilityMap); @@ -427,30 +435,30 @@ public class MappingsReader_v1 extends MappingsReader { String displayName = name; if (node.has("display_name")) { - displayName = node.get("display_name").asText(); + displayName = node.get("display_name").getAsString(); } builder.displayName(displayName); if (node.has("friction")) { - builder.friction(node.get("friction").floatValue()); + builder.friction(node.get("friction").getAsFloat()); } if (node.has("light_emission")) { - builder.lightEmission(node.get("light_emission").asInt()); + builder.lightEmission(node.get("light_emission").getAsInt()); } if (node.has("light_dampening")) { - builder.lightDampening(node.get("light_dampening").asInt()); + builder.lightDampening(node.get("light_dampening").getAsInt()); } boolean placeAir = true; if (node.has("place_air")) { - placeAir = node.get("place_air").asBoolean(); + placeAir = node.get("place_air").getAsBoolean(); } builder.placeAir(placeAir); if (node.has("transformation")) { - JsonNode transformation = node.get("transformation"); + JsonObject transformation = node.getAsJsonObject("transformation"); int rotationX = 0; int rotationY = 0; @@ -463,22 +471,22 @@ public class MappingsReader_v1 extends MappingsReader { float transformZ = 0; if (transformation.has("rotation")) { - JsonNode rotation = transformation.get("rotation"); - rotationX = rotation.get(0).asInt(); - rotationY = rotation.get(1).asInt(); - rotationZ = rotation.get(2).asInt(); + JsonArray rotation = transformation.getAsJsonArray("rotation"); + rotationX = rotation.get(0).getAsInt(); + rotationY = rotation.get(1).getAsInt(); + rotationZ = rotation.get(2).getAsInt(); } if (transformation.has("scale")) { - JsonNode scale = transformation.get("scale"); - scaleX = scale.get(0).floatValue(); - scaleY = scale.get(1).floatValue(); - scaleZ = scale.get(2).floatValue(); + JsonArray scale = transformation.getAsJsonArray("scale"); + scaleX = scale.get(0).getAsFloat(); + scaleY = scale.get(1).getAsFloat(); + scaleZ = scale.get(2).getAsFloat(); } if (transformation.has("translation")) { - JsonNode translation = transformation.get("translation"); - transformX = translation.get(0).floatValue(); - transformY = translation.get(1).floatValue(); - transformZ = translation.get(2).floatValue(); + JsonArray translation = transformation.getAsJsonArray("translation"); + transformX = translation.get(0).getAsFloat(); + transformY = translation.get(1).getAsFloat(); + transformZ = translation.get(2).getAsFloat(); } builder.transformation(new TransformationComponent(rotationX, rotationY, rotationZ, scaleX, scaleY, scaleZ, transformX, transformY, transformZ)); } @@ -490,12 +498,10 @@ public class MappingsReader_v1 extends MappingsReader { } if (node.has("material_instances")) { - JsonNode materialInstances = node.get("material_instances"); - if (materialInstances.isObject()) { - materialInstances.fields().forEachRemaining(entry -> { + if (node.get("material_instances") instanceof JsonObject materialInstances) { + materialInstances.entrySet().forEach(entry -> { String key = entry.getKey(); - JsonNode value = entry.getValue(); - if (value.isObject()) { + if (entry.getValue() instanceof JsonObject value) { MaterialInstance materialInstance = createMaterialInstanceComponent(value); builder.materialInstance(key, materialInstance); } @@ -503,16 +509,10 @@ public class MappingsReader_v1 extends MappingsReader { } } - if (node.has("placement_filter")) { - JsonNode placementFilter = node.get("placement_filter"); - if (placementFilter.isObject()) { - if (placementFilter.has("conditions")) { - JsonNode conditions = placementFilter.get("conditions"); - if (conditions.isArray()) { - List filter = createPlacementFilterComponent(conditions); - builder.placementFilter(filter); - } - } + if (node.get("placement_filter") instanceof JsonObject placementFilter) { + if (placementFilter.get("conditions") instanceof JsonArray conditions) { + List filter = createPlacementFilterComponent(conditions); + builder.placementFilter(filter); } } @@ -521,9 +521,9 @@ public class MappingsReader_v1 extends MappingsReader { // Ideally we could programmatically extract the tags here https://wiki.bedrock.dev/blocks/block-tags.html // This would let us automatically apply the correct vanilla tags to blocks // However, its worth noting that vanilla tools do not currently honor these tags anyway - if (node.get("tags") instanceof ArrayNode tags) { + if (node.get("tags") instanceof JsonArray tags) { Set tagsSet = new ObjectOpenHashSet<>(); - tags.forEach(tag -> tagsSet.add(tag.asText())); + tags.forEach(tag -> tagsSet.add(tag.getAsString())); builder.tags(tagsSet); } @@ -613,21 +613,21 @@ public class MappingsReader_v1 extends MappingsReader { /** * Creates a {@link BoxComponent} from a JSON Node * - * @param node the JSON node + * @param element the JSON node * @return the {@link BoxComponent} */ - private @Nullable BoxComponent createBoxComponent(JsonNode node) { - if (node != null && node.isObject()) { + private @Nullable BoxComponent createBoxComponent(JsonElement element) { + if (element instanceof JsonObject node) { if (node.has("origin") && node.has("size")) { - JsonNode origin = node.get("origin"); - float originX = origin.get(0).floatValue(); - float originY = origin.get(1).floatValue(); - float originZ = origin.get(2).floatValue(); + JsonArray origin = node.getAsJsonArray("origin"); + float originX = origin.get(0).getAsFloat(); + float originY = origin.get(1).getAsFloat(); + float originZ = origin.get(2).getAsFloat(); - JsonNode size = node.get("size"); - float sizeX = size.get(0).floatValue(); - float sizeY = size.get(1).floatValue(); - float sizeZ = size.get(2).floatValue(); + JsonArray size = node.getAsJsonArray("size"); + float sizeX = size.get(0).getAsFloat(); + float sizeY = size.get(1).getAsFloat(); + float sizeZ = size.get(2).getAsFloat(); return new BoxComponent(originX, originY, originZ, sizeX, sizeY, sizeZ); } @@ -642,26 +642,26 @@ public class MappingsReader_v1 extends MappingsReader { * @param node the material instance node * @return the {@link MaterialInstance} */ - private MaterialInstance createMaterialInstanceComponent(JsonNode node) { + private MaterialInstance createMaterialInstanceComponent(JsonObject node) { // Set default values, and use what the user provides if they have provided something String texture = null; if (node.has("texture")) { - texture = node.get("texture").asText(); + texture = node.get("texture").getAsString(); } String renderMethod = "opaque"; if (node.has("render_method")) { - renderMethod = node.get("render_method").asText(); + renderMethod = node.get("render_method").getAsString(); } boolean faceDimming = true; if (node.has("face_dimming")) { - faceDimming = node.get("face_dimming").asBoolean(); + faceDimming = node.get("face_dimming").getAsBoolean(); } boolean ambientOcclusion = true; if (node.has("ambient_occlusion")) { - ambientOcclusion = node.get("ambient_occlusion").asBoolean(); + ambientOcclusion = node.get("ambient_occlusion").getAsBoolean(); } return new GeyserMaterialInstance.Builder() @@ -678,32 +678,33 @@ public class MappingsReader_v1 extends MappingsReader { * @param node the conditions node * @return the list of {@link PlacementConditions} */ - private List createPlacementFilterComponent(JsonNode node) { + private List createPlacementFilterComponent(JsonArray node) { List conditions = new ArrayList<>(); // The structure of the placement filter component is the most complex of the current components // Each condition effectively separated into two arrays: one of allowed faces, and one of blocks/block Molang queries - node.forEach(condition -> { + node.forEach(json -> { + if (!(json instanceof JsonObject condition)) { + return; + } Set faces = EnumSet.noneOf(Face.class); if (condition.has("allowed_faces")) { - JsonNode allowedFaces = condition.get("allowed_faces"); - if (allowedFaces.isArray()) { - allowedFaces.forEach(face -> faces.add(Face.valueOf(face.asText().toUpperCase()))); + if (condition.get("allowed_faces") instanceof JsonArray allowedFaces) { + allowedFaces.forEach(face -> faces.add(Face.valueOf(face.getAsString().toUpperCase()))); } } LinkedHashMap blockFilters = new LinkedHashMap<>(); if (condition.has("block_filter")) { - JsonNode blockFilter = condition.get("block_filter"); - if (blockFilter.isArray()) { + if (condition.get("block_filter") instanceof JsonArray blockFilter) { blockFilter.forEach(filter -> { - if (filter.isObject()) { - if (filter.has("tags")) { - JsonNode tags = filter.get("tags"); - blockFilters.put(tags.asText(), BlockFilterType.TAG); + if (filter instanceof JsonObject jsonObject) { + if (jsonObject.has("tags")) { + JsonElement tags = jsonObject.get("tags"); + blockFilters.put(tags.getAsString(), BlockFilterType.TAG); } - } else if (filter.isTextual()) { - blockFilters.put(filter.asText(), BlockFilterType.BLOCK); + } else if (filter instanceof JsonPrimitive primitive && primitive.isString()) { + blockFilters.put(filter.getAsString(), BlockFilterType.BLOCK); } }); } diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java index d7dc989da..4c7fa8f8a 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java @@ -25,17 +25,26 @@ package org.geysermc.geyser.registry.populator; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ArrayNode; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Interner; import com.google.common.collect.Interners; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; -import it.unimi.dsi.fastutil.objects.*; -import org.cloudburstmc.nbt.*; +import it.unimi.dsi.fastutil.objects.Object2ObjectMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectMaps; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectIntPair; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import org.cloudburstmc.nbt.NBTInputStream; +import org.cloudburstmc.nbt.NbtMap; +import org.cloudburstmc.nbt.NbtMapBuilder; +import org.cloudburstmc.nbt.NbtType; +import org.cloudburstmc.nbt.NbtUtils; import org.cloudburstmc.protocol.bedrock.codec.v671.Bedrock_v671; import org.cloudburstmc.protocol.bedrock.codec.v685.Bedrock_v685; import org.cloudburstmc.protocol.bedrock.data.BlockPropertyData; @@ -56,12 +65,21 @@ import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.type.BlockMappings; import org.geysermc.geyser.registry.type.GeyserBedrockBlock; import org.geysermc.geyser.util.BlockUtils; +import org.geysermc.geyser.util.JsonUtils; import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack; import java.io.DataInputStream; import java.io.InputStream; import java.nio.charset.StandardCharsets; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.BitSet; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; import java.util.stream.Stream; import java.util.zip.GZIPInputStream; @@ -461,23 +479,23 @@ public final class BlockRegistryPopulator { BLOCKS_NBT = blocksNbt; - JsonNode blockInteractionsJson; + JsonObject blockInteractionsJson; try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow("mappings/interactions.json")) { - blockInteractionsJson = GeyserImpl.JSON_MAPPER.readTree(stream); + blockInteractionsJson = JsonUtils.fromJson(stream); } catch (Exception e) { throw new AssertionError("Unable to load Java block interaction mappings", e); } - BlockRegistries.INTERACTIVE.set(toBlockStateSet((ArrayNode) blockInteractionsJson.get("always_consumes"))); - BlockRegistries.INTERACTIVE_MAY_BUILD.set(toBlockStateSet((ArrayNode) blockInteractionsJson.get("requires_may_build"))); + BlockRegistries.INTERACTIVE.set(toBlockStateSet(blockInteractionsJson.getAsJsonArray("always_consumes"))); + BlockRegistries.INTERACTIVE_MAY_BUILD.set(toBlockStateSet(blockInteractionsJson.getAsJsonArray("requires_may_build"))); BlockRegistries.BLOCK_STATES.freeze(); } - private static BitSet toBlockStateSet(ArrayNode node) { + private static BitSet toBlockStateSet(JsonArray node) { BitSet blockStateSet = new BitSet(node.size()); - for (JsonNode javaIdentifier : node) { - blockStateSet.set(BlockRegistries.JAVA_IDENTIFIER_TO_ID.get().getInt(javaIdentifier.textValue())); + for (JsonElement javaIdentifier : node) { + blockStateSet.set(BlockRegistries.JAVA_IDENTIFIER_TO_ID.get().getInt(javaIdentifier.getAsString())); } return blockStateSet; } diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/CreativeItemRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/CreativeItemRegistryPopulator.java index c536d739c..701775223 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/CreativeItemRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/CreativeItemRegistryPopulator.java @@ -25,7 +25,9 @@ package org.geysermc.geyser.registry.populator; -import com.fasterxml.jackson.databind.JsonNode; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.nbt.NbtMap; import org.cloudburstmc.nbt.NbtMapBuilder; @@ -37,6 +39,7 @@ import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.type.BlockMappings; import org.geysermc.geyser.registry.type.GeyserBedrockBlock; +import org.geysermc.geyser.util.JsonUtils; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -61,16 +64,16 @@ public class CreativeItemRegistryPopulator { GeyserBootstrap bootstrap = GeyserImpl.getInstance().getBootstrap(); // Load creative items - JsonNode creativeItemEntries; + JsonArray creativeItemEntries; try (InputStream stream = bootstrap.getResourceOrThrow(String.format("bedrock/creative_items.%s.json", palette.version()))) { - creativeItemEntries = GeyserImpl.JSON_MAPPER.readTree(stream).get("items"); + creativeItemEntries = JsonUtils.fromJson(stream).getAsJsonArray("items"); } catch (Exception e) { throw new AssertionError("Unable to load creative items", e); } BlockMappings blockMappings = BlockRegistries.BLOCKS.forVersion(palette.protocolVersion()); - for (JsonNode itemNode : creativeItemEntries) { - ItemData.Builder itemBuilder = createItemData(itemNode, blockMappings, definitions); + for (JsonElement itemNode : creativeItemEntries) { + ItemData.Builder itemBuilder = createItemData((JsonObject) itemNode, blockMappings, definitions); if (itemBuilder == null) { continue; } @@ -79,41 +82,41 @@ public class CreativeItemRegistryPopulator { } } - private static ItemData.@Nullable Builder createItemData(JsonNode itemNode, BlockMappings blockMappings, Map definitions) { + private static ItemData.@Nullable Builder createItemData(JsonObject itemNode, BlockMappings blockMappings, Map definitions) { int count = 1; int damage = 0; int bedrockBlockRuntimeId; NbtMap tag = null; - String identifier = itemNode.get("id").textValue(); + String identifier = itemNode.get("id").getAsString(); for (BiPredicate predicate : JAVA_ONLY_ITEM_FILTER) { if (predicate.test(identifier, damage)) { return null; } } - JsonNode damageNode = itemNode.get("damage"); + JsonElement damageNode = itemNode.get("damage"); if (damageNode != null) { - damage = damageNode.asInt(); + damage = damageNode.getAsInt(); } - JsonNode countNode = itemNode.get("count"); + JsonElement countNode = itemNode.get("count"); if (countNode != null) { - count = countNode.asInt(); + count = countNode.getAsInt(); } GeyserBedrockBlock blockDefinition = null; - JsonNode blockRuntimeIdNode = itemNode.get("blockRuntimeId"); - JsonNode blockStateNode; + JsonElement blockRuntimeIdNode = itemNode.get("blockRuntimeId"); + JsonElement blockStateNode; if (blockRuntimeIdNode != null) { - bedrockBlockRuntimeId = blockRuntimeIdNode.asInt(); + bedrockBlockRuntimeId = blockRuntimeIdNode.getAsInt(); if (bedrockBlockRuntimeId == 0 && !identifier.equals("minecraft:blue_candle")) { // FIXME bedrockBlockRuntimeId = -1; } blockDefinition = bedrockBlockRuntimeId == -1 ? null : blockMappings.getDefinition(bedrockBlockRuntimeId); } else if ((blockStateNode = itemNode.get("block_state_b64")) != null) { - byte[] bytes = Base64.getDecoder().decode(blockStateNode.asText()); + byte[] bytes = Base64.getDecoder().decode(blockStateNode.getAsString()); ByteArrayInputStream bais = new ByteArrayInputStream(bytes); try { NbtMap stateTag = (NbtMap) NbtUtils.createReaderLE(bais).readTag(); @@ -132,9 +135,9 @@ public class CreativeItemRegistryPopulator { } } - JsonNode nbtNode = itemNode.get("nbt_b64"); + JsonElement nbtNode = itemNode.get("nbt_b64"); if (nbtNode != null) { - byte[] bytes = Base64.getDecoder().decode(nbtNode.asText()); + byte[] bytes = Base64.getDecoder().decode(nbtNode.getAsString()); ByteArrayInputStream bais = new ByteArrayInputStream(bytes); try { tag = (NbtMap) NbtUtils.createReaderLE(bais).readTag(); diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java index e19066462..32d420efa 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java @@ -25,15 +25,21 @@ package org.geysermc.geyser.registry.populator; -import com.fasterxml.jackson.core.type.TypeReference; import com.google.common.collect.Multimap; import com.google.common.collect.MultimapBuilder; +import com.google.gson.reflect.TypeToken; import it.unimi.dsi.fastutil.Pair; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; -import it.unimi.dsi.fastutil.objects.*; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import org.checkerframework.checker.nullness.qual.NonNull; import org.cloudburstmc.nbt.NbtMap; import org.cloudburstmc.nbt.NbtMapBuilder; @@ -61,10 +67,24 @@ import org.geysermc.geyser.item.Items; import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.Registries; -import org.geysermc.geyser.registry.type.*; +import org.geysermc.geyser.registry.type.BlockMappings; +import org.geysermc.geyser.registry.type.GeyserBedrockBlock; +import org.geysermc.geyser.registry.type.GeyserMappingItem; +import org.geysermc.geyser.registry.type.ItemMapping; +import org.geysermc.geyser.registry.type.ItemMappings; +import org.geysermc.geyser.registry.type.NonVanillaItemRegistration; +import org.geysermc.geyser.registry.type.PaletteItem; +import org.geysermc.geyser.util.JsonUtils; import java.io.InputStream; -import java.util.*; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; /** @@ -92,12 +112,12 @@ public class ItemRegistryPopulator { GeyserBootstrap bootstrap = GeyserImpl.getInstance().getBootstrap(); - TypeReference> mappingItemsType = new TypeReference<>() { }; + Type mappingItemsType = new TypeToken>() { }.getType(); Map items; try (InputStream stream = bootstrap.getResourceOrThrow("mappings/items.json")) { // Load item mappings from Java Edition to Bedrock Edition - items = GeyserImpl.JSON_MAPPER.readValue(stream, mappingItemsType); + items = JsonUtils.fromJson(stream, mappingItemsType); } catch (Exception e) { throw new AssertionError("Unable to load Java runtime item IDs", e); } @@ -126,11 +146,11 @@ public class ItemRegistryPopulator { /* Load item palette */ for (PaletteVersion palette : paletteVersions) { - TypeReference> paletteEntriesType = new TypeReference<>() {}; + Type paletteEntriesType = new TypeToken>() { }.getType(); List itemEntries; try (InputStream stream = bootstrap.getResourceOrThrow(String.format("bedrock/runtime_item_states.%s.json", palette.version()))) { - itemEntries = GeyserImpl.JSON_MAPPER.readValue(stream, paletteEntriesType); + itemEntries = JsonUtils.fromJson(stream, paletteEntriesType); } catch (Exception e) { throw new AssertionError("Unable to load Bedrock runtime item IDs", e); } diff --git a/core/src/main/java/org/geysermc/geyser/registry/type/GeyserMappingItem.java b/core/src/main/java/org/geysermc/geyser/registry/type/GeyserMappingItem.java index ab8c52bf6..50198e0fc 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/type/GeyserMappingItem.java +++ b/core/src/main/java/org/geysermc/geyser/registry/type/GeyserMappingItem.java @@ -25,7 +25,7 @@ package org.geysermc.geyser.registry.type; -import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.gson.annotations.SerializedName; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.Getter; @@ -43,14 +43,14 @@ import lombok.With; @NoArgsConstructor @AllArgsConstructor public class GeyserMappingItem { - @JsonProperty("bedrock_identifier") String bedrockIdentifier; - @JsonProperty("bedrock_data") int bedrockData; + @SerializedName("bedrock_identifier") String bedrockIdentifier; + @SerializedName("bedrock_data") int bedrockData; Integer firstBlockRuntimeId; Integer lastBlockRuntimeId; - @JsonProperty("tool_type") String toolType; - @JsonProperty("tool_tier") String toolTier; - @JsonProperty("armor_type") String armorType; - @JsonProperty("protection_value") int protectionValue; - @JsonProperty("is_edible") boolean edible = false; - @JsonProperty("is_entity_placer") boolean entityPlacer = false; + @SerializedName("tool_type") String toolType; + @SerializedName("tool_tier") String toolTier; + @SerializedName("armor_type") String armorType; + @SerializedName("protection_value") int protectionValue; + @SerializedName("is_edible") boolean edible = false; + @SerializedName("is_entity_placer") boolean entityPlacer = false; } diff --git a/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java b/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java index 4c3db7504..88a9c6d01 100644 --- a/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java +++ b/core/src/main/java/org/geysermc/geyser/skin/SkinManager.java @@ -282,6 +282,7 @@ public class SkinManager { } public static @Nullable GameProfileData loadFromJson(String encodedJson) throws IOException, IllegalArgumentException { + // TODO use GameProfile method. JsonNode skinObject; try { skinObject = GeyserImpl.JSON_MAPPER.readTree(new String(Base64.getDecoder().decode(encodedJson), StandardCharsets.UTF_8)); @@ -334,4 +335,4 @@ public class SkinManager { private static final String DEFAULT_FLOODGATE_STEVE = "https://textures.minecraft.net/texture/31f477eb1a7beee631c2ca64d06f8f68fa93a3386d04452ab27f43acdf1b60cb"; } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/geysermc/geyser/text/MinecraftLocale.java b/core/src/main/java/org/geysermc/geyser/text/MinecraftLocale.java index 94d8b254f..3f3e8ff6f 100644 --- a/core/src/main/java/org/geysermc/geyser/text/MinecraftLocale.java +++ b/core/src/main/java/org/geysermc/geyser/text/MinecraftLocale.java @@ -25,11 +25,13 @@ package org.geysermc.geyser.text; -import com.fasterxml.jackson.databind.JsonNode; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.util.AssetUtils; import org.geysermc.geyser.util.FileUtils; +import org.geysermc.geyser.util.JsonUtils; import org.geysermc.geyser.util.WebUtils; import java.io.FileNotFoundException; @@ -39,7 +41,6 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.HashMap; -import java.util.Iterator; import java.util.Locale; import java.util.Map; @@ -189,14 +190,12 @@ public class MinecraftLocale { // Read the localefile try (InputStream localeStream = Files.newInputStream(localeFile, StandardOpenOption.READ)) { // Parse the file as json - JsonNode localeObj = GeyserImpl.JSON_MAPPER.readTree(localeStream); + JsonObject localeObj = JsonUtils.fromJson(localeStream); // Parse all the locale fields - Iterator> localeIterator = localeObj.fields(); Map langMap = new HashMap<>(); - while (localeIterator.hasNext()) { - Map.Entry entry = localeIterator.next(); - langMap.put(entry.getKey(), entry.getValue().asText()); + for (Map.Entry entry : localeObj.entrySet()) { + langMap.put(entry.getKey(), entry.getValue().getAsString()); } return langMap; } catch (FileNotFoundException e){ @@ -266,4 +265,4 @@ public class MinecraftLocale { } return result.toString(); } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/geysermc/geyser/util/AssetUtils.java b/core/src/main/java/org/geysermc/geyser/util/AssetUtils.java index 6bdae6dfe..0c07f4237 100644 --- a/core/src/main/java/org/geysermc/geyser/util/AssetUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/AssetUtils.java @@ -25,18 +25,28 @@ package org.geysermc.geyser.util; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.JsonNode; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.annotations.SerializedName; import lombok.Getter; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.text.GeyserLocale; -import java.io.*; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; -import java.util.*; +import java.util.ArrayDeque; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Queue; import java.util.concurrent.CompletableFuture; import java.util.zip.ZipFile; @@ -83,7 +93,7 @@ public final class AssetUtils { return CompletableFuture.supplyAsync(() -> { try { // Get the version manifest from Mojang - VersionManifest versionManifest = GeyserImpl.JSON_MAPPER.readValue( + VersionManifest versionManifest = GeyserImpl.GSON.fromJson( WebUtils.getBody("https://launchermeta.mojang.com/mc/game/version_manifest.json"), VersionManifest.class); // Get the url for the latest version of the games manifest @@ -101,26 +111,24 @@ public final class AssetUtils { } // Get the individual version manifest - VersionInfo versionInfo = GeyserImpl.JSON_MAPPER.readValue(WebUtils.getBody(latestInfoURL), VersionInfo.class); + VersionInfo versionInfo = GeyserImpl.GSON.fromJson(WebUtils.getBody(latestInfoURL), VersionInfo.class); // Get the client jar for use when downloading the en_us locale - GeyserImpl.getInstance().getLogger().debug(GeyserImpl.JSON_MAPPER.writeValueAsString(versionInfo.getDownloads())); + GeyserImpl.getInstance().getLogger().debug(versionInfo.getDownloads()); // Was previously a Jackson call for writeValueToString CLIENT_JAR_INFO = versionInfo.getDownloads().get("client"); - GeyserImpl.getInstance().getLogger().debug(GeyserImpl.JSON_MAPPER.writeValueAsString(CLIENT_JAR_INFO)); + GeyserImpl.getInstance().getLogger().debug(CLIENT_JAR_INFO); // Was previously a Jackson call for writeValueToString // Get the assets list - JsonNode assets = GeyserImpl.JSON_MAPPER.readTree(WebUtils.getBody(versionInfo.getAssetIndex().getUrl())).get("objects"); + JsonObject assets = ((JsonObject) new JsonParser().parse(WebUtils.getBody(versionInfo.getAssetIndex().getUrl()))).getAsJsonObject("objects"); // Put each asset into an array for use later - Iterator> assetIterator = assets.fields(); - while (assetIterator.hasNext()) { - Map.Entry entry = assetIterator.next(); + for (Map.Entry entry : assets.entrySet()) { if (!entry.getKey().startsWith("minecraft/lang/")) { // No need to cache non-language assets as we don't use them continue; } - Asset asset = GeyserImpl.JSON_MAPPER.treeToValue(entry.getValue(), Asset.class); + Asset asset = GeyserImpl.GSON.fromJson(entry.getValue(), Asset.class); ASSET_MAP.put(entry.getKey(), asset); } @@ -221,106 +229,99 @@ public final class AssetUtils { /* Classes that map to JSON files served by Mojang */ - @JsonIgnoreProperties(ignoreUnknown = true) @Getter static class VersionManifest { - @JsonProperty("latest") + @SerializedName("latest") private LatestVersion latestVersion; - @JsonProperty("versions") + @SerializedName("versions") private List versions; } - @JsonIgnoreProperties(ignoreUnknown = true) @Getter static class LatestVersion { - @JsonProperty("release") + @SerializedName("release") private String release; - @JsonProperty("snapshot") + @SerializedName("snapshot") private String snapshot; } - @JsonIgnoreProperties(ignoreUnknown = true) @Getter static class Version { - @JsonProperty("id") + @SerializedName("id") private String id; - @JsonProperty("type") + @SerializedName("type") private String type; - @JsonProperty("url") + @SerializedName("url") private String url; - @JsonProperty("time") + @SerializedName("time") private String time; - @JsonProperty("releaseTime") + @SerializedName("releaseTime") private String releaseTime; } - @JsonIgnoreProperties(ignoreUnknown = true) @Getter static class VersionInfo { - @JsonProperty("id") + @SerializedName("id") private String id; - @JsonProperty("type") + @SerializedName("type") private String type; - @JsonProperty("time") + @SerializedName("time") private String time; - @JsonProperty("releaseTime") + @SerializedName("releaseTime") private String releaseTime; - @JsonProperty("assetIndex") + @SerializedName("assetIndex") private AssetIndex assetIndex; - @JsonProperty("downloads") + @SerializedName("downloads") private Map downloads; } - @JsonIgnoreProperties(ignoreUnknown = true) @Getter static class VersionDownload { - @JsonProperty("sha1") + @SerializedName("sha1") private String sha1; - @JsonProperty("size") + @SerializedName("size") private int size; - @JsonProperty("url") + @SerializedName("url") private String url; } - @JsonIgnoreProperties(ignoreUnknown = true) @Getter static class AssetIndex { - @JsonProperty("id") + @SerializedName("id") private String id; - @JsonProperty("sha1") + @SerializedName("sha1") private String sha1; - @JsonProperty("size") + @SerializedName("size") private int size; - @JsonProperty("totalSize") + @SerializedName("totalSize") private int totalSize; - @JsonProperty("url") + @SerializedName("url") private String url; } - @JsonIgnoreProperties(ignoreUnknown = true) @Getter public static class Asset { - @JsonProperty("hash") + @SerializedName("hash") private String hash; - @JsonProperty("size") + @SerializedName("size") private int size; } diff --git a/core/src/main/java/org/geysermc/geyser/util/FileUtils.java b/core/src/main/java/org/geysermc/geyser/util/FileUtils.java index c8423c3be..4a7b7fda6 100644 --- a/core/src/main/java/org/geysermc/geyser/util/FileUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/FileUtils.java @@ -30,6 +30,7 @@ import com.fasterxml.jackson.annotation.Nulls; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import com.google.gson.Gson; import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.GeyserImpl; @@ -63,9 +64,13 @@ public class FileUtils { return objectMapper.readValue(src, valueType); } - public static T loadJson(InputStream src, Class valueType) throws IOException { + public static T loadJson(InputStream src, Class valueType) { + return loadJson(GeyserImpl.GSON, src, valueType); + } + + public static T loadJson(Gson gson, InputStream src, Class valueType) { // Read specifically with UTF-8 to allow any non-UTF-encoded JSON to read - return GeyserImpl.JSON_MAPPER.readValue(new InputStreamReader(src, StandardCharsets.UTF_8), valueType); + return gson.fromJson(new InputStreamReader(src, StandardCharsets.UTF_8), valueType); } /** diff --git a/core/src/main/java/org/geysermc/geyser/util/JsonUtils.java b/core/src/main/java/org/geysermc/geyser/util/JsonUtils.java new file mode 100644 index 000000000..9ae74b177 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/util/JsonUtils.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024 GeyserMC. http://geysermc.org + * + * 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. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.util; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.geysermc.geyser.GeyserImpl; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; + +public final class JsonUtils { + + public static T fromJson(byte[] bytes, Class type) { + return GeyserImpl.GSON.fromJson(new String(bytes, StandardCharsets.UTF_8), type); + } + + public static JsonObject fromJson(InputStream stream) { + return (JsonObject) new JsonParser().parse(new InputStreamReader(stream)); + } + + public static T fromJson(InputStream stream, Type type) { + return GeyserImpl.GSON.fromJson(new InputStreamReader(stream), type); + } + + private JsonUtils() { + } +}