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);
}
}
}
}