Most things now use Gson for JSON

This commit is contained in:
Camotoy 2024-06-26 01:04:19 -04:00
parent 8d24891c97
commit 2d33cfd149
No known key found for this signature in database
GPG key ID: 7EEFB66FE798081F
20 changed files with 432 additions and 296 deletions

View file

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

View file

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

View file

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

View file

@ -43,6 +43,7 @@ import java.nio.file.Path;
import java.util.List;
import java.util.UUID;
@ConfigSerializable
public interface GeyserConfig {
BedrockConfig bedrock();

View file

@ -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<Module> modules, Collection<Dependency> dependencies) implements ResourcePackManifest {
public record GeyserResourcePackManifest(@SerializedName("format_version") int formatVersion, Header header, Collection<Module> modules, Collection<Dependency> 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<Version> {
public static class VersionDeserializer extends TypeAdapter<Version> {
@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;
}
}
}

View file

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

View file

@ -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<String, Obj
Map<String, BiomeEntry> 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);
}

View file

@ -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<Path, Map<String, ResourcePack>> {
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<Path, Map<String, Reso
}
if (name.contains("manifest.json")) {
try {
GeyserResourcePackManifest manifest = FileUtils.loadJson(zip.getInputStream(x), GeyserResourcePackManifest.class);
GeyserResourcePackManifest manifest = FileUtils.loadJson(GSON, zip.getInputStream(x), GeyserResourcePackManifest.class);
if (manifest.header().uuid() != null) {
manifestReference.set(manifest);
}

View file

@ -25,7 +25,8 @@
package org.geysermc.geyser.registry.mappings;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.checkerframework.checker.nullness.qual.Nullable;
@ -35,6 +36,7 @@ import org.geysermc.geyser.registry.mappings.util.CustomBlockMapping;
import org.geysermc.geyser.registry.mappings.versions.MappingsReader;
import org.geysermc.geyser.registry.mappings.versions.MappingsReader_v1;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@ -95,10 +97,10 @@ public class MappingsConfigReader {
}
}
public @Nullable JsonNode getMappingsRoot(Path file) {
JsonNode mappingsRoot;
try {
mappingsRoot = GeyserImpl.JSON_MAPPER.readTree(file.toFile());
public @Nullable JsonObject getMappingsRoot(Path file) {
JsonObject mappingsRoot;
try (FileReader reader = new FileReader(file.toFile())) {
mappingsRoot = (JsonObject) new JsonParser().parse(reader);
} catch (IOException e) {
GeyserImpl.getInstance().getLogger().error("Failed to read custom mapping file: " + file, e);
return null;
@ -112,8 +114,8 @@ public class MappingsConfigReader {
return mappingsRoot;
}
public int getFormatVersion(JsonNode mappingsRoot, Path file) {
int formatVersion = mappingsRoot.get("format_version").asInt();
public int getFormatVersion(JsonObject mappingsRoot, Path file) {
int formatVersion = mappingsRoot.get("format_version").getAsInt();
if (!this.mappingReaders.containsKey(formatVersion)) {
GeyserImpl.getInstance().getLogger().error("Mappings file " + file + " has an unknown format version: " + formatVersion);
return -1;
@ -122,7 +124,7 @@ public class MappingsConfigReader {
}
public void readItemMappingsFromJson(Path file, BiConsumer<String, CustomItemData> 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<String, CustomBlockMapping> consumer) {
JsonNode mappingsRoot = getMappingsRoot(file);
JsonObject mappingsRoot = getMappingsRoot(file);
if (mappingsRoot == null) {
return;

View file

@ -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<String, CustomItemData> consumer);
public abstract void readBlockMappings(Path file, JsonNode mappingsRoot, BiConsumer<String, CustomBlockMapping> consumer);
public abstract void readItemMappings(Path file, JsonObject mappingsRoot, BiConsumer<String, CustomItemData> consumer);
public abstract void readBlockMappings(Path file, JsonObject mappingsRoot, BiConsumer<String, CustomBlockMapping> 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()
);
}
}

View file

@ -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<String, CustomItemData> consumer) {
public void readItemMappings(Path file, JsonObject mappingsRoot, BiConsumer<String, CustomItemData> 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<String, CustomBlockMapping> consumer) {
public void readBlockMappings(Path file, JsonObject mappingsRoot, BiConsumer<String, CustomBlockMapping> consumer) {
this.readBlockMappingsV1(file, mappingsRoot, consumer);
}
public void readItemMappingsV1(Path file, JsonNode mappingsRoot, BiConsumer<String, CustomItemData> consumer) {
JsonNode itemsNode = mappingsRoot.get("items");
public void readItemMappingsV1(Path file, JsonObject mappingsRoot, BiConsumer<String, CustomItemData> 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<String, CustomBlockMapping> 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<String, CustomBlockMapping> 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<String> 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<String, CustomBlockComponentsMapping> 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<Map.Entry<String, JsonNode>> fields = stateOverrides.fields();
while (fields.hasNext()) {
Map.Entry<String, JsonNode> overrideEntry = fields.next();
for (Map.Entry<String, JsonElement> 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<String, String> 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<PlacementConditions> filter = createPlacementFilterComponent(conditions);
builder.placementFilter(filter);
}
}
if (node.get("placement_filter") instanceof JsonObject placementFilter) {
if (placementFilter.get("conditions") instanceof JsonArray conditions) {
List<PlacementConditions> 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<String> 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<PlacementConditions> createPlacementFilterComponent(JsonNode node) {
private List<PlacementConditions> createPlacementFilterComponent(JsonArray node) {
List<PlacementConditions> 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<Face> 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<String, BlockFilterType> 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);
}
});
}

View file

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

View file

@ -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<String, ItemDefinition> definitions) {
private static ItemData.@Nullable Builder createItemData(JsonObject itemNode, BlockMappings blockMappings, Map<String, ItemDefinition> 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<String, Integer> 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();

View file

@ -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<Map<String, GeyserMappingItem>> mappingItemsType = new TypeReference<>() { };
Type mappingItemsType = new TypeToken<Map<String, GeyserMappingItem>>() { }.getType();
Map<String, GeyserMappingItem> 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<List<PaletteItem>> paletteEntriesType = new TypeReference<>() {};
Type paletteEntriesType = new TypeToken<List<PaletteItem>>() { }.getType();
List<PaletteItem> 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);
}

View file

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

View file

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

View file

@ -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<Map.Entry<String, JsonNode>> localeIterator = localeObj.fields();
Map<String, String> langMap = new HashMap<>();
while (localeIterator.hasNext()) {
Map.Entry<String, JsonNode> entry = localeIterator.next();
langMap.put(entry.getKey(), entry.getValue().asText());
for (Map.Entry<String, JsonElement> 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();
}
}
}

View file

@ -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<Map.Entry<String, JsonNode>> assetIterator = assets.fields();
while (assetIterator.hasNext()) {
Map.Entry<String, JsonNode> entry = assetIterator.next();
for (Map.Entry<String, JsonElement> 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<Version> 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<String, VersionDownload> 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;
}

View file

@ -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> T loadJson(InputStream src, Class<T> valueType) throws IOException {
public static <T> T loadJson(InputStream src, Class<T> valueType) {
return loadJson(GeyserImpl.GSON, src, valueType);
}
public static <T> T loadJson(Gson gson, InputStream src, Class<T> 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);
}
/**

View file

@ -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> T fromJson(byte[] bytes, Class<T> 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> T fromJson(InputStream stream, Type type) {
return GeyserImpl.GSON.fromJson(new InputStreamReader(stream), type);
}
private JsonUtils() {
}
}