/* * CraftBook Copyright (C) 2010-2017 sk89q <http://www.sk89q.com> * CraftBook Copyright (C) 2011-2017 me4502 <http://www.me4502.com> * CraftBook Copyright (C) Contributors * * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public * License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program. If not, * see <http://www.gnu.org/licenses/>. */ package com.sk89q.craftbook.sponge.mechanics.variable; import com.google.common.collect.Maps; import com.google.common.reflect.TypeToken; import com.google.inject.Inject; import com.me4502.modularframework.module.Module; import com.me4502.modularframework.module.guice.ModuleConfiguration; import com.sk89q.craftbook.core.util.ConfigValue; import com.sk89q.craftbook.core.util.CraftBookException; import com.sk89q.craftbook.core.util.documentation.DocumentationProvider; import com.sk89q.craftbook.sponge.CraftBookPlugin; import com.sk89q.craftbook.sponge.mechanics.types.SpongeMechanic; import com.sk89q.craftbook.sponge.mechanics.variable.command.GetVariableCommand; import com.sk89q.craftbook.sponge.mechanics.variable.command.RemoveVariableCommand; import com.sk89q.craftbook.sponge.mechanics.variable.command.SetVariableCommand; import com.sk89q.craftbook.sponge.util.TextUtil; import com.sk89q.craftbook.sponge.util.type.TypeTokens; import ninja.leaping.configurate.ConfigurationNode; import ninja.leaping.configurate.objectmapping.ObjectMappingException; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; import org.spongepowered.api.Sponge; import org.spongepowered.api.command.CommandMapping; import org.spongepowered.api.command.args.GenericArguments; import org.spongepowered.api.command.spec.CommandSpec; import org.spongepowered.api.entity.living.player.Player; import org.spongepowered.api.event.Listener; import org.spongepowered.api.event.command.SendCommandEvent; import org.spongepowered.api.event.message.MessageChannelEvent; import org.spongepowered.api.text.LiteralText; import org.spongepowered.api.text.Text; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.annotation.Nullable; @Module(id = "variables", name = "Variables", onEnable="onInitialize", onDisable="onDisable") public class Variables extends SpongeMechanic implements DocumentationProvider { private static final Pattern VARIABLE_PATTERN = Pattern.compile("%(?:([a-zA-Z0-9]+)\\|)*([a-zA-Z0-9]+)%"); public static Variables instance; private Map<String, Map<String, String>> variableStore; @Inject @ModuleConfiguration public ConfigurationNode config; private ConfigValue<Boolean> defaultToGlobal = new ConfigValue<>("default-to-global", "If no namespace is provided, default to global. " + "Otherwise personal namespace", true); private CommandMapping variableCommandMapping; @Override public void onInitialize() throws CraftBookException { super.onInitialize(); instance = this; try { variableStore = config.getNode("variables").getValue(new TypeTokens.VariableTypeToken(), Maps.newHashMap()); } catch (ObjectMappingException e) { CraftBookPlugin.spongeInst().getLogger().warn("Failed to read variables! Resetting..", e); variableStore = new HashMap<>(); } defaultToGlobal.load(config); CommandSpec setVariable = CommandSpec.builder() .description(Text.of("Set the value of a variable")) .arguments(GenericArguments.string(Text.of("key")), GenericArguments.string(Text.of("value"))) .executor(new SetVariableCommand(this, false)) .build(); CommandSpec setGlobalVariable = CommandSpec.builder() .description(Text.of("Set the value of a global variable")) .arguments(GenericArguments.string(Text.of("key")), GenericArguments.string(Text.of("value"))) .executor(new SetVariableCommand(this, true)) .build(); CommandSpec getVariable = CommandSpec.builder() .description(Text.of("Get the value of a variable")) .arguments(GenericArguments.string(Text.of("key"))) .executor(new GetVariableCommand(this, false)) .build(); CommandSpec getGlobalVariable = CommandSpec.builder() .description(Text.of("Get the value of a global variable")) .arguments(GenericArguments.string(Text.of("key"))) .executor(new GetVariableCommand(this, true)) .build(); CommandSpec removeVariable = CommandSpec.builder() .description(Text.of("Removes a variable")) .arguments(GenericArguments.string(Text.of("key"))) .executor(new RemoveVariableCommand(this, false)) .build(); CommandSpec removeGlobalVariable = CommandSpec.builder() .description(Text.of("Removes a global variable")) .arguments(GenericArguments.string(Text.of("key"))) .executor(new RemoveVariableCommand(this, true)) .build(); CommandSpec variableCommand = CommandSpec.builder() .description(Text.of("Base Variable command")) .child(setVariable, "set", "def", "define") .child(setGlobalVariable, "setglobal", "defglobal", "defineglobal") .child(getVariable, "get") .child(getGlobalVariable, "getglobal") .child(removeVariable, "rm", "del", "remove") .child(removeGlobalVariable, "rmglobal", "delglobal", "removeglobal") .build(); variableCommandMapping = Sponge.getGame().getCommandManager().register(CraftBookPlugin.spongeInst(), variableCommand, "var", "variable", "variables").orElse(null); } @Override public void onDisable() { super.onDisable(); try { config.getNode("variables").setValue(new TypeToken<Map<String, Map<String, String>>>() {}, variableStore); } catch (ObjectMappingException e) { e.printStackTrace(); } if (variableCommandMapping != null) { Sponge.getCommandManager().removeMapping(variableCommandMapping); } } public void addVariable(String namespace, String key, String value) { Map<String, String> map = variableStore.getOrDefault(namespace, new HashMap<>()); map.put(key, value); variableStore.put(namespace, map); } public String getVariable(String namespace, String key) { Map<String, String> map = variableStore.getOrDefault(namespace, new HashMap<>()); return map.getOrDefault(key, null); } public void removeVariable(String namespace, String key) { Map<String, String> map = variableStore.getOrDefault(namespace, new HashMap<>()); map.remove(key); if(!map.isEmpty()) variableStore.put(namespace, map); else variableStore.remove(namespace); } public String parseVariables(String line, @Nullable Player player) { for(Pair<String, String> possibleVariable : getPossibleVariables(line)) { String namespace = possibleVariable.getLeft(); String name = possibleVariable.getRight(); boolean explicit = true; if (namespace == null) { if (defaultToGlobal.getValue()) { namespace = "global"; if(player != null && getVariable(player.getUniqueId().toString(), name) != null) { namespace = player.getUniqueId().toString(); } } else { if (player != null) { namespace = player.getUniqueId().toString(); } else { continue; } } explicit = false; } String variable = getVariable(namespace, name); if(variable != null) { String variableText = (explicit ? (namespace + '|') : "") + name; line = line.replace('%' + variableText + '%', getVariable(namespace, name)); } } return line; } @Listener public void onCommandSend(SendCommandEvent event) { Player source = null; if(event.getCause().first(Player.class).isPresent()) source = event.getCause().first(Player.class).get(); if(!event.getArguments().isEmpty()) event.setArguments(parseVariables(event.getArguments(), source)); event.setCommand(parseVariables(event.getCommand(), source)); } @Listener public void onPlayerChat(MessageChannelEvent event) { event.setMessage(TextUtil.transform(event.getMessage(), old -> { if (old instanceof LiteralText) { LiteralText literal = (LiteralText) old; return literal.toBuilder().content(parseVariables(literal.getContent(), event.getCause().first(Player.class).orElse(null))).toText(); } return old; })); } public static Set<Pair<String, String>> getPossibleVariables(String line) { Set<Pair<String, String>> variables = new HashSet<>(); if(!line.contains("%")) return variables; Matcher matcher = VARIABLE_PATTERN.matcher(line); while (matcher.find()) { String namespace = matcher.group(1); String key = matcher.group(2); variables.add(new ImmutablePair<>(namespace, key)); } return variables; } public static boolean isValidVariableKey(String key) { return !key.contains("|") && !key.contains("%"); } @Override public String getPath() { return "mechanics/variables"; } @Override public ConfigValue<?>[] getConfigurationNodes() { return new ConfigValue[] { defaultToGlobal }; } }