Skip to content

Commit 5311195

Browse files
me4502octylFractal
andauthored
Add a //registry command to search registries (#2584)
* Add a //registry command to search registries * Fix missing arg annotation for registry command * Remove RegistryType since it's not needed * Fix this up to actually work * Allow viewing the entire registry with no search param --------- Co-authored-by: Octavia Togami <octavia.togami@gmail.com>
1 parent ad872f8 commit 5311195

24 files changed

Lines changed: 274 additions & 20 deletions

File tree

worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import com.sk89q.worldedit.internal.anvil.ChunkDeleter;
4545
import com.sk89q.worldedit.internal.command.CommandUtil;
4646
import com.sk89q.worldedit.internal.util.LogManagerCompat;
47+
import com.sk89q.worldedit.registry.Registries;
4748
import com.sk89q.worldedit.registry.state.Property;
4849
import com.sk89q.worldedit.util.lifecycle.Lifecycled;
4950
import com.sk89q.worldedit.util.lifecycle.SimpleLifecycled;
@@ -251,14 +252,16 @@ private void initializeRegistries() {
251252
EntityType.REGISTRY.register(key, new EntityType(key));
252253
});
253254

254-
// ... :|
255-
GameModes.get("");
256-
WeatherTypes.get("");
257-
255+
// Registries only available via NMS
258256
BukkitImplAdapter adapter = getBukkitImplAdapter();
259257
if (adapter != null) {
260258
adapter.initializeRegistries();
261259
}
260+
261+
// ... :|
262+
GameModes.get("");
263+
WeatherTypes.get("");
264+
Registries.get("");
262265
}
263266

264267
private void setupTags() {

worldedit-core/src/main/java/com/sk89q/util/StringUtil.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,4 +384,20 @@ public static List<String> parseListInQuotes(String[] input, char delimiter, cha
384384

385385
return parsableBlocks;
386386
}
387+
388+
/**
389+
* Converts a glob pattern to a regex pattern, supporting * and ?.
390+
*
391+
* <p>
392+
* Note: this assumes that the text has been pre-validated or quoted, to not contain any regex special characters.
393+
* </p>
394+
*
395+
* @param glob The glob pattern
396+
* @return The regex pattern
397+
*/
398+
public static Pattern convertGlobToRegex(String glob) {
399+
return Pattern.compile("^" + glob
400+
.replace("*", ".*")
401+
.replace("?", ".") + "$");
402+
}
387403
}

worldedit-core/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
package com.sk89q.worldedit.command;
2121

2222
import com.google.common.collect.ImmutableList;
23+
import com.sk89q.util.StringUtil;
2324
import com.sk89q.worldedit.EditSession;
2425
import com.sk89q.worldedit.LocalConfiguration;
2526
import com.sk89q.worldedit.LocalSession;
@@ -39,6 +40,8 @@
3940
import com.sk89q.worldedit.internal.command.CommandUtil;
4041
import com.sk89q.worldedit.internal.cui.ServerCUIHandler;
4142
import com.sk89q.worldedit.math.BlockVector3;
43+
import com.sk89q.worldedit.registry.Keyed;
44+
import com.sk89q.worldedit.registry.Registry;
4245
import com.sk89q.worldedit.session.Placement;
4346
import com.sk89q.worldedit.session.PlacementType;
4447
import com.sk89q.worldedit.util.SideEffect;
@@ -49,8 +52,12 @@
4952
import com.sk89q.worldedit.util.formatting.text.Component;
5053
import com.sk89q.worldedit.util.formatting.text.TextComponent;
5154
import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
55+
import com.sk89q.worldedit.util.formatting.text.event.ClickEvent;
56+
import com.sk89q.worldedit.util.formatting.text.event.HoverEvent;
5257
import com.sk89q.worldedit.util.formatting.text.format.TextColor;
5358
import com.sk89q.worldedit.world.World;
59+
import com.sk89q.worldedit.world.biome.BiomeType;
60+
import com.sk89q.worldedit.world.block.BlockType;
5461
import com.sk89q.worldedit.world.item.ItemType;
5562
import org.enginehub.piston.CommandManager;
5663
import org.enginehub.piston.CommandManagerService;
@@ -68,6 +75,7 @@
6875
import java.util.Set;
6976
import java.util.TreeMap;
7077
import java.util.concurrent.Callable;
78+
import java.util.regex.Pattern;
7179
import java.util.stream.Collectors;
7280

7381
import static com.google.common.base.Preconditions.checkNotNull;
@@ -518,4 +526,76 @@ public Component call() throws Exception {
518526
.create(page);
519527
}
520528
}
529+
530+
private static final Pattern ALLOWED_KEY_CHARACTERS = Pattern.compile("[a-z0-9_:?*/]+");
531+
532+
@Command(
533+
name = "/registry",
534+
desc = "Search through the given registry"
535+
)
536+
@CommandPermissions("worldedit.registry")
537+
public void registry(Actor actor,
538+
@Arg(desc = "The registry to search through")
539+
Registry<?> registry,
540+
@ArgFlag(name = 'p', desc = "Page of results to return", def = "1")
541+
int page,
542+
@Arg(desc = "Search query", variable = true, def = "*")
543+
List<String> queryBits) {
544+
String query = String.join("_", queryBits);
545+
546+
if (!ALLOWED_KEY_CHARACTERS.matcher(query).matches()) {
547+
actor.printError(TranslatableComponent.of("worldedit.registry.error.invalid-key"));
548+
}
549+
550+
WorldEditAsyncCommandBuilder.createAndSendMessage(actor, new RegistrySearcher(registry, query, page),
551+
TranslatableComponent.of("worldedit.registry.searching", TextComponent.of(query)));
552+
}
553+
554+
private static class RegistrySearcher implements Callable<Component> {
555+
private final Registry<?> registry;
556+
private final String search;
557+
private final int page;
558+
private final Pattern matcher;
559+
560+
RegistrySearcher(Registry<?> registry, String search, int page) {
561+
this.registry = registry;
562+
this.search = search;
563+
this.page = page;
564+
565+
String matcherQuery = search;
566+
if (!matcherQuery.contains("*") && !matcherQuery.contains("?")) {
567+
// If there are no wildcards, add them around the query
568+
matcherQuery = "*" + matcherQuery + "*";
569+
}
570+
571+
this.matcher = StringUtil.convertGlobToRegex(matcherQuery);
572+
}
573+
574+
@Override
575+
public Component call() throws Exception {
576+
String command = "//registry " + registry.id() + " -p %page% " + search;
577+
Map<String, Component> results = new TreeMap<>();
578+
for (Keyed searchType : registry) {
579+
final String id = searchType.id();
580+
if (matcher.matcher(id).matches()) {
581+
var builder = TextComponent.builder()
582+
.append(searchType.id())
583+
.clickEvent(ClickEvent.copyToClipboard(searchType.id()));
584+
switch (searchType) {
585+
case ItemType itemType -> builder.hoverEvent(HoverEvent.showText(itemType.getRichName()));
586+
case BlockType blockType -> builder.hoverEvent(HoverEvent.showText(blockType.getRichName()));
587+
case BiomeType biomeType -> builder.hoverEvent(HoverEvent.showText(biomeType.getRichName()));
588+
default -> {
589+
}
590+
}
591+
results.put(id, builder.build());
592+
}
593+
}
594+
List<Component> list = new ArrayList<>(results.values());
595+
boolean isBlank = search.isBlank() || search.equals("*");
596+
String title = isBlank ? "Registry contents" : "Search results for '" + search + "'";
597+
return PaginationBox.fromComponents(title, command, list)
598+
.create(page);
599+
}
600+
}
521601
}

worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/RegistryConverter.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
package com.sk89q.worldedit.command.argument;
2121

2222
import com.google.common.collect.ImmutableList;
23+
import com.google.common.reflect.TypeToken;
2324
import com.sk89q.worldedit.command.util.SuggestionHelper;
2425
import com.sk89q.worldedit.registry.Keyed;
2526
import com.sk89q.worldedit.registry.Registry;
@@ -48,7 +49,6 @@
4849
import java.lang.reflect.Field;
4950
import java.util.List;
5051
import java.util.Locale;
51-
import java.util.stream.Collectors;
5252

5353
public final class RegistryConverter<V extends Keyed> implements ArgumentConverter<V> {
5454

@@ -73,6 +73,9 @@ public static void register(CommandManager commandManager) {
7373
.forEach(registryType ->
7474
commandManager.registerConverter(Key.of(registryType), from(registryType))
7575
);
76+
77+
// This must be separate as it has a generic type
78+
commandManager.registerConverter(Key.of(new TypeToken<>() {}), new RegistryConverter<>(Registry.REGISTRY));
7679
}
7780

7881
@SuppressWarnings("unchecked")
@@ -112,6 +115,6 @@ public ConversionResult<V> convert(String argument, InjectedValueAccess injected
112115

113116
@Override
114117
public List<String> getSuggestions(String input, InjectedValueAccess context) {
115-
return SuggestionHelper.getRegistrySuggestions(registry, input).collect(Collectors.toList());
118+
return SuggestionHelper.getRegistrySuggestions(registry, input).toList();
116119
}
117120
}

worldedit-core/src/main/java/com/sk89q/worldedit/registry/NamespacedRegistry.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,23 +32,36 @@ public final class NamespacedRegistry<V extends Keyed> extends Registry<V> {
3232
private final Set<String> knownNamespaces = new HashSet<>();
3333
private final String defaultNamespace;
3434

35+
@Deprecated
3536
public NamespacedRegistry(final String name) {
3637
this(name, MINECRAFT_NAMESPACE);
3738
}
3839

40+
@Deprecated
3941
public NamespacedRegistry(final String name, final boolean checkInitialized) {
4042
this(name, MINECRAFT_NAMESPACE, checkInitialized);
4143
}
4244

45+
@Deprecated
4346
public NamespacedRegistry(final String name, final String defaultNamespace) {
4447
this(name, defaultNamespace, false);
4548
}
4649

50+
@Deprecated
4751
public NamespacedRegistry(final String name, final String defaultNamespace, final boolean checkInitialized) {
4852
super(name, checkInitialized);
4953
this.defaultNamespace = defaultNamespace;
5054
}
5155

56+
public NamespacedRegistry(final String name, final String id, final String defaultNamespace) {
57+
this(name, id, defaultNamespace, false);
58+
}
59+
60+
public NamespacedRegistry(final String name, final String id, final String defaultNamespace, final boolean checkInitialized) {
61+
super(name, id, checkInitialized);
62+
this.defaultNamespace = defaultNamespace;
63+
}
64+
5265
@Nullable
5366
@Override
5467
public V get(final String key) {
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* WorldEdit, a Minecraft world manipulation toolkit
3+
* Copyright (C) sk89q <http://www.sk89q.com>
4+
* Copyright (C) WorldEdit team and contributors
5+
*
6+
* This program is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
18+
*/
19+
20+
package com.sk89q.worldedit.registry;
21+
22+
import com.sk89q.worldedit.world.biome.BiomeCategory;
23+
import com.sk89q.worldedit.world.biome.BiomeType;
24+
import com.sk89q.worldedit.world.block.BlockCategory;
25+
import com.sk89q.worldedit.world.block.BlockType;
26+
import com.sk89q.worldedit.world.entity.EntityType;
27+
import com.sk89q.worldedit.world.fluid.FluidCategory;
28+
import com.sk89q.worldedit.world.fluid.FluidType;
29+
import com.sk89q.worldedit.world.gamemode.GameMode;
30+
import com.sk89q.worldedit.world.generation.ConfiguredFeatureType;
31+
import com.sk89q.worldedit.world.generation.StructureType;
32+
import com.sk89q.worldedit.world.item.ItemCategory;
33+
import com.sk89q.worldedit.world.item.ItemType;
34+
import com.sk89q.worldedit.world.weather.WeatherType;
35+
36+
import javax.annotation.Nullable;
37+
38+
public class Registries {
39+
public static final Registry<BlockType> BLOCK_TYPE = addRegistry(BlockType.REGISTRY);
40+
public static final Registry<BlockCategory> BLOCK_CATEGORY = addRegistry(BlockCategory.REGISTRY);
41+
public static final Registry<ItemType> ITEM_TYPE = addRegistry(ItemType.REGISTRY);
42+
public static final Registry<ItemCategory> ITEM_CATEGORY = addRegistry(ItemCategory.REGISTRY);
43+
public static final Registry<GameMode> GAME_MODE = addRegistry(GameMode.REGISTRY);
44+
public static final Registry<WeatherType> WEATHER_TYPE = addRegistry(WeatherType.REGISTRY);
45+
public static final Registry<BiomeType> BIOME_TYPE = addRegistry(BiomeType.REGISTRY);
46+
public static final Registry<BiomeCategory> BIOME_CATEGORY = addRegistry(BiomeCategory.REGISTRY);
47+
public static final Registry<EntityType> ENTITY_TYPE = addRegistry(EntityType.REGISTRY);
48+
public static final Registry<FluidType> FLUID_TYPE = addRegistry(FluidType.REGISTRY);
49+
public static final Registry<FluidCategory> FLUID_CATEGORY = addRegistry(FluidCategory.REGISTRY);
50+
public static final Registry<ConfiguredFeatureType> CONFIGURED_FEATURE_TYPE = addRegistry(ConfiguredFeatureType.REGISTRY);
51+
public static final Registry<StructureType> STRUCTURE_TYPE = addRegistry(StructureType.REGISTRY);
52+
53+
private static <T extends Keyed> Registry<T> addRegistry(Registry<T> registry) {
54+
Registry.REGISTRY.register(registry.id(), registry);
55+
return registry;
56+
}
57+
58+
@Nullable
59+
public static Registry<?> get(final String id) {
60+
return Registry.REGISTRY.get(id);
61+
}
62+
}

worldedit-core/src/main/java/com/sk89q/worldedit/registry/Registry.java

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,24 +33,73 @@
3333
import static com.google.common.base.Preconditions.checkState;
3434
import static java.util.Objects.requireNonNull;
3535

36-
public class Registry<V extends Keyed> implements Iterable<V> {
36+
public class Registry<V extends Keyed> implements Iterable<V>, Keyed {
37+
public static final Registry<Registry<?>> REGISTRY = new Registry<>("registry", "registry");
38+
3739
private final Map<String, V> map = new HashMap<>();
3840
private final String name;
41+
private final String id;
3942
private final boolean checkInitialized;
4043

44+
private static String nameToId(String name) {
45+
return name.toLowerCase(Locale.ROOT).replace(' ', '_');
46+
}
47+
48+
/**
49+
* Creates a new Registry.
50+
*
51+
* @param name The name of the registry
52+
* @deprecated Use {@link #Registry(String, String)} instead to provide an ID
53+
*/
54+
@Deprecated
4155
public Registry(final String name) {
4256
this(name, false);
4357
}
4458

59+
/**
60+
* Creates a new Registry.
61+
*
62+
* @param name The name of the registry
63+
* @param checkInitialized Whether to check if WorldEdit is initialized
64+
* @deprecated Use {@link #Registry(String, String, boolean)} instead to provide an ID
65+
*/
66+
@Deprecated
4567
public Registry(final String name, final boolean checkInitialized) {
68+
this(name, nameToId(name), checkInitialized);
69+
}
70+
71+
/**
72+
* Creates a new Registry.
73+
*
74+
* @param name The name of the registry
75+
* @param id The ID of the registry
76+
*/
77+
public Registry(final String name, final String id) {
78+
this(name, id, false);
79+
}
80+
81+
/**
82+
* Creates a new Registry.
83+
*
84+
* @param name The name of the registry
85+
* @param id The ID of the registry
86+
* @param checkInitialized Whether to check if WorldEdit is initialized
87+
*/
88+
public Registry(final String name, final String id, final boolean checkInitialized) {
4689
this.name = name;
90+
this.id = id;
4791
this.checkInitialized = checkInitialized;
4892
}
4993

5094
public String getName() {
5195
return name;
5296
}
5397

98+
@Override
99+
public String id() {
100+
return this.id;
101+
}
102+
54103
@Nullable
55104
public V get(final String key) {
56105
checkState(key.equals(key.toLowerCase(Locale.ROOT)), "key must be lowercase: %s", key);

worldedit-core/src/main/java/com/sk89q/worldedit/world/biome/BiomeCategory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
*/
3232
public class BiomeCategory extends Category<BiomeType> implements Keyed {
3333

34-
public static final NamespacedRegistry<BiomeCategory> REGISTRY = new NamespacedRegistry<>("biome tag", true);
34+
public static final NamespacedRegistry<BiomeCategory> REGISTRY = new NamespacedRegistry<>("biome tag", "biome_tag", "minecraft", true);
3535

3636
public BiomeCategory(final String id) {
3737
super(id, Set::of);

0 commit comments

Comments
 (0)