From fc5573333d2ed4c0829b6e4e96e7e4c532bf88ec Mon Sep 17 00:00:00 2001 From: tterrag Date: Sat, 7 Sep 2019 01:09:42 -0400 Subject: [PATCH] Make /locate command support modded structures --- .../command/impl/LocateCommand.java.patch | 17 ++++ .../common/command/StructureArgument.java | 93 +++++++++++++++++++ .../minecraftforge/registries/GameData.java | 2 +- .../resources/assets/forge/lang/en_us.json | 2 + 4 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 patches/minecraft/net/minecraft/command/impl/LocateCommand.java.patch create mode 100644 src/main/java/net/minecraftforge/common/command/StructureArgument.java diff --git a/patches/minecraft/net/minecraft/command/impl/LocateCommand.java.patch b/patches/minecraft/net/minecraft/command/impl/LocateCommand.java.patch new file mode 100644 index 000000000..c5fb832c6 --- /dev/null +++ b/patches/minecraft/net/minecraft/command/impl/LocateCommand.java.patch @@ -0,0 +1,17 @@ +--- a/net/minecraft/command/impl/LocateCommand.java ++++ b/net/minecraft/command/impl/LocateCommand.java +@@ -20,6 +20,14 @@ + public static void func_198528_a(CommandDispatcher p_198528_0_) { + p_198528_0_.register(Commands.func_197057_a("locate").requires((p_198533_0_) -> { + return p_198533_0_.func_197034_c(2); ++ }).then(Commands.func_197056_a("structure_type", net.minecraftforge.common.command.StructureArgument.structure()) ++ .suggests(net.minecraftforge.common.command.StructureArgument.suggestions()) ++ .executes(ctx -> { ++ return func_198534_a(ctx.getSource(), ctx.getArgument("structure_type", net.minecraft.util.ResourceLocation.class).toString().replace("minecraft:", "")); ++ }))); ++ if (true) return; // Forge: replace this code with above extensible version ++ p_198528_0_.register(Commands.func_197057_a("locate").requires((p_198533_0_) -> { ++ return p_198533_0_.func_197034_c(2); + }).then(Commands.func_197057_a("Pillager_Outpost").executes((p_198530_0_) -> { + return func_198534_a(p_198530_0_.getSource(), "Pillager_Outpost"); + })).then(Commands.func_197057_a("Mineshaft").executes((p_198535_0_) -> { diff --git a/src/main/java/net/minecraftforge/common/command/StructureArgument.java b/src/main/java/net/minecraftforge/common/command/StructureArgument.java new file mode 100644 index 000000000..0fd673675 --- /dev/null +++ b/src/main/java/net/minecraftforge/common/command/StructureArgument.java @@ -0,0 +1,93 @@ +package net.minecraftforge.common.command; + +import com.google.common.collect.Streams; +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import com.mojang.brigadier.suggestion.SuggestionProvider; + +import net.minecraft.command.CommandSource; +import net.minecraft.command.ISuggestionProvider; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.ResourceLocationException; +import net.minecraft.util.text.TranslationTextComponent; +import net.minecraft.world.gen.feature.structure.Structure; +import net.minecraftforge.registries.GameData; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Locale; + +public class StructureArgument implements ArgumentType +{ + private static final Collection EXAMPLES = Arrays.asList("minecraft:village", "Mansion"); + public static final DynamicCommandExceptionType STRUCTURE_UNKNOWN_TYPE = new DynamicCommandExceptionType((p_211367_0_) -> + { + return new TranslationTextComponent("structure.notFound", p_211367_0_); + }); + + public static StructureArgument structure() + { + return new StructureArgument(); + } + + public static SuggestionProvider suggestions() + { + return (ctx, sb) -> ISuggestionProvider.suggest(Streams.concat( + GameData.getStructureMap().values().stream().map(Structure::getStructureName), + GameData.getStructureFeatures().keySet().stream().map(ResourceLocation::toString)), sb); + } + + public static ResourceLocation getStructureId(CommandContext context, String name) throws CommandSyntaxException + { + return checkIfStructureExists(context.getArgument(name, ResourceLocation.class)); + } + + private static ResourceLocation checkIfStructureExists(ResourceLocation id) throws CommandSyntaxException + { + if (id.getNamespace().equals("minecraft")) + { + // Special case vanilla hardcoded names, for vanilla compat + Structure structure = GameData.getStructureMap().values().stream() + .filter(s -> s.getStructureName().equalsIgnoreCase(id.getPath())) + .findFirst() + .orElse(null); + if (structure != null) + return id; + } + GameData.getStructureFeatures().getValue(id).orElseThrow(() -> STRUCTURE_UNKNOWN_TYPE.create(id)); + return id; + } + + private static final SimpleCommandExceptionType INVALID_EXCEPTION = new SimpleCommandExceptionType(new TranslationTextComponent("argument.id.invalid")); + + public ResourceLocation parse(StringReader reader) throws CommandSyntaxException + { + // Logic taken from ResourceLocation.read, but made case-insensitive + int cursor = reader.getCursor(); + try + { + while (reader.canRead() && ResourceLocation.isValidPathCharacter(Character.toLowerCase(reader.peek()))) + { + reader.skip(); + } + + String s = reader.getString().substring(cursor, reader.getCursor()); + ResourceLocation rl = new ResourceLocation(s.toLowerCase(Locale.ROOT)); + return checkIfStructureExists(rl); + } + catch (ResourceLocationException e) + { + reader.setCursor(cursor); + throw INVALID_EXCEPTION.createWithContext(reader); + } + } + + public Collection getExamples() + { + return EXAMPLES; + } +} diff --git a/src/main/java/net/minecraftforge/registries/GameData.java b/src/main/java/net/minecraftforge/registries/GameData.java index c59e4b3b7..94aaa01dc 100644 --- a/src/main/java/net/minecraftforge/registries/GameData.java +++ b/src/main/java/net/minecraftforge/registries/GameData.java @@ -198,7 +198,7 @@ public class GameData // Worldgen makeRegistry(WORLD_CARVERS, WorldCarver.class).disableSaving().disableSync().create(); makeRegistry(SURFACE_BUILDERS, SurfaceBuilder.class).disableSaving().disableSync().create(); - makeRegistry(FEATURES, Feature.class).addCallback(FeatureCallbacks.INSTANCE).disableSaving().disableSync().create(); + makeRegistry(FEATURES, Feature.class).addCallback(FeatureCallbacks.INSTANCE).disableSaving().create(); makeRegistry(DECORATORS, Placement.class).disableSaving().disableSync().create(); makeRegistry(BIOME_PROVIDER_TYPES, BiomeProviderType.class).disableSaving().disableSync().create(); makeRegistry(CHUNK_GENERATOR_TYPES, ChunkGeneratorType.class).disableSaving().disableSync().create(); diff --git a/src/main/resources/assets/forge/lang/en_us.json b/src/main/resources/assets/forge/lang/en_us.json index 5b04c5838..5b974a86a 100644 --- a/src/main/resources/assets/forge/lang/en_us.json +++ b/src/main/resources/assets/forge/lang/en_us.json @@ -72,6 +72,8 @@ "fml.messages.version.restriction.bounded.lowerexclusive":"above {0}, and {1} or below", "fml.messages.version.restriction.bounded.upperexclusive":"{0} or above, and below {1}", + "structure.notFound": "Unknown structure: %s", + "commands.forge.dimensions.list": "Currently registered dimensions by type:", "commands.forge.entity.list.invalid": "Invalid filter, does not match any entities. Use /forge entity list for a proper list", "commands.forge.entity.list.invalidworld": "Could not load world for dimension {0}. Please select a valid dimension.",