From e8e4e80fe81489747e65691dea87d46568ae7533 Mon Sep 17 00:00:00 2001 From: Eclipse Date: Wed, 17 Sep 2025 12:22:34 +0000 Subject: [PATCH] Add loading of new util.json mappings (#5823) * Add loading of new util.json mappings * Remove now unused set * Switch to loading util mappings through registries * Add Javadocs for UtilMappings * Target mappings with util.json --- .../geysermc/geyser/registry/Registries.java | 21 +++++ .../registry/loader/RegistryLoaders.java | 5 ++ .../geyser/registry/type/UtilMappings.java | 90 +++++++++++++++++++ .../session/cache/BlockBreakHandler.java | 13 +-- core/src/main/resources/mappings | 2 +- 5 files changed, 119 insertions(+), 12 deletions(-) create mode 100644 core/src/main/java/org/geysermc/geyser/registry/type/UtilMappings.java diff --git a/core/src/main/java/org/geysermc/geyser/registry/Registries.java b/core/src/main/java/org/geysermc/geyser/registry/Registries.java index d2c0e26b2..5aed0d5a5 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/Registries.java +++ b/core/src/main/java/org/geysermc/geyser/registry/Registries.java @@ -29,6 +29,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2ObjectMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import net.kyori.adventure.key.Key; import org.cloudburstmc.nbt.NbtMap; import org.cloudburstmc.nbt.NbtMapBuilder; import org.cloudburstmc.protocol.bedrock.data.biome.BiomeDefinitions; @@ -56,6 +57,7 @@ import org.geysermc.geyser.registry.provider.ProviderSupplier; import org.geysermc.geyser.registry.type.ItemMappings; import org.geysermc.geyser.registry.type.ParticleMapping; import org.geysermc.geyser.registry.type.SoundMapping; +import org.geysermc.geyser.registry.type.UtilMappings; import org.geysermc.geyser.translator.level.block.entity.BlockEntityTranslator; import org.geysermc.geyser.translator.level.event.LevelEventTranslator; import org.geysermc.geyser.translator.sound.SoundInteractionTranslator; @@ -197,6 +199,21 @@ public final class Registries { */ public static final SimpleMappedDeferredRegistry> SOUND_TRANSLATORS = SimpleMappedDeferredRegistry.create("org.geysermc.geyser.translator.sound.SoundTranslator", SoundTranslatorRegistryLoader::new); + /** + * A registry containing all of Java's "game master blocks" - blocks that can't be broken without operator permission level 2 or higher. + */ + public static final ListDeferredRegistry GAME_MASTER_BLOCKS = ListDeferredRegistry.create(UtilMappings::gameMasterBlocks, RegistryLoaders.UTIL_MAPPINGS_KEYS); + + /** + * A registry containing all block entities Java considers "dangerous" - these have a red warning in the item tooltip on Java. + */ + public static final ListDeferredRegistry DANGEROUS_BLOCK_ENTITIES = ListDeferredRegistry.create(UtilMappings::dangerousBlockEntities, RegistryLoaders.UTIL_MAPPINGS_KEYS); + + /** + * A registry containing all entities Java considers "dangerous" - spawn eggs of these entities have a red warning in the item tooltip on Java. + */ + public static final ListDeferredRegistry DANGEROUS_ENTITIES = ListDeferredRegistry.create(UtilMappings::dangerousEntities, RegistryLoaders.UTIL_MAPPINGS_KEYS); + public static void load() { if (loaded) return; loaded = true; @@ -216,6 +233,10 @@ public final class Registries { SOUNDS.load(); SOUND_LEVEL_EVENTS.load(); SOUND_TRANSLATORS.load(); + + GAME_MASTER_BLOCKS.load(); + DANGEROUS_BLOCK_ENTITIES.load(); + DANGEROUS_ENTITIES.load(); } public static void populate() { diff --git a/core/src/main/java/org/geysermc/geyser/registry/loader/RegistryLoaders.java b/core/src/main/java/org/geysermc/geyser/registry/loader/RegistryLoaders.java index 0b26e0178..af4810f31 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/loader/RegistryLoaders.java +++ b/core/src/main/java/org/geysermc/geyser/registry/loader/RegistryLoaders.java @@ -25,8 +25,11 @@ package org.geysermc.geyser.registry.loader; +import net.kyori.adventure.key.Key; import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.registry.type.UtilMappings; +import java.util.List; import java.util.function.Supplier; /** @@ -48,6 +51,8 @@ public final class RegistryLoaders { */ public static final ResourcePackLoader RESOURCE_PACKS = new ResourcePackLoader(); + public static final UtilMappings.Loader> UTIL_MAPPINGS_KEYS = new UtilMappings.Loader<>(); + /** * Wraps the surrounding {@link Supplier} in a {@link RegistryLoader} which does * not take in any input value. diff --git a/core/src/main/java/org/geysermc/geyser/registry/type/UtilMappings.java b/core/src/main/java/org/geysermc/geyser/registry/type/UtilMappings.java new file mode 100644 index 000000000..184dc5e3e --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/registry/type/UtilMappings.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2025 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.registry.type; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import net.kyori.adventure.key.Key; +import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.registry.loader.RegistryLoader; +import org.geysermc.geyser.util.MinecraftKey; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; + +/** + * Used to load and store the {@code mappings/util.json} file. When adding new fields to util mappings, be sure to create a proper registry for them + * in {@link org.geysermc.geyser.registry.Registries}. Use {@link org.geysermc.geyser.registry.loader.RegistryLoaders#UTIL_MAPPINGS_KEYS} (or create a new loader if loading something + * other than keys). + */ +public record UtilMappings(List gameMasterBlocks, List dangerousBlockEntities, List dangerousEntities) { + private static final String INPUT = "mappings/util.json"; + private static UtilMappings loaded = null; + + /** + * Gets the loaded util mappings, or loads them if they weren't yet. + */ + private static UtilMappings get() { + if (loaded == null) { + try (InputStream utilInput = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow(INPUT)) { + JsonObject utilJson = JsonParser.parseReader(new InputStreamReader(utilInput)).getAsJsonObject(); + + List gameMasterBlocks = new ArrayList<>(); + List dangerousBlockEntities = new ArrayList<>(); + List dangerousEntities = new ArrayList<>(); + + utilJson.get("game_master_blocks").getAsJsonArray() + .forEach(element -> gameMasterBlocks.add(MinecraftKey.key(element.getAsString()))); + utilJson.get("dangerous_block_entities").getAsJsonArray() + .forEach(element -> dangerousBlockEntities.add(MinecraftKey.key(element.getAsString()))); + utilJson.get("dangerous_entities").getAsJsonArray() + .forEach(element -> dangerousEntities.add(MinecraftKey.key(element.getAsString()))); + + loaded = new UtilMappings(List.copyOf(gameMasterBlocks), List.copyOf(dangerousBlockEntities), List.copyOf(dangerousEntities)); + } catch (IOException e) { + throw new AssertionError("Failed to load " + INPUT); + } + } + return loaded; + } + + /** + * Simply gets a field of the loaded {@link UtilMappings} object. Instead of re-opening the util mappings file every time a field is loaded, + * the mappings are parsed once by {@link UtilMappings#get()} and kept in a static variable. + * Loader input is a function that extracts the field to get from the {@link UtilMappings} object. + */ + public static class Loader implements RegistryLoader, T> { + + @Override + public T load(Function input) { + return input.apply(UtilMappings.get()); + } + } +} diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/BlockBreakHandler.java b/core/src/main/java/org/geysermc/geyser/session/cache/BlockBreakHandler.java index bd4369452..34c424cb6 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/BlockBreakHandler.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/BlockBreakHandler.java @@ -50,6 +50,7 @@ import org.geysermc.geyser.level.block.type.Block; import org.geysermc.geyser.level.block.type.BlockState; import org.geysermc.geyser.level.physics.Direction; import org.geysermc.geyser.registry.BlockRegistries; +import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.item.CustomItemTranslator; @@ -76,16 +77,6 @@ import java.util.concurrent.TimeUnit; */ public class BlockBreakHandler { - private final static Set GAME_MASTER_BLOCKS = Set.of( - Blocks.COMMAND_BLOCK, - Blocks.CHAIN_COMMAND_BLOCK, - Blocks.REPEATING_COMMAND_BLOCK, - Blocks.JIGSAW, - Blocks.STRUCTURE_BLOCK, - Blocks.TEST_BLOCK, - Blocks.TEST_INSTANCE_BLOCK - ); - protected final GeyserSession session; /** @@ -457,7 +448,7 @@ public class BlockBreakHandler { } } - if (GAME_MASTER_BLOCKS.contains(state.block())) { + if (Registries.GAME_MASTER_BLOCKS.get().contains(state.block().javaIdentifier())) { if (!instabuild || session.getOpPermissionLevel() < 2) { return false; } diff --git a/core/src/main/resources/mappings b/core/src/main/resources/mappings index 8002ed6ed..01a320f40 160000 --- a/core/src/main/resources/mappings +++ b/core/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit 8002ed6ed859686b9f544e626ad01995fc3ba7f1 +Subproject commit 01a320f40a2392130554bbc2ac6cd41a1e08d390