diff --git a/examples/postInit/custom/vanilla.groovy b/examples/postInit/custom/vanilla.groovy index faf097b30..ef005dee7 100644 --- a/examples/postInit/custom/vanilla.groovy +++ b/examples/postInit/custom/vanilla.groovy @@ -256,3 +256,7 @@ item('minecraft:golden_apple').setRarity(textformat('-1')) eventManager.listen(EnderTeleportEvent) { event -> event.setAttackDamage 19.5f } + +command.registerCommand('groovy_test') { server, sender, args -> + sender.sendMessage('Hello from GroovyScript') +} diff --git a/src/main/java/com/cleanroommc/groovyscript/GroovyScript.java b/src/main/java/com/cleanroommc/groovyscript/GroovyScript.java index 1a0e2f2d0..f0fc00285 100644 --- a/src/main/java/com/cleanroommc/groovyscript/GroovyScript.java +++ b/src/main/java/com/cleanroommc/groovyscript/GroovyScript.java @@ -192,6 +192,7 @@ public void onPostInit(FMLPostInitializationEvent event) { @Mod.EventHandler public void onServerLoad(FMLServerStartingEvent event) { event.registerServerCommand(new GSCommand()); + VanillaModule.command.onStartServer(event.getServer()); } @SubscribeEvent diff --git a/src/main/java/com/cleanroommc/groovyscript/command/GSCommand.java b/src/main/java/com/cleanroommc/groovyscript/command/GSCommand.java index 3b3022ee3..92040b404 100644 --- a/src/main/java/com/cleanroommc/groovyscript/command/GSCommand.java +++ b/src/main/java/com/cleanroommc/groovyscript/command/GSCommand.java @@ -169,7 +169,7 @@ public String getName() { @Override @NotNull public List getAliases() { - return Arrays.asList("grs", "GroovyScript", "gs"); + return Arrays.asList("grs", "gs"); } @Override diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/vanilla/Command.java b/src/main/java/com/cleanroommc/groovyscript/compat/vanilla/Command.java new file mode 100644 index 000000000..356874bba --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/vanilla/Command.java @@ -0,0 +1,115 @@ +package com.cleanroommc.groovyscript.compat.vanilla; + +import com.cleanroommc.groovyscript.GroovyScript; +import com.cleanroommc.groovyscript.api.GroovyBlacklist; +import com.cleanroommc.groovyscript.api.GroovyLog; +import com.cleanroommc.groovyscript.api.IScriptReloadable; +import com.cleanroommc.groovyscript.command.SimpleCommand; +import com.cleanroommc.groovyscript.core.mixin.CommandHandlerAccessor; +import com.cleanroommc.groovyscript.registry.AbstractReloadableStorage; +import com.cleanroommc.groovyscript.registry.NamedRegistry; +import net.minecraft.command.CommandHandler; +import net.minecraft.command.ICommand; +import net.minecraft.server.MinecraftServer; +import net.minecraftforge.client.ClientCommandHandler; +import net.minecraftforge.fml.common.FMLCommonHandler; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.function.Consumer; + +public class Command extends NamedRegistry implements IScriptReloadable { + + private final List serverCommands = new ArrayList<>(); + private final AbstractReloadableStorage serverReloadableCommands = new AbstractReloadableStorage<>(); + private final AbstractReloadableStorage clientReloadableCommands = new AbstractReloadableStorage<>(); + private boolean serverStarted = false; + + public void registerCommand(ICommand command) { + if (GroovyScript.getSandbox().isRunning() && GroovyScript.getSandbox().getCurrentLoader().isReloadable()) { + this.serverReloadableCommands.addScripted(command); + } else { + this.serverCommands.add(command); + } + if (this.serverStarted) { + forServer(commandHandler -> registerCommand(commandHandler, command)); + } + } + + public void registerClientCommand(ICommand command) { + if (FMLCommonHandler.instance().getSide().isServer()) return; + + if (registerCommand(ClientCommandHandler.instance, command) && GroovyScript.getSandbox().isRunning() && GroovyScript.getSandbox().getCurrentLoader().isReloadable()) { + this.clientReloadableCommands.addScripted(command); + } + } + + public void registerCommand(String name, String usage, SimpleCommand.ICommand command) { + registerCommand(new SimpleCommand(name, usage, command)); + } + + public void registerCommand(String name, SimpleCommand.ICommand command) { + registerCommand(new SimpleCommand(name, "/" + name, command)); + } + + public void registerClientCommand(String name, String usage, SimpleCommand.ICommand command) { + registerClientCommand(new SimpleCommand(name, usage, command)); + } + + public void registerClientCommand(String name, SimpleCommand.ICommand command) { + registerClientCommand(new SimpleCommand(name, "/" + name, command)); + } + + public boolean registerCommand(CommandHandler handler, ICommand command) { + if (handler.getCommands().containsKey(command.getName())) { + GroovyLog.get().error("Error registering command '/{}', because a command with that name already exists", command.getName()); + return false; + } + for (String alias : command.getAliases()) { + if (handler.getCommands().containsKey(alias)) { + GroovyLog.get().error("Error registering command '/{}', because a command for the alias '/{}' already exists", command.getName(), alias); + return false; + } + } + handler.registerCommand(command); + return true; + } + + @GroovyBlacklist + public void removeCommand(CommandHandler commandHandler, ICommand command) { + Set commands = ((CommandHandlerAccessor) commandHandler).getCommandSet(); + if (commands.remove(command)) { + commandHandler.getCommands().entrySet().removeIf(entry -> Objects.equals(command, entry.getValue())); + } + } + + @GroovyBlacklist + public void onStartServer(MinecraftServer server) { + this.serverStarted = true; + CommandHandler commandHandler = (CommandHandler) server.getCommandManager(); + for (ICommand command : this.serverCommands) { + registerCommand(commandHandler, command); + } + for (ICommand command : this.serverReloadableCommands.getScriptedRecipes()) { + registerCommand(commandHandler, command); + } + } + + @GroovyBlacklist + public void onReload() { + this.clientReloadableCommands.removeScripted().forEach(c -> removeCommand(ClientCommandHandler.instance, c)); + forServer(commandHandler -> this.serverReloadableCommands.removeScripted().forEach(c -> removeCommand(commandHandler, c))); + } + + @Override + public void afterScriptLoad() {} + + private void forServer(Consumer consumer) { + MinecraftServer server = FMLCommonHandler.instance().getMinecraftServerInstance(); + if (server != null) { + consumer.accept((CommandHandler) server.getCommandManager()); + } + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/vanilla/CommandSenderExpansion.java b/src/main/java/com/cleanroommc/groovyscript/compat/vanilla/CommandSenderExpansion.java new file mode 100644 index 000000000..8e1c9ceb1 --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/compat/vanilla/CommandSenderExpansion.java @@ -0,0 +1,16 @@ +package com.cleanroommc.groovyscript.compat.vanilla; + +import net.minecraft.command.ICommandSender; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.text.TextComponentString; + +public class CommandSenderExpansion { + + public static boolean isPlayer(ICommandSender commandSender) { + return commandSender instanceof EntityPlayer; + } + + public static void sendMessage(ICommandSender commandSender, String msg) { + commandSender.sendMessage(new TextComponentString(msg)); + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/compat/vanilla/VanillaModule.java b/src/main/java/com/cleanroommc/groovyscript/compat/vanilla/VanillaModule.java index 08edb6574..a698d2029 100644 --- a/src/main/java/com/cleanroommc/groovyscript/compat/vanilla/VanillaModule.java +++ b/src/main/java/com/cleanroommc/groovyscript/compat/vanilla/VanillaModule.java @@ -8,6 +8,7 @@ import com.cleanroommc.groovyscript.compat.loot.Loot; import com.cleanroommc.groovyscript.compat.mods.GroovyPropertyContainer; import com.cleanroommc.groovyscript.sandbox.expand.ExpansionHelper; +import net.minecraft.command.ICommandSender; import net.minecraft.item.ItemStack; import java.util.Collection; @@ -25,6 +26,7 @@ public class VanillaModule extends GroovyPropertyContainer implements IScriptRel public static final Content content = new Content(); public static final Rarity rarity = new Rarity(); public static final InWorldCrafting inWorldCrafting = new InWorldCrafting(); + public static final Command command = new Command(); public static void initializeBinding() { GroovyScript.getSandbox().registerBinding(crafting); @@ -35,8 +37,10 @@ public static void initializeBinding() { GroovyScript.getSandbox().registerBinding(content); GroovyScript.getSandbox().registerBinding(rarity); GroovyScript.getSandbox().registerBinding(inWorldCrafting); + GroovyScript.getSandbox().registerBinding(command); ExpansionHelper.mixinClass(ItemStack.class, ItemStackExpansion.class); + ExpansionHelper.mixinClass(ICommandSender.class, CommandSenderExpansion.class); } @Override @@ -49,6 +53,7 @@ public void onReload() { rarity.onReload(); player.onReload(); inWorldCrafting.onReload(); + command.onReload(); } @Override diff --git a/src/main/java/com/cleanroommc/groovyscript/core/mixin/CommandHandlerAccessor.java b/src/main/java/com/cleanroommc/groovyscript/core/mixin/CommandHandlerAccessor.java new file mode 100644 index 000000000..eaf857b2a --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/core/mixin/CommandHandlerAccessor.java @@ -0,0 +1,15 @@ +package com.cleanroommc.groovyscript.core.mixin; + +import net.minecraft.command.CommandHandler; +import net.minecraft.command.ICommand; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Set; + +@Mixin(CommandHandler.class) +public interface CommandHandlerAccessor { + + @Accessor + Set getCommandSet(); +} diff --git a/src/main/java/com/cleanroommc/groovyscript/helper/ingredient/ItemsIngredient.java b/src/main/java/com/cleanroommc/groovyscript/helper/ingredient/ItemsIngredient.java index b99e6bdf5..136d87753 100644 --- a/src/main/java/com/cleanroommc/groovyscript/helper/ingredient/ItemsIngredient.java +++ b/src/main/java/com/cleanroommc/groovyscript/helper/ingredient/ItemsIngredient.java @@ -45,9 +45,9 @@ public Ingredient toMcIngredient() { @Override public ItemStack[] getMatchingStacks() { - ItemStack[] stacks = itemStacks.toArray(new ItemStack[0]); + ItemStack[] stacks = new ItemStack[itemStacks.size()]; for (int i = 0; i < stacks.length; i++) { - ItemStack stack = stacks[i].copy(); + ItemStack stack = itemStacks.get(i).copy(); stack.setCount(getAmount()); stacks[i] = stack; } diff --git a/src/main/java/com/cleanroommc/groovyscript/helper/ingredient/OreDictIngredient.java b/src/main/java/com/cleanroommc/groovyscript/helper/ingredient/OreDictIngredient.java index 0ceb3b1ea..86a5a1bbf 100644 --- a/src/main/java/com/cleanroommc/groovyscript/helper/ingredient/OreDictIngredient.java +++ b/src/main/java/com/cleanroommc/groovyscript/helper/ingredient/OreDictIngredient.java @@ -1,13 +1,12 @@ package com.cleanroommc.groovyscript.helper.ingredient; -import com.cleanroommc.groovyscript.api.GroovyBlacklist; import com.cleanroommc.groovyscript.compat.vanilla.VanillaModule; -import com.google.common.collect.Iterators; import net.minecraft.item.ItemStack; import net.minecraft.item.crafting.Ingredient; import net.minecraftforge.oredict.OreDictionary; import org.jetbrains.annotations.NotNull; +import java.util.Arrays; import java.util.Iterator; import java.util.List; @@ -61,28 +60,24 @@ public Ingredient toMcIngredient() { return Ingredient.fromStacks(getMatchingStacks()); } - @GroovyBlacklist - private List prepareItemStacks() { + @Override + public ItemStack[] getMatchingStacks() { List stacks = OreDictionary.getOres(this.oreDict); + ItemStack[] copies = new ItemStack[stacks.size()]; for (int i = 0; i < stacks.size(); i++) { ItemStack stack = stacks.get(i).copy(); stack.setCount(getAmount()); - stacks.set(i, stack); + copies[i] = stack; } - return stacks; - } - - @Override - public ItemStack[] getMatchingStacks() { - return prepareItemStacks().toArray(new ItemStack[0]); + return copies; } public ItemStack getFirst() { - return prepareItemStacks().get(0); + return getMatchingStacks()[0]; } public ItemStack getAt(int index) { - return prepareItemStacks().get(index); + return getMatchingStacks()[index]; } @Override @@ -129,6 +124,6 @@ public void remove(Iterable itemStacks) { @NotNull @Override public Iterator iterator() { - return Iterators.unmodifiableIterator(prepareItemStacks().listIterator()); + return Arrays.asList(getMatchingStacks()).listIterator(); } } diff --git a/src/main/resources/mixin.groovyscript.json b/src/main/resources/mixin.groovyscript.json index 79cfe2a83..6b3d2a423 100644 --- a/src/main/resources/mixin.groovyscript.json +++ b/src/main/resources/mixin.groovyscript.json @@ -5,6 +5,7 @@ "minVersion": "0.8", "compatibilityLevel": "JAVA_8", "mixins": [ + "CommandHandlerAccessor", "CreativeTabsAccessor", "EntityAccessor", "EntityItemMixin",