From 8abe9b6b0cf9d730a1916f29ade3cfcf11653110 Mon Sep 17 00:00:00 2001 From: Joseph B Date: Sun, 14 May 2023 16:53:34 -0400 Subject: [PATCH] Add API for registering custom world preset editors (#9436) --- build.gradle | 1 + .../net/minecraftforge/fml/ModLoader.java | 3 + .../net/minecraft/client/Minecraft.java.patch | 18 +-- .../worldselection/PresetEditor.java.patch | 13 ++ .../WorldCreationUiState.java.patch | 11 ++ .../custom_preset_editor_test.json | 15 ++ .../DimensionSpecialEffectsManager.java | 3 +- .../client/EntitySpectatorShaderManager.java | 3 +- .../client/ForgeHooksClient.java | 39 ++++- .../client/ItemDecoratorHandler.java | 3 +- .../client/NamedRenderTypeManager.java | 3 +- .../client/PresetEditorManager.java | 53 +++++++ .../client/RecipeBookManager.java | 3 +- .../event/RegisterPresetEditorsEvent.java | 55 +++++++ .../gui/ClientTooltipComponentManager.java | 3 +- .../client/gui/overlay/GuiOverlayManager.java | 3 +- .../model/geometry/GeometryLoaderManager.java | 3 +- .../TextureAtlasSpriteLoaderManager.java | 3 +- .../minecraftforge/registries/GameData.java | 2 +- .../registries/RegistryManager.java | 5 +- .../debug/client/CustomPresetEditorTest.java | 147 ++++++++++++++++++ src/test/resources/META-INF/mods.toml | 2 + .../custom_preset_editor_test/lang/en_us.json | 3 + .../tags/worldgen/world_preset/normal.json | 5 + 24 files changed, 358 insertions(+), 41 deletions(-) create mode 100644 patches/minecraft/net/minecraft/client/gui/screens/worldselection/PresetEditor.java.patch create mode 100644 patches/minecraft/net/minecraft/client/gui/screens/worldselection/WorldCreationUiState.java.patch create mode 100644 src/generated_test/resources/data/custom_preset_editor_test/worldgen/world_preset/custom_preset_editor_test.json create mode 100644 src/main/java/net/minecraftforge/client/PresetEditorManager.java create mode 100644 src/main/java/net/minecraftforge/client/event/RegisterPresetEditorsEvent.java create mode 100644 src/test/java/net/minecraftforge/debug/client/CustomPresetEditorTest.java create mode 100644 src/test/resources/assets/custom_preset_editor_test/lang/en_us.json create mode 100644 src/test/resources/data/minecraft/tags/worldgen/world_preset/normal.json diff --git a/build.gradle b/build.gradle index 3be2b305f6..bd5d1462aa 100644 --- a/build.gradle +++ b/build.gradle @@ -1078,6 +1078,7 @@ project(':forge') { '--mod', 'data_pack_registries_test', '--mod', 'biome_modifiers_test', '--mod', 'structure_modifiers_test', + '--mod', 'custom_preset_editor_test', '--output', rootProject.file('src/generated_test/resources/'), '--existing', sourceSets.main.resources.srcDirs[0], '--existing', sourceSets.test.resources.srcDirs[0] diff --git a/fmlcore/src/main/java/net/minecraftforge/fml/ModLoader.java b/fmlcore/src/main/java/net/minecraftforge/fml/ModLoader.java index e9f519a2c2..328020b49d 100644 --- a/fmlcore/src/main/java/net/minecraftforge/fml/ModLoader.java +++ b/fmlcore/src/main/java/net/minecraftforge/fml/ModLoader.java @@ -319,6 +319,9 @@ public class ModLoader ModList.get().forEachModInOrder(mc -> mc.acceptEvent(e)); return e; } + public void postEventWrapContainerInModOrder(T event) { + postEventWithWrapInModOrder(event, (mc, e) -> ModLoadingContext.get().setActiveContainer(mc), (mc, e) -> ModLoadingContext.get().setActiveContainer(null)); + } public void postEventWithWrapInModOrder(T e, BiConsumer pre, BiConsumer post) { if (!loadingStateValid) { LOGGER.error("Cowardly refusing to send event {} to a broken mod state", e.getClass().getName()); diff --git a/patches/minecraft/net/minecraft/client/Minecraft.java.patch b/patches/minecraft/net/minecraft/client/Minecraft.java.patch index 81e23a8186..bb7e7f43b9 100644 --- a/patches/minecraft/net/minecraft/client/Minecraft.java.patch +++ b/patches/minecraft/net/minecraft/client/Minecraft.java.patch @@ -60,25 +60,11 @@ this.f_91064_ = new DebugRenderer(this); this.f_238717_ = new RealmsDataFetcher(RealmsClient.m_239151_(this)); RenderSystem.m_69900_(this::m_91113_); -@@ -545,6 +_,21 @@ +@@ -545,6 +_,7 @@ TinyFileDialogs.tinyfd_messageBox("Minecraft", stringbuilder.toString(), "ok", "error", false); } -+ net.minecraftforge.gametest.ForgeGameTestHooks.registerGametests(); -+ net.minecraftforge.fml.ModLoader.get().postEvent(new net.minecraftforge.client.event.RegisterClientReloadListenersEvent(this.f_91036_)); -+ net.minecraftforge.fml.ModLoader.get().postEvent(new net.minecraftforge.client.event.EntityRenderersEvent.RegisterLayerDefinitions()); -+ net.minecraftforge.fml.ModLoader.get().postEvent(new net.minecraftforge.client.event.EntityRenderersEvent.RegisterRenderers()); -+ net.minecraftforge.client.textures.TextureAtlasSpriteLoaderManager.init(); -+ net.minecraftforge.client.gui.ClientTooltipComponentManager.init(); -+ net.minecraftforge.client.EntitySpectatorShaderManager.init(); -+ net.minecraftforge.client.ForgeHooksClient.onRegisterKeyMappings(f_91066_); -+ net.minecraftforge.client.RecipeBookManager.init(); -+ net.minecraftforge.client.gui.overlay.GuiOverlayManager.init(); -+ net.minecraftforge.client.DimensionSpecialEffectsManager.init(); -+ net.minecraftforge.client.NamedRenderTypeManager.init(); -+ net.minecraftforge.client.ColorResolverManager.init(); -+ net.minecraftforge.client.ItemDecoratorHandler.init(); -+ ++ net.minecraftforge.client.ForgeHooksClient.initClientHooks(this, this.f_91036_); this.f_90990_.m_85409_(this.f_91066_.m_231817_().m_231551_()); this.f_90990_.m_85424_(this.f_91066_.m_232123_().m_231551_()); this.f_90990_.m_85426_(); diff --git a/patches/minecraft/net/minecraft/client/gui/screens/worldselection/PresetEditor.java.patch b/patches/minecraft/net/minecraft/client/gui/screens/worldselection/PresetEditor.java.patch new file mode 100644 index 0000000000..5cd0b65641 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/screens/worldselection/PresetEditor.java.patch @@ -0,0 +1,13 @@ +--- a/net/minecraft/client/gui/screens/worldselection/PresetEditor.java ++++ b/net/minecraft/client/gui/screens/worldselection/PresetEditor.java +@@ -28,6 +_,10 @@ + + @OnlyIn(Dist.CLIENT) + public interface PresetEditor { ++ /** ++ * @deprecated Forge: Use {@link net.minecraftforge.client.PresetEditorManager#get(ResourceKey)} instead. ++ */ ++ @Deprecated + Map>, PresetEditor> f_232950_ = Map.of(Optional.of(WorldPresets.f_226438_), (p_232974_, p_232975_) -> { + ChunkGenerator chunkgenerator = p_232975_.f_243796_().m_246737_(); + RegistryAccess registryaccess = p_232975_.m_246480_(); diff --git a/patches/minecraft/net/minecraft/client/gui/screens/worldselection/WorldCreationUiState.java.patch b/patches/minecraft/net/minecraft/client/gui/screens/worldselection/WorldCreationUiState.java.patch new file mode 100644 index 0000000000..5c8b2a09c2 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/screens/worldselection/WorldCreationUiState.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/client/gui/screens/worldselection/WorldCreationUiState.java ++++ b/net/minecraft/client/gui/screens/worldselection/WorldCreationUiState.java +@@ -226,7 +_,7 @@ + @Nullable + public PresetEditor m_267744_() { + Holder holder = this.m_267828_().f_267398_(); +- return holder != null ? PresetEditor.f_232950_.get(holder.m_203543_()) : null; ++ return holder != null ? holder.m_203543_().map(net.minecraftforge.client.PresetEditorManager::get).orElse(null) : null; // FORGE: redirect lookup to expanded map + } + + public List m_267815_() { diff --git a/src/generated_test/resources/data/custom_preset_editor_test/worldgen/world_preset/custom_preset_editor_test.json b/src/generated_test/resources/data/custom_preset_editor_test/worldgen/world_preset/custom_preset_editor_test.json new file mode 100644 index 0000000000..b5f3f7226f --- /dev/null +++ b/src/generated_test/resources/data/custom_preset_editor_test/worldgen/world_preset/custom_preset_editor_test.json @@ -0,0 +1,15 @@ +{ + "dimensions": { + "minecraft:overworld": { + "type": "minecraft:overworld", + "generator": { + "type": "minecraft:noise", + "biome_source": { + "type": "minecraft:fixed", + "biome": "minecraft:plains" + }, + "settings": "minecraft:overworld" + } + } + } +} \ No newline at end of file diff --git a/src/main/java/net/minecraftforge/client/DimensionSpecialEffectsManager.java b/src/main/java/net/minecraftforge/client/DimensionSpecialEffectsManager.java index bfd3cd2e77..f19c4b7b82 100644 --- a/src/main/java/net/minecraftforge/client/DimensionSpecialEffectsManager.java +++ b/src/main/java/net/minecraftforge/client/DimensionSpecialEffectsManager.java @@ -11,7 +11,6 @@ import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.dimension.BuiltinDimensionTypes; import net.minecraftforge.client.event.RegisterDimensionSpecialEffectsEvent; import net.minecraftforge.fml.ModLoader; -import net.minecraftforge.fml.ModLoadingContext; import org.jetbrains.annotations.ApiStatus; import java.util.HashMap; @@ -41,7 +40,7 @@ public final class DimensionSpecialEffectsManager var effects = new HashMap(); DEFAULT_EFFECTS = preRegisterVanillaEffects(effects); var event = new RegisterDimensionSpecialEffectsEvent(effects); - ModLoader.get().postEventWithWrapInModOrder(event, (mc, e) -> ModLoadingContext.get().setActiveContainer(mc), (mc, e) -> ModLoadingContext.get().setActiveContainer(null)); + ModLoader.get().postEventWrapContainerInModOrder(event); EFFECTS = ImmutableMap.copyOf(effects); } diff --git a/src/main/java/net/minecraftforge/client/EntitySpectatorShaderManager.java b/src/main/java/net/minecraftforge/client/EntitySpectatorShaderManager.java index f15d7dc9e4..bf3566ac04 100644 --- a/src/main/java/net/minecraftforge/client/EntitySpectatorShaderManager.java +++ b/src/main/java/net/minecraftforge/client/EntitySpectatorShaderManager.java @@ -10,7 +10,6 @@ import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.EntityType; import net.minecraftforge.client.event.RegisterEntitySpectatorShadersEvent; import net.minecraftforge.fml.ModLoader; -import net.minecraftforge.fml.ModLoadingContext; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; @@ -40,7 +39,7 @@ public final class EntitySpectatorShaderManager { var shaders = new HashMap, ResourceLocation>(); var event = new RegisterEntitySpectatorShadersEvent(shaders); - ModLoader.get().postEventWithWrapInModOrder(event, (mc, e) -> ModLoadingContext.get().setActiveContainer(mc), (mc, e) -> ModLoadingContext.get().setActiveContainer(null)); + ModLoader.get().postEventWrapContainerInModOrder(event); SHADERS = ImmutableMap.copyOf(shaders); } diff --git a/src/main/java/net/minecraftforge/client/ForgeHooksClient.java b/src/main/java/net/minecraftforge/client/ForgeHooksClient.java index 8d70aabb7b..7235c0f1c9 100644 --- a/src/main/java/net/minecraftforge/client/ForgeHooksClient.java +++ b/src/main/java/net/minecraftforge/client/ForgeHooksClient.java @@ -53,7 +53,6 @@ import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.ShaderInstance; import net.minecraft.client.renderer.block.BlockRenderDispatcher; import net.minecraft.client.renderer.block.model.BakedQuad; -import net.minecraft.client.renderer.block.model.ItemTransforms; import net.minecraft.client.renderer.culling.Frustum; import net.minecraft.client.renderer.texture.SpriteContents; import net.minecraft.client.renderer.texture.TextureAtlas; @@ -80,6 +79,7 @@ import net.minecraft.network.chat.PlayerChatMessage; import net.minecraft.network.chat.Style; import net.minecraft.network.protocol.status.ServerStatus; import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.ReloadableResourceManager; import net.minecraft.server.packs.resources.Resource; import net.minecraft.util.Mth; import net.minecraft.util.RandomSource; @@ -91,7 +91,6 @@ import net.minecraft.world.entity.HumanoidArm; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.ai.attributes.AttributeInstance; import net.minecraft.world.entity.player.Player; -import net.minecraft.world.flag.FeatureFlagSet; import net.minecraft.world.inventory.tooltip.TooltipComponent; import net.minecraft.world.item.CreativeModeTab; import net.minecraft.world.item.ItemDisplayContext; @@ -115,10 +114,12 @@ import net.minecraftforge.client.event.ClientPlayerChangeGameTypeEvent; import net.minecraftforge.client.event.ClientPlayerNetworkEvent; import net.minecraftforge.client.event.ComputeFovModifierEvent; import net.minecraftforge.client.event.CustomizeGuiOverlayEvent; +import net.minecraftforge.client.event.EntityRenderersEvent; import net.minecraftforge.client.event.InputEvent; import net.minecraftforge.client.event.ModelEvent; import net.minecraftforge.client.event.MovementInputUpdateEvent; import net.minecraftforge.client.event.RecipesUpdatedEvent; +import net.minecraftforge.client.event.RegisterClientReloadListenersEvent; import net.minecraftforge.client.event.RegisterColorHandlersEvent; import net.minecraftforge.client.event.RegisterKeyMappingsEvent; import net.minecraftforge.client.event.RegisterParticleProvidersEvent; @@ -139,8 +140,11 @@ import net.minecraftforge.client.event.sound.PlaySoundEvent; import net.minecraftforge.client.extensions.common.IClientFluidTypeExtensions; import net.minecraftforge.client.extensions.common.IClientItemExtensions; import net.minecraftforge.client.extensions.common.IClientMobEffectExtensions; +import net.minecraftforge.client.gui.ClientTooltipComponentManager; +import net.minecraftforge.client.gui.overlay.GuiOverlayManager; import net.minecraftforge.client.model.data.ModelData; import net.minecraftforge.client.textures.ForgeTextureMetadata; +import net.minecraftforge.client.textures.TextureAtlasSpriteLoaderManager; import net.minecraftforge.common.ForgeI18n; import net.minecraftforge.common.ForgeMod; import net.minecraftforge.common.MinecraftForge; @@ -153,12 +157,12 @@ import net.minecraftforge.fml.ModList; import net.minecraftforge.fml.ModLoader; import net.minecraftforge.fml.VersionChecker; import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.gametest.ForgeGameTestHooks; import net.minecraftforge.network.NetworkConstants; import net.minecraftforge.network.NetworkRegistry; import net.minecraftforge.network.ServerStatusPing; import net.minecraftforge.registries.GameData; import net.minecraftforge.versions.forge.ForgeVersion; -import org.apache.commons.lang3.tuple.Pair; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Marker; @@ -1224,4 +1228,33 @@ public class ForgeHooksClient for (var entry : entries) output.accept(entry.getKey(), entry.getValue()); } + + // Make sure the below method is only ever called once (by forge). + private static boolean initializedClientHooks = false; + // Runs during Minecraft construction, before initial resource loading. + @ApiStatus.Internal + public static void initClientHooks(Minecraft mc, ReloadableResourceManager resourceManager) + { + if (initializedClientHooks) + { + throw new IllegalStateException("Client hooks initialized more than once"); + } + initializedClientHooks = true; + + ForgeGameTestHooks.registerGametests(); + ModLoader.get().postEvent(new RegisterClientReloadListenersEvent(resourceManager)); + ModLoader.get().postEvent(new EntityRenderersEvent.RegisterLayerDefinitions()); + ModLoader.get().postEvent(new EntityRenderersEvent.RegisterRenderers()); + TextureAtlasSpriteLoaderManager.init(); + ClientTooltipComponentManager.init(); + EntitySpectatorShaderManager.init(); + ForgeHooksClient.onRegisterKeyMappings(mc.options); + RecipeBookManager.init(); + GuiOverlayManager.init(); + DimensionSpecialEffectsManager.init(); + NamedRenderTypeManager.init(); + ColorResolverManager.init(); + ItemDecoratorHandler.init(); + PresetEditorManager.init(); + } } diff --git a/src/main/java/net/minecraftforge/client/ItemDecoratorHandler.java b/src/main/java/net/minecraftforge/client/ItemDecoratorHandler.java index e4eb21df12..28e2d0ba46 100644 --- a/src/main/java/net/minecraftforge/client/ItemDecoratorHandler.java +++ b/src/main/java/net/minecraftforge/client/ItemDecoratorHandler.java @@ -21,7 +21,6 @@ import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraftforge.client.event.RegisterItemDecorationsEvent; import net.minecraftforge.fml.ModLoader; -import net.minecraftforge.fml.ModLoadingContext; @ApiStatus.Internal public final class ItemDecoratorHandler @@ -44,7 +43,7 @@ public final class ItemDecoratorHandler { var decorators = new HashMap>(); var event = new RegisterItemDecorationsEvent(decorators); - ModLoader.get().postEventWithWrapInModOrder(event, (mc, e) -> ModLoadingContext.get().setActiveContainer(mc), (mc, e) -> ModLoadingContext.get().setActiveContainer(null)); + ModLoader.get().postEventWrapContainerInModOrder(event); var builder = new ImmutableMap.Builder(); decorators.forEach((item, itemDecorators) -> builder.put(item, new ItemDecoratorHandler(itemDecorators))); DECORATOR_LOOKUP = builder.build(); diff --git a/src/main/java/net/minecraftforge/client/NamedRenderTypeManager.java b/src/main/java/net/minecraftforge/client/NamedRenderTypeManager.java index 77c9732a20..0ee3b835af 100644 --- a/src/main/java/net/minecraftforge/client/NamedRenderTypeManager.java +++ b/src/main/java/net/minecraftforge/client/NamedRenderTypeManager.java @@ -10,7 +10,6 @@ import net.minecraft.client.renderer.RenderType; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.client.event.RegisterNamedRenderTypesEvent; import net.minecraftforge.fml.ModLoader; -import net.minecraftforge.fml.ModLoadingContext; import org.jetbrains.annotations.ApiStatus; import java.util.HashMap; @@ -39,7 +38,7 @@ public final class NamedRenderTypeManager var renderTypes = new HashMap(); preRegisterVanillaRenderTypes(renderTypes); var event = new RegisterNamedRenderTypesEvent(renderTypes); - ModLoader.get().postEventWithWrapInModOrder(event, (mc, e) -> ModLoadingContext.get().setActiveContainer(mc), (mc, e) -> ModLoadingContext.get().setActiveContainer(null)); + ModLoader.get().postEventWrapContainerInModOrder(event); RENDER_TYPES = ImmutableMap.copyOf(renderTypes); } diff --git a/src/main/java/net/minecraftforge/client/PresetEditorManager.java b/src/main/java/net/minecraftforge/client/PresetEditorManager.java new file mode 100644 index 0000000000..6f0e79935d --- /dev/null +++ b/src/main/java/net/minecraftforge/client/PresetEditorManager.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.minecraftforge.client; + +import java.util.HashMap; +import java.util.Map; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +import net.minecraft.client.gui.screens.worldselection.PresetEditor; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.level.levelgen.presets.WorldPreset; +import net.minecraftforge.client.event.RegisterPresetEditorsEvent; +import net.minecraftforge.fml.ModLoader; + +public final class PresetEditorManager +{ + private PresetEditorManager() {} // Utility class + + private static Map, PresetEditor> editors = Map.of(); + + @SuppressWarnings("deprecation") + @ApiStatus.Internal + static void init() + { + // Start with the vanilla entries + Map, PresetEditor> gatheredEditors = new HashMap<>(); + // Vanilla's map uses Optionals as its keys. + // As far as we can tell there's no good reason for this, so we'll just use regular keys. + PresetEditor.EDITORS.forEach((k, v) -> k.ifPresent(key -> gatheredEditors.put(key, v))); + + // Gather mods' entries + RegisterPresetEditorsEvent event = new RegisterPresetEditorsEvent(gatheredEditors); + ModLoader.get().postEventWrapContainerInModOrder(event); + + editors = gatheredEditors; + } + + /** + * {@return the PresetEditor for the given WorldPreset key, or null if no such PresetEditor exists} + * + * @param key ResourceKey for the specified WorldPreset/PresetEditor. + */ + @Nullable + public static PresetEditor get(ResourceKey key) + { + return editors.get(key); + } +} diff --git a/src/main/java/net/minecraftforge/client/RecipeBookManager.java b/src/main/java/net/minecraftforge/client/RecipeBookManager.java index f7da8d958c..3de9594a33 100644 --- a/src/main/java/net/minecraftforge/client/RecipeBookManager.java +++ b/src/main/java/net/minecraftforge/client/RecipeBookManager.java @@ -13,7 +13,6 @@ import net.minecraft.world.item.crafting.Recipe; import net.minecraft.world.item.crafting.RecipeType; import net.minecraftforge.client.event.RegisterRecipeBookCategoriesEvent; import net.minecraftforge.fml.ModLoader; -import net.minecraftforge.fml.ModLoadingContext; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; @@ -68,7 +67,7 @@ public final class RecipeBookManager var typeCategories = new HashMap>(); var recipeCategoryLookups = new HashMap, Function, RecipeBookCategories>>(); var event = new RegisterRecipeBookCategoriesEvent(aggregateCategories, typeCategories, recipeCategoryLookups); - ModLoader.get().postEventWithWrapInModOrder(event, (mc, e) -> ModLoadingContext.get().setActiveContainer(mc), (mc, e) -> ModLoadingContext.get().setActiveContainer(null)); + ModLoader.get().postEventWrapContainerInModOrder(event); AGGREGATE_CATEGORIES.putAll(aggregateCategories); TYPE_CATEGORIES.putAll(typeCategories); RECIPE_CATEGORY_LOOKUPS.putAll(recipeCategoryLookups); diff --git a/src/main/java/net/minecraftforge/client/event/RegisterPresetEditorsEvent.java b/src/main/java/net/minecraftforge/client/event/RegisterPresetEditorsEvent.java new file mode 100644 index 0000000000..08077668c8 --- /dev/null +++ b/src/main/java/net/minecraftforge/client/event/RegisterPresetEditorsEvent.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.minecraftforge.client.event; + +import java.util.Map; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.ApiStatus; + +import net.minecraft.client.gui.screens.worldselection.PresetEditor; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.level.levelgen.presets.WorldPreset; +import net.minecraftforge.eventbus.api.Cancelable; +import net.minecraftforge.eventbus.api.Event; +import net.minecraftforge.fml.LogicalSide; +import net.minecraftforge.fml.ModLoadingContext; +import net.minecraftforge.fml.event.IModBusEvent; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; + +/** + *

Event for registering {@link PresetEditor} screen factories for world presets.

+ * + *

This event is not {@linkplain Cancelable cancellable}, and does not {@linkplain HasResult have a result}. + * + *

This event is fired on the {@linkplain FMLJavaModLoadingContext#getModEventBus() mod-specific event bus}, + * only on the {@linkplain LogicalSide#CLIENT logical client}.

+ */ +public class RegisterPresetEditorsEvent extends Event implements IModBusEvent +{ + private static final Logger LOGGER = LogManager.getLogger(); + + private final Map, PresetEditor> editors; + + @ApiStatus.Internal + public RegisterPresetEditorsEvent(Map, PresetEditor> editors) + { + this.editors = editors; + } + + /** + * Registers a PresetEditor for a given world preset key. + */ + public void register(ResourceKey key, PresetEditor editor) + { + PresetEditor old = this.editors.put(key, editor); + if (old != null) + { + LOGGER.debug("PresetEditor {} overridden by mod {}", key.location(), ModLoadingContext.get().getActiveNamespace()); + } + } +} diff --git a/src/main/java/net/minecraftforge/client/gui/ClientTooltipComponentManager.java b/src/main/java/net/minecraftforge/client/gui/ClientTooltipComponentManager.java index 2ee297d8ed..8fe35de80e 100644 --- a/src/main/java/net/minecraftforge/client/gui/ClientTooltipComponentManager.java +++ b/src/main/java/net/minecraftforge/client/gui/ClientTooltipComponentManager.java @@ -10,7 +10,6 @@ import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent import net.minecraft.world.inventory.tooltip.TooltipComponent; import net.minecraftforge.client.event.RegisterClientTooltipComponentFactoriesEvent; import net.minecraftforge.fml.ModLoader; -import net.minecraftforge.fml.ModLoadingContext; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; @@ -41,7 +40,7 @@ public final class ClientTooltipComponentManager { var factories = new HashMap, Function>(); var event = new RegisterClientTooltipComponentFactoriesEvent(factories); - ModLoader.get().postEventWithWrapInModOrder(event, (mc, e) -> ModLoadingContext.get().setActiveContainer(mc), (mc, e) -> ModLoadingContext.get().setActiveContainer(null)); + ModLoader.get().postEventWrapContainerInModOrder(event); FACTORIES = ImmutableMap.copyOf(factories); } diff --git a/src/main/java/net/minecraftforge/client/gui/overlay/GuiOverlayManager.java b/src/main/java/net/minecraftforge/client/gui/overlay/GuiOverlayManager.java index 89990c0a76..cd094a2c4a 100644 --- a/src/main/java/net/minecraftforge/client/gui/overlay/GuiOverlayManager.java +++ b/src/main/java/net/minecraftforge/client/gui/overlay/GuiOverlayManager.java @@ -10,7 +10,6 @@ import com.google.common.collect.ImmutableMap; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.client.event.RegisterGuiOverlaysEvent; import net.minecraftforge.fml.ModLoader; -import net.minecraftforge.fml.ModLoadingContext; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; @@ -53,7 +52,7 @@ public final class GuiOverlayManager var orderedOverlays = new ArrayList(); preRegisterVanillaOverlays(overlays, orderedOverlays); var event = new RegisterGuiOverlaysEvent(overlays, orderedOverlays); - ModLoader.get().postEventWithWrapInModOrder(event, (mc, e) -> ModLoadingContext.get().setActiveContainer(mc), (mc, e) -> ModLoadingContext.get().setActiveContainer(null)); + ModLoader.get().postEventWrapContainerInModOrder(event); OVERLAYS = orderedOverlays.stream() .map(id -> new NamedGuiOverlay(id, overlays.get(id))) .collect(ImmutableList.toImmutableList()); diff --git a/src/main/java/net/minecraftforge/client/model/geometry/GeometryLoaderManager.java b/src/main/java/net/minecraftforge/client/model/geometry/GeometryLoaderManager.java index f366e4c048..2be549bb93 100644 --- a/src/main/java/net/minecraftforge/client/model/geometry/GeometryLoaderManager.java +++ b/src/main/java/net/minecraftforge/client/model/geometry/GeometryLoaderManager.java @@ -10,7 +10,6 @@ import net.minecraft.resources.ResourceLocation; import net.minecraftforge.client.event.ModelEvent; import net.minecraftforge.client.model.ElementsModel; import net.minecraftforge.fml.ModLoader; -import net.minecraftforge.fml.ModLoadingContext; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; @@ -50,7 +49,7 @@ public final class GeometryLoaderManager var loaders = new HashMap>(); loaders.put(new ResourceLocation("minecraft:elements"), ElementsModel.Loader.INSTANCE_DEPRECATED); // TODO: Deprecated. To be removed in 1.20 var event = new ModelEvent.RegisterGeometryLoaders(loaders); - ModLoader.get().postEventWithWrapInModOrder(event, (mc, e) -> ModLoadingContext.get().setActiveContainer(mc), (mc, e) -> ModLoadingContext.get().setActiveContainer(null)); + ModLoader.get().postEventWrapContainerInModOrder(event); LOADERS = ImmutableMap.copyOf(loaders); LOADER_LIST = loaders.keySet().stream().map(ResourceLocation::toString).collect(Collectors.joining(", ")); } diff --git a/src/main/java/net/minecraftforge/client/textures/TextureAtlasSpriteLoaderManager.java b/src/main/java/net/minecraftforge/client/textures/TextureAtlasSpriteLoaderManager.java index 478eedf2dc..06d0b6ab47 100644 --- a/src/main/java/net/minecraftforge/client/textures/TextureAtlasSpriteLoaderManager.java +++ b/src/main/java/net/minecraftforge/client/textures/TextureAtlasSpriteLoaderManager.java @@ -9,7 +9,6 @@ import com.google.common.collect.ImmutableMap; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.client.event.RegisterTextureAtlasSpriteLoadersEvent; import net.minecraftforge.fml.ModLoader; -import net.minecraftforge.fml.ModLoadingContext; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; @@ -38,7 +37,7 @@ public final class TextureAtlasSpriteLoaderManager { var loaders = new HashMap(); var event = new RegisterTextureAtlasSpriteLoadersEvent(loaders); - ModLoader.get().postEventWithWrapInModOrder(event, (mc, e) -> ModLoadingContext.get().setActiveContainer(mc), (mc, e) -> ModLoadingContext.get().setActiveContainer(null)); + ModLoader.get().postEventWrapContainerInModOrder(event); LOADERS = ImmutableMap.copyOf(loaders); } diff --git a/src/main/java/net/minecraftforge/registries/GameData.java b/src/main/java/net/minecraftforge/registries/GameData.java index 95858dd56d..b58319ce37 100644 --- a/src/main/java/net/minecraftforge/registries/GameData.java +++ b/src/main/java/net/minecraftforge/registries/GameData.java @@ -379,7 +379,7 @@ public class GameData if (forgeRegistry != null) forgeRegistry.unfreeze(); - ModLoader.get().postEventWithWrapInModOrder(registerEvent, (mc, e) -> ModLoadingContext.get().setActiveContainer(mc), (mc, e)-> ModLoadingContext.get().setActiveContainer(null)); + ModLoader.get().postEventWrapContainerInModOrder(registerEvent); if (forgeRegistry != null) forgeRegistry.freeze(); diff --git a/src/main/java/net/minecraftforge/registries/RegistryManager.java b/src/main/java/net/minecraftforge/registries/RegistryManager.java index 594105b2d9..da1670b626 100644 --- a/src/main/java/net/minecraftforge/registries/RegistryManager.java +++ b/src/main/java/net/minecraftforge/registries/RegistryManager.java @@ -24,7 +24,6 @@ import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.core.Registry; import net.minecraftforge.fml.ModLoader; -import net.minecraftforge.fml.ModLoadingContext; import net.minecraftforge.network.HandshakeMessages; import net.minecraftforge.registries.ForgeRegistry.Snapshot; import org.apache.commons.lang3.tuple.Pair; @@ -149,8 +148,8 @@ public class RegistryManager DataPackRegistryEvent.NewRegistry dataPackEvent = new DataPackRegistryEvent.NewRegistry(); vanillaRegistryKeys = Set.copyOf(BuiltInRegistries.REGISTRY.keySet()); - ModLoader.get().postEventWithWrapInModOrder(event, (mc, e) -> ModLoadingContext.get().setActiveContainer(mc), (mc, e) -> ModLoadingContext.get().setActiveContainer(null)); - ModLoader.get().postEventWithWrapInModOrder(dataPackEvent, (mc, e) -> ModLoadingContext.get().setActiveContainer(mc), (mc, e) -> ModLoadingContext.get().setActiveContainer(null)); + ModLoader.get().postEventWrapContainerInModOrder(event); + ModLoader.get().postEventWrapContainerInModOrder(dataPackEvent); event.fill(); dataPackEvent.process(); diff --git a/src/test/java/net/minecraftforge/debug/client/CustomPresetEditorTest.java b/src/test/java/net/minecraftforge/debug/client/CustomPresetEditorTest.java new file mode 100644 index 0000000000..f306e98b43 --- /dev/null +++ b/src/test/java/net/minecraftforge/debug/client/CustomPresetEditorTest.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.minecraftforge.debug.client; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; + +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.Button.OnPress; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.worldselection.CreateWorldScreen; +import net.minecraft.client.gui.screens.worldselection.WorldCreationContext; +import net.minecraft.client.gui.screens.worldselection.WorldCreationContext.DimensionsUpdater; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.RegistrySetBuilder; +import net.minecraft.core.registries.Registries; +import net.minecraft.data.DataGenerator; +import net.minecraft.data.PackOutput; +import net.minecraft.data.worldgen.BootstapContext; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeSource; +import net.minecraft.world.level.biome.Biomes; +import net.minecraft.world.level.biome.FixedBiomeSource; +import net.minecraft.world.level.chunk.ChunkGenerator; +import net.minecraft.world.level.dimension.BuiltinDimensionTypes; +import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.dimension.LevelStem; +import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator; +import net.minecraft.world.level.levelgen.NoiseGeneratorSettings; +import net.minecraft.world.level.levelgen.presets.WorldPreset; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.client.event.RegisterPresetEditorsEvent; +import net.minecraftforge.common.data.DatapackBuiltinEntriesProvider; +import net.minecraftforge.data.event.GatherDataEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.Mod.EventBusSubscriber; +import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus; + +@Mod(CustomPresetEditorTest.MODID) +public class CustomPresetEditorTest +{ + public static final String MODID = "custom_preset_editor_test"; + public static final ResourceKey WORLD_PRESET_KEY = ResourceKey.create(Registries.WORLD_PRESET, new ResourceLocation(MODID, MODID)); + + @EventBusSubscriber(modid = MODID, bus = Bus.MOD) + public static class CommonModEvents + { + @SubscribeEvent + public static void onGatherData(GatherDataEvent event) + { + DataGenerator gen = event.getGenerator(); + PackOutput packOutput = gen.getPackOutput(); + CompletableFuture lookupProvider = event.getLookupProvider(); + + RegistrySetBuilder registrySetBuilder = new RegistrySetBuilder() + .add(Registries.WORLD_PRESET, context -> context.register(WORLD_PRESET_KEY, makeWorldPreset(context))); + + gen.addProvider(event.includeServer(), new DatapackBuiltinEntriesProvider(packOutput, lookupProvider, registrySetBuilder, Set.of(MODID)) + { + @Override + public String getName() + { + return MODID + ":" + super.getName(); // dataproviders must have unique names + } + }); + } + + private static WorldPreset makeWorldPreset(BootstapContext context) + { + Holder overworldNoise = context.lookup(Registries.NOISE_SETTINGS) + .getOrThrow(NoiseGeneratorSettings.OVERWORLD); + Holder plains = context.lookup(Registries.BIOME) + .getOrThrow(Biomes.PLAINS); + Holder overworldDimensionType = context.lookup(Registries.DIMENSION_TYPE) + .getOrThrow(BuiltinDimensionTypes.OVERWORLD); + BiomeSource biomeSource = new FixedBiomeSource(plains); + ChunkGenerator chunkGenerator = new NoiseBasedChunkGenerator(biomeSource, overworldNoise); + LevelStem levelStem = new LevelStem(overworldDimensionType, chunkGenerator); + return new WorldPreset(Map.of(LevelStem.OVERWORLD, levelStem)); + } + } + + @EventBusSubscriber(modid = MODID, value = Dist.CLIENT, bus = Bus.MOD) + public static class ClientModEvents + { + @SubscribeEvent + public static void onRegisterPresetEditors(RegisterPresetEditorsEvent event) + { + event.register(WORLD_PRESET_KEY, SwampDesertScreen::new); + } + } + + public static class SwampDesertScreen extends Screen + { + private final CreateWorldScreen parent; + + public SwampDesertScreen(CreateWorldScreen parent, WorldCreationContext context) + { + super(Component.literal(MODID)); + this.parent = parent; + } + + @Override + protected void init() + { + // Examples of configuring the world preset via gui controls. + // These buttons tell the parent create world screen to replace the overworld with a single-biome dimension when clicked. + this.addRenderableWidget( + Button.builder(Component.literal("Swamp"), this.onPressBiomeButton(Biomes.SWAMP)) + .bounds(this.width/2 - 155, this.height - 28, 150, 20) + .build()); + this.addRenderableWidget( + Button.builder(Component.literal("Desert"), this.onPressBiomeButton(Biomes.DESERT)) + .bounds(this.width/2 + 5, this.height - 28, 150, 20) + .build()); + } + + private OnPress onPressBiomeButton(ResourceKey biomeKey) + { + return button -> { + this.parent.getUiState().updateDimensions(singleBiomeDimension(biomeKey)); + this.minecraft.setScreen(this.parent); + }; + } + + private DimensionsUpdater singleBiomeDimension(ResourceKey biomeKey) + { + // The original dimension list from the world preset json is provided to the DimensionsUpdater lambda here. + // We can alter which dimensions are present by returning a different list of dimensions. + return (registries, oldDimensions) -> { + Holder overworldNoise = registries.registryOrThrow(Registries.NOISE_SETTINGS) + .getHolderOrThrow(NoiseGeneratorSettings.OVERWORLD); + Holder biome = registries.registryOrThrow(Registries.BIOME).getHolderOrThrow(biomeKey); + return oldDimensions.replaceOverworldGenerator(registries, new NoiseBasedChunkGenerator(new FixedBiomeSource(biome), overworldNoise)); + }; + } + } +} diff --git a/src/test/resources/META-INF/mods.toml b/src/test/resources/META-INF/mods.toml index 75c14c3962..06f7bd98fd 100644 --- a/src/test/resources/META-INF/mods.toml +++ b/src/test/resources/META-INF/mods.toml @@ -30,6 +30,8 @@ modId="mdk_datagen" modId="deferred_work_queue_test" [[mods]] modId="custom_game_rule_test" +[[mods]] + modId="custom_preset_editor_test" # LEGACY TEST CASES ###### The mods below are from the old test framework and need to be yeeted later again. diff --git a/src/test/resources/assets/custom_preset_editor_test/lang/en_us.json b/src/test/resources/assets/custom_preset_editor_test/lang/en_us.json new file mode 100644 index 0000000000..8e1aad728b --- /dev/null +++ b/src/test/resources/assets/custom_preset_editor_test/lang/en_us.json @@ -0,0 +1,3 @@ +{ + "generator.custom_preset_editor_test.custom_preset_editor_test": "Example Preset Editor" +} \ No newline at end of file diff --git a/src/test/resources/data/minecraft/tags/worldgen/world_preset/normal.json b/src/test/resources/data/minecraft/tags/worldgen/world_preset/normal.json new file mode 100644 index 0000000000..d9c1417266 --- /dev/null +++ b/src/test/resources/data/minecraft/tags/worldgen/world_preset/normal.json @@ -0,0 +1,5 @@ +{ + "values": [ + "custom_preset_editor_test:custom_preset_editor_test" + ] +} \ No newline at end of file