From 9ee0f000304b1b7b727312af4c47dcc4a7b70109 Mon Sep 17 00:00:00 2001 From: Kenzie Togami Date: Mon, 4 Mar 2019 18:31:20 -0800 Subject: [PATCH] Initial command registration setup. Pretty hacky, subcommands do not work, some arguments missing. --- worldedit-forge/build.gradle | 2 +- .../sk89q/worldedit/forge/CommandWrapper.java | 157 ++++++++++++------ .../sk89q/worldedit/forge/ForgeAdapter.java | 18 +- .../forge/ForgePermissionsProvider.java | 5 +- .../sk89q/worldedit/forge/ForgePlatform.java | 13 +- .../sk89q/worldedit/forge/ForgeWorldEdit.java | 35 +--- .../forge/net/handler/WECUIPacketHandler.java | 4 +- .../src/main/resources/META-INF/mods.toml | 8 +- .../src/main/resources/pack.mcmeta | 6 + 9 files changed, 153 insertions(+), 95 deletions(-) create mode 100644 worldedit-forge/src/main/resources/pack.mcmeta diff --git a/worldedit-forge/build.gradle b/worldedit-forge/build.gradle index faaa4931c..536739a5c 100644 --- a/worldedit-forge/build.gradle +++ b/worldedit-forge/build.gradle @@ -14,7 +14,7 @@ buildscript { apply plugin: 'net.minecraftforge.gradle' def minecraftVersion = "1.13.2" -def forgeVersion = "25.0.34" +def forgeVersion = "25.0.70" dependencies { compile project(':worldedit-core') diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/CommandWrapper.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/CommandWrapper.java index 1ad0f10ca..cb1d051b4 100644 --- a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/CommandWrapper.java +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/CommandWrapper.java @@ -19,60 +19,123 @@ package com.sk89q.worldedit.forge; +import com.mojang.brigadier.Command; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.ArgumentBuilder; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.tree.CommandNode; +import com.mojang.brigadier.tree.LiteralCommandNode; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.event.platform.CommandEvent; import com.sk89q.worldedit.util.command.CommandMapping; -import net.minecraft.command.CommandBase; -import net.minecraft.command.CommandException; -import net.minecraft.command.ICommand; -import net.minecraft.command.ICommandSender; -import net.minecraft.server.MinecraftServer; +import com.sk89q.worldedit.util.command.Parameter; +import net.minecraft.command.CommandSource; +import net.minecraft.entity.player.EntityPlayerMP; -import java.util.Arrays; -import java.util.List; +import java.util.LinkedList; +import java.util.function.Predicate; -import javax.annotation.Nullable; +import static com.sk89q.worldedit.forge.ForgeAdapter.adaptPlayer; +import static net.minecraft.command.Commands.argument; +import static net.minecraft.command.Commands.literal; -public class CommandWrapper extends CommandBase { - private CommandMapping command; +public class CommandWrapper { - protected CommandWrapper(CommandMapping command) { - this.command = command; - } + public static void register(CommandDispatcher dispatcher, CommandMapping command) { + LiteralArgumentBuilder base = literal(command.getPrimaryAlias()); + LinkedList> parameterStack = new LinkedList<>(); + LinkedList> optionalParameterStack = new LinkedList<>(); + boolean hasFlag = false; + for (Parameter parameter : command.getDescription().getParameters()) { + if (parameter.isValueFlag()) { + if (!hasFlag) { + hasFlag = true; + optionalParameterStack.push(argument("flags", StringArgumentType.string())); + } + } else if (parameter.isOptional()) { + optionalParameterStack.push(argument(parameter.getName(), StringArgumentType.string())); + } else { + parameterStack.push(argument(parameter.getName(), StringArgumentType.string())); + } + } - @Override - public String getName() { - return command.getPrimaryAlias(); - } - - @Override - public List getAliases() { - return Arrays.asList(command.getAllAliases()); - } - - @Override - public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { - } - - @Override - public String getUsage(ICommandSender icommandsender) { - return "/" + command.getPrimaryAlias() + " " + command.getDescription().getUsage(); - } - - @Override - public int getRequiredPermissionLevel() { - return 0; - } - - @Override - public boolean checkPermission(MinecraftServer server, ICommandSender sender) { - return true; - } - - @Override - public int compareTo(@Nullable ICommand o) { - if (o == null) { - return 0; + ArgumentBuilder argument = buildChildNodes(parameterStack, optionalParameterStack, command); + if (argument != null) { + base.then(argument); } else { - return super.compareTo(o); + base.executes(commandFor(command)); + } + LiteralCommandNode registered = + dispatcher.register( + base.requires(requirementsFor(command)) + ); + for (String alias : command.getAllAliases()) { + dispatcher.register( + literal(alias).redirect(registered) + ); } } + + /** + * Make the appropriate {@code then()} and {@code execute()} calls to emulate required and + * optional parameters, given the argument orders. + * + * @param parameterStack required parameters + * @param optionalParameterStack optional parameters + * @return the node with all calls chained + */ + private static ArgumentBuilder buildChildNodes(LinkedList> parameterStack, + LinkedList> optionalParameterStack, + CommandMapping mapping) { + ArgumentBuilder currentChild = null; + Command command = commandFor(mapping); + while (!optionalParameterStack.isEmpty()) { + ArgumentBuilder next = optionalParameterStack.removeLast(); + if (currentChild != null) { + next.then(currentChild.executes(command)); + } + currentChild = next; + } + boolean requiredExecute = false; + while (!parameterStack.isEmpty()) { + ArgumentBuilder next = parameterStack.removeLast(); + if (currentChild != null) { + next.then(currentChild); + } + if (!requiredExecute) { + // first required parameter also gets execute + requiredExecute = true; + next.executes(command); + } + currentChild = next; + } + return currentChild; + } + + private static Command commandFor(CommandMapping mapping) { + return ctx -> { + EntityPlayerMP player = ctx.getSource().asPlayer(); + if (player.world.isRemote()) { + return 0; + } + WorldEdit.getInstance().getEventBus().post(new CommandEvent( + adaptPlayer(player), + ctx.getRange().get(ctx.getInput()) + )); + return 1; + }; + } + + private static Predicate requirementsFor(CommandMapping mapping) { + return ctx -> { + ForgePermissionsProvider permsProvider = ForgeWorldEdit.inst.getPermissionsProvider(); + return ctx.getEntity() instanceof EntityPlayerMP && + mapping.getDescription().getPermissions().stream() + .allMatch(perm -> permsProvider.hasPermission( + (EntityPlayerMP) ctx.getEntity(), perm + )); + }; + } + } diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeAdapter.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeAdapter.java index 58aba645c..fe13ed7eb 100644 --- a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeAdapter.java +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeAdapter.java @@ -40,6 +40,7 @@ import com.sk89q.worldedit.world.item.ItemType; import com.sk89q.worldedit.world.item.ItemTypes; import net.minecraft.block.Block; import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; @@ -59,7 +60,9 @@ import java.util.Map; import java.util.TreeMap; import java.util.stream.Collectors; -final class ForgeAdapter { +import static com.google.common.base.Preconditions.checkNotNull; + +public final class ForgeAdapter { private ForgeAdapter() { } @@ -154,7 +157,7 @@ final class ForgeAdapter { private static IBlockState applyProperties(StateContainer stateContainer, IBlockState newState, Map, Object> states) { for (Map.Entry, Object> state : states.entrySet()) { - IProperty property = stateContainer.getProperty(state.getKey().getName()); + IProperty property = stateContainer.getProperty(state.getKey().getName()); Comparable value = (Comparable) state.getValue(); // we may need to adapt this value, depending on the source prop if (property instanceof DirectionProperty) { @@ -212,4 +215,15 @@ final class ForgeAdapter { CompoundTag tag = NBTConverter.fromNative(itemStack.serializeNBT()); return new BaseItemStack(adapt(itemStack.getItem()), tag, itemStack.getCount()); } + + /** + * Get the WorldEdit proxy for the given player. + * + * @param player the player + * @return the WorldEdit player + */ + public static ForgePlayer adaptPlayer(EntityPlayerMP player) { + checkNotNull(player); + return new ForgePlayer(player); + } } diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePermissionsProvider.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePermissionsProvider.java index 8f9011268..6129cb31b 100644 --- a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePermissionsProvider.java +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePermissionsProvider.java @@ -19,7 +19,6 @@ package com.sk89q.worldedit.forge; -import net.minecraft.command.ICommand; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.world.GameType; import net.minecraftforge.fml.server.ServerLifecycleHooks; @@ -28,7 +27,7 @@ public interface ForgePermissionsProvider { boolean hasPermission(EntityPlayerMP player, String permission); - void registerPermission(ICommand command, String permission); + void registerPermission(String permission); class VanillaPermissionsProvider implements ForgePermissionsProvider { @@ -47,7 +46,7 @@ public interface ForgePermissionsProvider { } @Override - public void registerPermission(ICommand command, String permission) {} + public void registerPermission(String permission) {} } // TODO Re-add when Sponge for 1.13 is out diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlatform.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlatform.java index 4d83e1ded..0530acc54 100644 --- a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlatform.java +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlatform.java @@ -29,7 +29,7 @@ import com.sk89q.worldedit.util.command.CommandMapping; import com.sk89q.worldedit.util.command.Dispatcher; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.registry.Registries; -import net.minecraft.command.ServerCommandManager; +import net.minecraft.command.Commands; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.server.MinecraftServer; import net.minecraft.server.management.PlayerList; @@ -37,14 +37,13 @@ import net.minecraft.util.ResourceLocation; import net.minecraft.world.WorldServer; import net.minecraftforge.fml.server.ServerLifecycleHooks; +import javax.annotation.Nullable; import java.util.ArrayList; import java.util.Collection; import java.util.EnumMap; import java.util.List; import java.util.Map; -import javax.annotation.Nullable; - class ForgePlatform extends AbstractPlatform implements MultiUserPlatform { private final ForgeWorldEdit mod; @@ -120,15 +119,13 @@ class ForgePlatform extends AbstractPlatform implements MultiUserPlatform { @Override public void registerCommands(Dispatcher dispatcher) { if (server == null) return; - ServerCommandManager mcMan = (ServerCommandManager) server.getCommandManager(); + Commands mcMan = server.getCommandManager(); for (final CommandMapping command : dispatcher.getCommands()) { - CommandWrapper wrapper = new CommandWrapper(command); - mcMan.registerCommand(wrapper); + CommandWrapper.register(mcMan.getDispatcher(), command); if (command.getDescription().getPermissions().size() > 0) { - ForgeWorldEdit.inst.getPermissionsProvider().registerPermission(wrapper, command.getDescription().getPermissions().get(0)); for (int i = 1; i < command.getDescription().getPermissions().size(); i++) { - ForgeWorldEdit.inst.getPermissionsProvider().registerPermission(null, command.getDescription().getPermissions().get(i)); + ForgeWorldEdit.inst.getPermissionsProvider().registerPermission(command.getDescription().getPermissions().get(i)); } } } diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java index 8b26c285f..985880a44 100644 --- a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java @@ -20,6 +20,7 @@ package com.sk89q.worldedit.forge; import static com.google.common.base.Preconditions.checkNotNull; +import static com.sk89q.worldedit.forge.ForgeAdapter.adaptPlayer; import com.google.common.base.Joiner; import com.sk89q.worldedit.LocalSession; @@ -92,9 +93,6 @@ public class ForgeWorldEdit { inst = this; FMLJavaModLoadingContext.get().getModEventBus().addListener(this::init); - FMLJavaModLoadingContext.get().getModEventBus().addListener(this::serverAboutToStart); - FMLJavaModLoadingContext.get().getModEventBus().addListener(this::serverStopping); - FMLJavaModLoadingContext.get().getModEventBus().addListener(this::serverStarted); MinecraftForge.EVENT_BUS.register(ThreadSafeCache.getInstance()); MinecraftForge.EVENT_BUS.register(this); @@ -123,6 +121,7 @@ public class ForgeWorldEdit { LOGGER.info("WorldEdit for Forge (version " + getInternalVersion() + ") is loaded"); } + @SubscribeEvent public void serverAboutToStart(FMLServerAboutToStartEvent event) { if (this.platform != null) { LOGGER.warn("FMLServerStartingEvent occurred when FMLServerStoppingEvent hasn't"); @@ -165,29 +164,18 @@ public class ForgeWorldEdit { } } + @SubscribeEvent public void serverStopping(FMLServerStoppingEvent event) { WorldEdit worldEdit = WorldEdit.getInstance(); worldEdit.getSessionManager().unload(); worldEdit.getPlatformManager().unregister(platform); } + @SubscribeEvent public void serverStarted(FMLServerStartedEvent event) { WorldEdit.getInstance().getEventBus().post(new PlatformReadyEvent()); } - @SubscribeEvent - public void onCommandEvent(CommandEvent event) { - if ((event.getSender() instanceof EntityPlayerMP)) { - if (((EntityPlayerMP) event.getSender()).world.isRemote) return; - String[] split = new String[event.getParameters().length + 1]; - System.arraycopy(event.getParameters(), 0, split, 1, event.getParameters().length); - split[0] = event.getCommand().getName(); - com.sk89q.worldedit.event.platform.CommandEvent weEvent = - new com.sk89q.worldedit.event.platform.CommandEvent(wrap((EntityPlayerMP) event.getSender()), Joiner.on(" ").join(split)); - WorldEdit.getInstance().getEventBus().post(weEvent); - } - } - @SubscribeEvent public void onPlayerInteract(PlayerInteractEvent event) { if (platform == null) { @@ -215,7 +203,7 @@ public class ForgeWorldEdit { } WorldEdit we = WorldEdit.getInstance(); - ForgePlayer player = wrap((EntityPlayerMP) event.getEntityPlayer()); + ForgePlayer player = adaptPlayer((EntityPlayerMP) event.getEntityPlayer()); ForgeWorld world = getWorld(event.getEntityPlayer().world); if (event instanceof PlayerInteractEvent.LeftClickEmpty) { @@ -259,17 +247,6 @@ public class ForgeWorldEdit { return this.config; } - /** - * Get the WorldEdit proxy for the given player. - * - * @param player the player - * @return the WorldEdit player - */ - public ForgePlayer wrap(EntityPlayerMP player) { - checkNotNull(player); - return new ForgePlayer(player); - } - /** * Get the session for a player. * @@ -278,7 +255,7 @@ public class ForgeWorldEdit { */ public LocalSession getSession(EntityPlayerMP player) { checkNotNull(player); - return WorldEdit.getInstance().getSessionManager().get(wrap(player)); + return WorldEdit.getInstance().getSessionManager().get(adaptPlayer(player)); } /** diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/net/handler/WECUIPacketHandler.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/net/handler/WECUIPacketHandler.java index 215e31526..c03218b01 100644 --- a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/net/handler/WECUIPacketHandler.java +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/net/handler/WECUIPacketHandler.java @@ -32,6 +32,8 @@ import net.minecraftforge.fml.network.event.EventNetworkChannel; import java.nio.charset.Charset; +import static com.sk89q.worldedit.forge.ForgeAdapter.adaptPlayer; + public class WECUIPacketHandler { public static final Charset UTF_8_CHARSET = Charset.forName("UTF-8"); private static final String PROTOCOL_VERSION = Integer.toString(1); @@ -57,7 +59,7 @@ public class WECUIPacketHandler { String text = event.getPayload().toString(UTF_8_CHARSET); session.handleCUIInitializationMessage(text); - session.describeCUI(ForgeWorldEdit.inst.wrap(player)); + session.describeCUI(adaptPlayer(player)); } public static void callProcessPacket(NetworkEvent.ClientCustomPayloadEvent event) { diff --git a/worldedit-forge/src/main/resources/META-INF/mods.toml b/worldedit-forge/src/main/resources/META-INF/mods.toml index 9bb66bea4..c89f9c261 100644 --- a/worldedit-forge/src/main/resources/META-INF/mods.toml +++ b/worldedit-forge/src/main/resources/META-INF/mods.toml @@ -15,7 +15,7 @@ authors="sk89q, wizjany, TomyLobo, kenzierocks, Me4502" # The modid of the mod modId="worldedit" # The version number of the mod - there's a few well known ${} variables useable here or just hardcode it -version="${internalVersion}" +version="${version}" # A display name for the mod displayName="WorldEdit" # The description text for the mod (multi line!) @@ -23,11 +23,11 @@ description=''' WorldEdit is an easy-to-use in-game world editor for Minecraft, supporting both single- and multi-player. ''' [[dependencies.worldedit]] - modId="minecraft" + modId="forge" mandatory=true - versionRange="[1.13.2]" + versionRange="[${forge_version},)" ordering="NONE" - side="SERVER" + side="BOTH" [[dependencies.worldedit]] modId="sponge" mandatory=false diff --git a/worldedit-forge/src/main/resources/pack.mcmeta b/worldedit-forge/src/main/resources/pack.mcmeta new file mode 100644 index 000000000..b48da3b7d --- /dev/null +++ b/worldedit-forge/src/main/resources/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "description": "WorldEdit Resources", + "pack_format": 4 + } +} \ No newline at end of file