diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java index 3eae94443..330f9fb6e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java @@ -7,6 +7,7 @@ import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import dev.dejvokep.boostedyaml.YamlDocument; +import net.momirealms.craftengine.bukkit.block.worldedit.WorldEditHook; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.injector.BukkitInjector; import net.momirealms.craftengine.bukkit.plugin.network.PacketConsumers; @@ -84,6 +85,8 @@ public class BukkitBlockManager extends AbstractBlockManager { private final Map modBlockStates = new HashMap<>(); // Cached command suggestions private final List cachedSuggestions = new ArrayList<>(); + // Cached Namespace + private final Set cachedNamespaces = new HashSet<>(); // Event listeners private final BlockEventListener blockEventListener; private final FallingBlockRemoveListener fallingBlockRemoveListener; @@ -235,6 +238,22 @@ public class BukkitBlockManager extends AbstractBlockManager { for (String state : states) { this.cachedSuggestions.add(Suggestion.suggestion(state)); } + this.cachedNamespaces.clear(); + initCachedNamespaces(); + } + + private void initCachedNamespaces() { + for (Suggestion suggestion : this.cachedSuggestions) { + String id = suggestion.suggestion(); + int index = id.indexOf(':'); + if (index != -1) { + cachedNamespaces.add(id.substring(0, index)); + } + } + } + + public Collection cachedNamespaces() { + return Collections.unmodifiableSet(cachedNamespaces); } public ImmutableMap> blockAppearanceArranger() { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/WorldEditHook.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/WorldEditHook.java deleted file mode 100644 index f4992949e..000000000 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/WorldEditHook.java +++ /dev/null @@ -1,24 +0,0 @@ -package net.momirealms.craftengine.bukkit.block; - -import com.sk89q.worldedit.bukkit.BukkitBlockRegistry; -import com.sk89q.worldedit.util.concurrency.LazyReference; -import com.sk89q.worldedit.world.block.BlockType; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.ReflectionUtils; -import org.bukkit.Material; - -import java.lang.reflect.Field; - -public class WorldEditHook { - private static final Field field$BlockType$blockMaterial; - - static { - field$BlockType$blockMaterial = ReflectionUtils.getDeclaredField(BlockType.class, "blockMaterial"); - } - - public static void register(Key id) throws ReflectiveOperationException { - BlockType blockType = new BlockType(id.toString(), blockState -> blockState); - field$BlockType$blockMaterial.set(blockType, LazyReference.from(() -> new BukkitBlockRegistry.BukkitBlockMaterial(null, Material.STONE))); - BlockType.REGISTRY.register(id.toString(), blockType); - } -} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/worldedit/SuggestionHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/worldedit/SuggestionHandler.java new file mode 100644 index 000000000..da6cb7da2 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/worldedit/SuggestionHandler.java @@ -0,0 +1,31 @@ +package net.momirealms.craftengine.bukkit.block.worldedit; + +import java.util.Set; +import java.util.function.Predicate; + +public class SuggestionHandler { + private final Predicate matcher; + + private SuggestionHandler(Predicate matcher) { + this.matcher = matcher; + } + + public boolean matches(String input) { + return matcher.test(input); + } + + public static SuggestionHandler of(Integer... pos) { + Set valid = Set.of(pos); + return new SuggestionHandler(input -> { + if (input.contains(" ")) return false; + + String[] args = input.split(" "); + int index = input.endsWith(" ") ? args.length : args.length - 1; + return valid.contains(index); + }); + } + + public static SuggestionHandler custom(Predicate matcher) { + return new SuggestionHandler(matcher); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/worldedit/WorldEditHook.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/worldedit/WorldEditHook.java new file mode 100644 index 000000000..ae2f3198f --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/worldedit/WorldEditHook.java @@ -0,0 +1,116 @@ +package net.momirealms.craftengine.bukkit.block.worldedit; + +import com.google.common.collect.ImmutableMap; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.bukkit.BukkitBlockRegistry; +import com.sk89q.worldedit.event.platform.CommandSuggestionEvent; +import com.sk89q.worldedit.internal.util.Substring; +import com.sk89q.worldedit.util.concurrency.LazyReference; +import com.sk89q.worldedit.util.eventbus.Subscribe; +import com.sk89q.worldedit.world.block.BlockType; +import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; +import net.momirealms.craftengine.bukkit.util.BlockStateUtils; +import net.momirealms.craftengine.core.block.BlockStateParser; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ReflectionUtils; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerCommandPreprocessEvent; +import org.incendo.cloud.suggestion.Suggestion; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class WorldEditHook implements Listener { + private static final Field field$BlockType$blockMaterial; + private final Map handlers = ImmutableMap.of( + "//set", SuggestionHandler.of(1), + "//replace", SuggestionHandler.of(1, 2) + ); + + static { + WorldEdit.getInstance().getEventBus().register(new WorldEditHook()); + field$BlockType$blockMaterial = ReflectionUtils.getDeclaredField(BlockType.class, "blockMaterial"); + } + + public static void register(Key id) throws ReflectiveOperationException { + BlockType blockType = new BlockType(id.toString(), blockState -> blockState); + field$BlockType$blockMaterial.set(blockType, LazyReference.from(() -> new BukkitBlockRegistry.BukkitBlockMaterial(null, Material.STONE))); + BlockType.REGISTRY.register(id.toString(), blockType); + } + + public WorldEditHook() { + Bukkit.getPluginManager().registerEvents(this, BukkitCraftEngine.instance().bootstrap()); + } + + @Subscribe + public void onSuggestion(CommandSuggestionEvent event) { + String input = event.getArguments(); + String command = input.substring(0, input.indexOf(" ")); + + SuggestionHandler handler = handlers.get(command); + if (handler == null || !handler.matches(input)) return; + int start = input.lastIndexOf(" ") + 1; + int end = input.length(); + + if (start == end) { + List suggestions = BukkitCraftEngine.instance().blockManager().cachedNamespaces() + .stream() + .map(ns -> Substring.wrap(ns + ":", start, end)) + .collect(Collectors.toList()); + suggestions.addAll(event.getSuggestions()); + event.setSuggestions(suggestions); + return; + } + + String last = input.substring(start, end); + List suggestions = new ArrayList<>(); + for (Suggestion s : BukkitCraftEngine.instance().blockManager().cachedSuggestions()) { + String id = s.suggestion(); + if (id.startsWith(last)) + suggestions.add(Substring.wrap(id, start, end)); + } + suggestions.addAll(event.getSuggestions()); + event.setSuggestions(suggestions); + } + + @EventHandler(priority = EventPriority.HIGH) + public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) { + String message = event.getMessage(); + if (!message.startsWith("//")) return; + + Collection cachedNamespaces = BukkitCraftEngine.instance().blockManager().cachedNamespaces(); + + String[] args = message.split(" "); + boolean modified = false; + + for (int i = 1; i < args.length; i++) { + String token = args[i]; + + int colon = token.indexOf(':'); + if (colon == -1) continue; + + String namespace = token.substring(0, colon); + if (!cachedNamespaces.contains(namespace)) continue; + + ImmutableBlockState state = BlockStateParser.deserialize(token); + if (state == null) continue; + + String internalId = BlockStateUtils.getBlockOwnerIdFromState(state.customBlockState().handle()).toString(); + args[i] = internalId; + modified = true; + } + + if (modified) { + event.setMessage(String.join(" ", args)); + } + } +}