Error with custom CommandSource

I’m trying to write a scripting engine that is primarily based on in-game commands. I wrote a custom wrapper implementation class called ScriptSource that implements the interface CommandSource, but when I pass this to a vanilla command, I get an error like this:

[15:56:03 ERROR] [Sponge]: Error occurred while executing command 'effect Zephling minecraft:instant_health 6 6' for source me.zephlon.core.plugin.scripts.ScriptSource@c5: me.zephlon.core.plugin.scripts.ScriptSource cannot be cast to org.spongepowered.common.interfaces.IMixinCommandSource
java.lang.ClassCastException: me.zephlon.core.plugin.scripts.ScriptSource cannot be cast to org.spongepowered.common.interfaces.IMixinCommandSource
        at org.spongepowered.common.command.MinecraftCommandWrapper.process(MinecraftCommandWrapper.java:103) ~[MinecraftCommandWrapper.class:1.10.2-5.0.0-BETA-91]
        at org.spongepowered.api.command.dispatcher.SimpleDispatcher.process(SimpleDispatcher.java:333) ~[SimpleDispatcher.class:1.10.2-5.0.0-BETA-91]
        at org.spongepowered.common.command.SpongeCommandManager.process(SpongeCommandManager.java:264) [SpongeCommandManager.class:1.10.2-5.0.0-BETA-91]
        at me.zephlon.core.plugin.scripts.Script.execute(Script.java:91) [Script.class:?]
        at me.zephlon.core.api.commands.CommandWrapper.execute(CommandWrapper.java:40) [CommandWrapper.class:?]
        at org.spongepowered.api.command.spec.CommandSpec.process(CommandSpec.java:332) [CommandSpec.class:1.10.2-5.0.0-BETA-91]
        at org.spongepowered.api.command.dispatcher.SimpleDispatcher.process(SimpleDispatcher.java:333) [SimpleDispatcher.class:1.10.2-5.0.0-BETA-91]
        at org.spongepowered.common.command.SpongeCommandManager.process(SpongeCommandManager.java:264) [SpongeCommandManager.class:1.10.2-5.0.0-BETA-91]
        at net.minecraft.command.ServerCommandManager.func_71556_a(SourceFile:82) [bd.class:?]
        at net.minecraft.network.NetHandlerPlayServer.func_147361_d(SourceFile:825) [me.class:?]
        at net.minecraft.network.NetHandlerPlayServer.func_147354_a(SourceFile:812) [me.class:?]
        at net.minecraft.network.play.client.CPacketChatMessage.func_148833_a(SourceFile:37) [im.class:?]
        at net.minecraft.network.play.client.CPacketChatMessage.func_148833_a(SourceFile:9) [im.class:?]
        at org.spongepowered.common.network.PacketUtil.onProcessPacket(PacketUtil.java:119) [PacketUtil.class:1.10.2-5.0.0-BETA-91]
        at net.minecraft.network.PacketThreadUtil$1.redirect$onProcessPacket$0(SourceFile:39) [fl$1.class:?]
        at net.minecraft.network.PacketThreadUtil$1.run(SourceFile:13) [fl$1.class:?]
        at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source) [?:1.8.0_92]
        at java.util.concurrent.FutureTask.run(Unknown Source) [?:1.8.0_92]
        at net.minecraft.util.Util.func_181617_a(SourceFile:45) [h.class:?]
        at net.minecraft.server.MinecraftServer.func_71190_q(SourceFile:134) [MinecraftServer.class:?]
        at net.minecraft.server.dedicated.DedicatedServer.func_71190_q(SourceFile:338) [ld.class:?]
        at net.minecraft.server.MinecraftServer.func_71217_p(SourceFile:554) [MinecraftServer.class:?]
        at net.minecraft.server.MinecraftServer.run(SourceFile:458) [MinecraftServer.class:?]
        at java.lang.Thread.run(Unknown Source) [?:1.8.0_92]

The ScriptSource class (without all the implemented methods and additional features) looks like the following. The rest of the wrapped methods are done in a way similar to getName().

public class ScriptSource implements CommandSource {

    private final CommandSource src;
    ...

    public ScriptSource(CommandSource src) {
        this.src = src;
        ...
    }
    
    @Override
    public String getName() {
        return src.getName();
    }
    ...
}

How would I fix this error? Or is there a completely different way I should be creating a custom CommandSource?

When you implemented the CommandSource are you sure you imported the right one? The correct import should be org.spongepowered.api.command.CommandSource

Yes: import org.spongepowered.api.command.CommandSource;

Could you post your class that includes your execute method

Script.java

package me.zephlon.core.plugin.scripts;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import me.zephlon.core.api.ZPlugin;
import me.zephlon.core.api.commands.CommandWrapper;
import me.zephlon.core.api.messenger.Messenger;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.command.CommandException;
import org.spongepowered.api.command.CommandResult;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.command.args.CommandContext;
import org.spongepowered.api.command.args.CommandElement;
import org.spongepowered.api.text.Text;
import org.spongepowered.api.text.serializer.TextSerializers;

/**
 *
 * @author Zephlon
 */
public class Script extends CommandWrapper {

    private final Map<String, String> options;
    private final List<CommandElement> args;
    private final List<String> commands;

    public Script(ZPlugin plugin, File file, List<String> aliases, Map<String, String> options, List<String> argumentDefinitions, List<String> commands) throws ScriptException {
        super(plugin);

        this.options = Collections.unmodifiableMap(options);

        setAliases(aliases.toArray(new String[aliases.size()]));

        if (options.containsKey("description")) {
            final String s = options.get("description");
            Text description = TextSerializers.FORMATTING_CODE.deserialize(s);
            builder.description(description);
        }

        if (options.containsKey("help")) {
            final String s = options.get("help");
            Text extendedDescription = TextSerializers.FORMATTING_CODE.deserialize(s);
            builder.extendedDescription(extendedDescription);
        }

        if (options.containsKey("permission") && options.get("permission").length() > 0) {
            builder.permission(options.get("permission"));
        }

        this.args = new ArrayList();

        for (String argumentDefinition : argumentDefinitions) {
            args.add(ScriptElementParser.parse(argumentDefinition));
        }

        builder.arguments(args.toArray(new CommandElement[args.size()]));
        this.commands = commands;
    }

    @Override
    public CommandResult execute(CommandSource commandSource, Messenger msr, CommandContext context) throws CommandException {
        ScriptSource src;

        if (commandSource instanceof ScriptSource) {
            src = (ScriptSource) commandSource;
        } else {
            src = new ScriptSource(commandSource);

            if (src.isPlayer()) {
                src.var("src", src.getName());
            }
        }

        for (CommandElement e : this.args) {
            String key = e.getUntranslatedKey();
            Optional<String> value = context.<String>getOne(key);

            if (value.isPresent()) {
                src.var(key, src.eval(value.get()));
            }
        }

        for (String command : commands) {
            if (command.startsWith("/")) {
                Sponge.getCommandManager().process(src, src.eval(command.substring(1)));
            } else {
                src.sendMessage(TextSerializers.FORMATTING_CODE.deserialize(src.eval(command)));
            }
        }

        return CommandResult.success();
    }

    /**
     * @return the options
     */
    public Map<String, String> getOptions() {
        return options;
    }

    public boolean hasOption(String key) {
        return options.containsKey(key);
    }

    public String getOption(String key) {
        return options.get(key);
    }

    public String getOption(String key, String defaultValue) {
        return options.getOrDefault(key, defaultValue);
    }

}

ScriptSource.java

package me.zephlon.core.plugin.scripts;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import me.zephlon.core.plugin.ZephyrCore;
import org.spongepowered.api.command.CommandException;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.service.context.Context;
import org.spongepowered.api.service.permission.Subject;
import org.spongepowered.api.service.permission.SubjectCollection;
import org.spongepowered.api.service.permission.SubjectData;
import org.spongepowered.api.text.Text;
import org.spongepowered.api.text.TextElement;
import org.spongepowered.api.text.TextTemplate;
import org.spongepowered.api.text.channel.MessageChannel;
import org.spongepowered.api.util.Tristate;

/**
 *
 * @author Zephlon
 */
public class ScriptSource implements CommandSource {

    private final CommandSource src;

    private final Map<String, String> variables;

    private boolean echo;
    private boolean passthrough;
    
    public ScriptSource(CommandSource src) {
        this.src = src;

        this.variables = new HashMap();
        this.passthrough = ZephyrCore.plugin().getDefaultConfig().getNode("scripting", "defaultPassthrough").getBoolean(false);
        this.echo = true;
    }

    public static final ScriptSource[] wrap(CommandSource... commandSources) {
        return Arrays.stream(commandSources).map((CommandSource t) -> new ScriptSource(t)).toArray(ScriptSource[]::new);
    }

    //<editor-fold defaultstate="collapsed" desc=" CommandSource Implementation ">
    /**
     * Returns the identifier associated with this Contextual. Not guaranteed to
     * be human-readable.
     *
     * @return The unique identifier for this subject
     */
    @Override
    public String getIdentifier() {
        return src.getIdentifier();
    }

    /**
     * Calculate active contexts, using the {@link ContextCalculator}s for the
     * service.
     *
     * <p>
     * The result of these calculations may be cached.</p>
     *
     * @return An immutable set of active contexts
     */
    @Override
    public Set<Context> getActiveContexts() {
        return src.getActiveContexts();
    }

    /**
     * Sends the formatted text message to source when possible. If text
     * formatting is not supported in the implementation it will be displayed as
     * plain text.
     *
     * @param message The message
     */
    @Override
    public void sendMessage(Text message) {
        if (echo) {
            src.sendMessage(message);
        }
    }

    /**
     * Sends the formatted text message(s) to source when possible. If text
     * formatting is not supported in the implementation it will be displayed as
     * plain text.
     *
     * @param messages The message(s)
     */
    @Override
    public void sendMessages(Text... messages) {
        if (echo) {
            src.sendMessages(messages);
        }
    }

    /**
     * Sends the formatted text message(s) to source when possible. If text
     * formatting is not supported in the implementation it will be displayed as
     * plain text.
     *
     * @param messages The messages
     */
    @Override
    public void sendMessages(Iterable<Text> messages) {
        if (echo) {
            src.sendMessages(messages);
        }
    }

    /**
     * Sends the result of the specified {@link TextTemplate} with the specified
     * parameters to the receiver.
     *
     * @param template TextTemplate to apply
     * @param params Parameters to apply to template
     */
    @Override
    public void sendMessage(TextTemplate template, Map<String, TextElement> params) {
        if (echo) {
            src.sendMessage(template, params);
        }
    }

    /**
     * Sends the result of the specified {@link TextTemplate} with an empty
     * parameter map.
     *
     * @param template TextTemplate to apply
     */
    @Override
    public void sendMessage(TextTemplate template) {
        if (echo) {
            src.sendMessage(template);
        }
    }

    /**
     * Return the message channel that messages from this source should be sent
     * to.
     *
     * @return This source's active message channel
     */
    @Override
    public MessageChannel getMessageChannel() {
        if (echo) {
            return src.getMessageChannel();
        } else {
            return MessageChannel.TO_NONE;
        }
    }

    /**
     * Set the message channel that messages sent by this source should be sent
     * to.
     *
     * @param channel The message channel to send messages to
     */
    @Override
    public void setMessageChannel(MessageChannel channel) {
        src.setMessageChannel(channel);
    }

    /**
     * If this subject represents an actual user currently connected, this
     * method returns this user. This user may in fact be the same as this
     * subject. Some subjects may never directly map to a command source, while
     * others may temporarily not have an accessible command source
     *
     * @return an optional active command source
     */
    @Override
    public Optional<CommandSource> getCommandSource() {
        return src.getCommandSource();
    }

    /**
     * Returns the subject collection this subject is a member of.
     *
     * @return The appropriate collection
     */
    @Override
    public SubjectCollection getContainingCollection() {
        return src.getContainingCollection();
    }

    /**
     * The container for permissions data that *may* be persisted if the service
     * provider supports it.
     *
     * @return The container for permissions data this subject uses
     */
    @Override
    public SubjectData getSubjectData() {
        return src.getSubjectData();
    }

    /**
     * Returns container for subject data that is guaranteed to be transient
     * (only lasting for the duration of the subject's session, not persisted).
     * This might return the same object as {@link #getSubjectData()} if the
     * provider for this service does not implement persistence for permissions
     * data
     *
     * @return Transient data storage for this subject
     */
    @Override
    public SubjectData getTransientSubjectData() {
        return src.getTransientSubjectData();
    }

    /**
     * Test whether the subject is permitted to perform an action given as the
     * given permission string.
     *
     * @param contexts The set of contexts that represents the subject's current
     * environment
     * @param permission The permission string
     * @return True if permission is granted
     */
    @Override
    public boolean hasPermission(Set<Context> contexts, String permission) {
        if (passthrough()) {
            return src.hasPermission(contexts, permission);
        } else {
            return true;
        }
    }

    /**
     * Test whether the subject is permitted to perform an action given as the
     * given permission string. This must return the same value as
     * {@link #hasPermission(Set, String)} using {@link #getActiveContexts()}.
     *
     * @param permission The permission string
     * @return True if permission is granted
     */
    @Override
    public boolean hasPermission(String permission) {
        if (passthrough()) {
            return src.hasPermission(permission);
        } else {
            return true;
        }
    }

    /**
     * Returns the calculated value set for a given permission.
     *
     * @param contexts The contexts to check for permissions in
     * @param permission The permission to check
     * @return The tristate true/false/unset value for permissions
     */
    @Override
    public Tristate getPermissionValue(Set<Context> contexts, String permission) {
        if (passthrough()) {
            return src.getPermissionValue(contexts, permission);
        } else {
            return Tristate.TRUE;

        }
    }

    /**
     * Check if this subject is a child of the given parent in the subject's
     * current context, traversing inheritance. This must return the same value
     * as {@link #hasPermission(Set, String)} using
     * {@link #getActiveContexts()}.
     *
     * @param parent The parent to check for inheritance
     * @return Whether this is a child of the given parent
     */
    @Override
    public boolean isChildOf(Subject parent) {
        return src.isChildOf(parent);
    }

    /**
     * Check if this subject is a child of the given parent in the given context
     * combination, traversing inheritance.
     *
     * @param contexts The context combination to check in
     * @param parent The parent to check for inheritance
     * @return Whether this is a child of the given parent
     */
    @Override
    public boolean isChildOf(Set<Context> contexts, Subject parent) {
        return src.isChildOf(contexts, parent);
    }

    /**
     * Return all parents that this group has in its current context
     * combination. This must include inherited values if the permissions
     * service supports inheritance. This must return the same value as
     * {@link #hasPermission(Set, String)} using {@link #getActiveContexts()}.
     *
     * @return An immutable list of parents
     */
    @Override
    public List<Subject> getParents() {
        return src.getParents();
    }

    /**
     * Return all parents that this group has. This must include inherited
     * values if the permissions service supports inheritance.
     *
     * @param contexts The set of contexts that represents the subject's current
     * environment
     * @return An immutable list of parents
     */
    @Override
    public List<Subject> getParents(Set<Context> contexts) {
        return src.getParents(contexts);
    }

    /**
     * Gets the name identifying this command source.
     *
     * @return The name of this command source
     */
    @Override
    public String getName() {
        return src.getName();
    }

    /**
     * Gets the locale used by this command source. If this
     * {@link CommandSource} does have a {@link Locale} configured or does not
     * support configuring a {@link Locale}, {@link Locales#DEFAULT} is used.
     *
     * @return The locale used by this command source
     */
    @Override
    public Locale getLocale() {
        return src.getLocale();
    }
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc=" Equals ">
    @Override
    public boolean equals(Object o) {
        Object target = o;
        if (o instanceof ScriptSource) {
            target = ((ScriptSource) o).src;
        }
        return this.src.equals(target);
    }

    @Override
    public int hashCode() {
        return this.src.hashCode();
    }
    //</editor-fold>

    public boolean isPlayer() {
        return (src instanceof Player);
    }

    public Optional<String> var(String key) {
        return Optional.ofNullable(variables.get(key));
    }

    public void var(String key, String value) {
        if (key != null && value != null) {
            variables.put(key, value);
        }
    }

    public String eval(String value) throws UndefinedVariableException {
        StringBuffer resultString = new StringBuffer();
        Pattern regex = Pattern.compile("<(\\w+)>");
        Matcher m = regex.matcher(value);

        while (m.find()) {
            Optional<String> v = var(m.group(1));

            if (v.isPresent()) {
                m.appendReplacement(resultString, v.get());
            } else {
                throw new UndefinedVariableException(m.group(1));
            }
        }
        m.appendTail(resultString);

        return resultString.toString();
    }

    /**
     * @return the echo
     */
    public boolean echo() {
        return echo;
    }

    /**
     * @param echo the echo to set
     */
    public void echo(boolean echo) {
        this.echo = echo;
    }

    /**
     * @return the passthrough
     */
    public boolean passthrough() {
        return passthrough;
    }

    /**
     * @param passthrough the passthrough to set
     */
    public void passthrough(boolean passthrough) {
        this.passthrough = passthrough;
    }

    public static class UndefinedVariableException extends CommandException {

        private UndefinedVariableException(String key) {
            super(txt(key));
        }

        private static Text txt(String key) {
            if (key.equalsIgnoreCase("src")) {
                return ZephyrCore.plugin().getMessageConfig().get("error.playerSender");
            } else {
                return ZephyrCore.plugin().getMessageConfig().get("error.undefinedvar", key);
            }
        }
    }

}

Looks like this is an error with the implementation of the vanilla minecraft command wrappers.

I’m just creating a small PR now (based on this comment) to allow custom command sources.

OK, I’ve created the PR - once it’s pulled your issue should be fixed.