├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── pom.xml └── src ├── main ├── java │ └── me │ │ └── konsolas │ │ └── conditionalcommands │ │ ├── ConditionalCommands.java │ │ ├── Expression.java │ │ ├── Placeholders.java │ │ └── placeholders │ │ ├── AbstractParameteredPlaceholder.java │ │ ├── AbstractStandardPlaceholder.java │ │ ├── Placeholder.java │ │ ├── PlaceholderAACVL.java │ │ ├── PlaceholderChance.java │ │ ├── PlaceholderCooldown.java │ │ ├── PlaceholderPerm.java │ │ ├── PlaceholderPermCount.java │ │ ├── PlaceholderPing.java │ │ ├── PlaceholderPlayerCount.java │ │ ├── PlaceholderTPS.java │ │ ├── PlaceholderTimeOnline.java │ │ └── PlaceholderUptime.java └── resources │ ├── config.yml │ └── plugin.yml └── test └── java └── me └── konsolas └── conditionalcommands └── ExpressionTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | pom.xml.tag 3 | pom.xml.releaseBackup 4 | pom.xml.versionsBackup 5 | pom.xml.next 6 | release.properties 7 | dependency-reduced-pom.xml 8 | buildNumber.properties 9 | .idea/ 10 | *.iml 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | branches: 3 | only: 4 | - master 5 | jdk: 6 | - openjdk11 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Vincent Tang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/konsolas/ConditionalCommands.svg?branch=master)](https://travis-ci.org/konsolas/ConditionalCommands) 2 | # ConditionalCommands 3 | Only execute a command if a condition is met. 4 | 5 | ConditionalCommands is intended to be used when plugins have automatic commands that should only be executed if certain conditions are met. It is able to execute multiple commands, with customisable delays on each command. 6 | 7 | ## Usage 8 | ``` 9 | /cc unless do 10 | /cc if do 11 | /cc help 12 | /cc reload 13 | /cc cooldown 14 | ``` 15 | Alias: `/ccmd` 16 | 17 | ### Conditions 18 | Grammar of conditions: 19 | ``` 20 | ::={} 21 | ::={} 22 | ::=||() 23 | ::= 24 | ::=floating point number or integer 25 | ::='&' 26 | ::='|' 27 | ::='!' 28 | ::='>'|'='|'<' 29 | ``` 30 | As shown above, only numbers can be compared, and placeholders can only consist of numbers. In case of multiple comparison operators in a group, i.e. 3>=<2, only the first operator will be used. Comparisons cannot include spaces. Inequality may be checked with !(value=value). 31 | 32 | Examples: 33 | ``` 34 | /cc konsolas unless -ping->200 do kick konsolas 35 | /cc konsolas if (-ping-<300&-ping->100)&-tps->15.0 do msg konsolas Your ping is between 300 and 100, and the TPS is greater than 15. 36 | ``` 37 | 38 | ### Placeholders 39 | Placeholders are delimited by '-'. Since they're applied with a replace, errors will probably be detected during parsing if they are typed incorrectly. 40 | 41 | - ```ping``` - The latency of the tested player. 42 | - ```tps``` - Server TPS average over the last 2 seconds 43 | - ```time_online``` - Player's online time in milliseconds 44 | - ```uptime``` - Server uptime in ticks 45 | - ```player_count``` - Number of players on the server 46 | - ```perm:``` - 1.0 if the player has the permission, 0.0 otherwise. e.g. ```-perm:essentials.home-``` 47 | - ```perm_count:``` - Number of players online who have the permission, 0.0 otherwise. e.g. ```-perm_count:essentials.home-``` 48 | - ```aacvl:``` - AAC 1-4 violation level of the given check (internal name). e.g. ```-aacvl:speed-``` 49 | - ```chance:%``` - Will be 1.0 percentage% of the time. e.g. ```-chance:34.5%-``` 50 | - ```cooldown:``` - Returns time in seconds since the last execution of the ```/cc cooldown ``` command. Returns ```43200``` if ```/cc cooldown``` has never been executed for the given ``````. Never returns a value < 0. 51 | e.g. ```/cc if -cooldown:some_key->59 do /0/ cc cooldown some_key /0/ broadcast sent up to once every 60 seconds``` 52 | Maximum supported cooldown is 12 Hours aka 43200 seconds. Cooldowns are not saved to file, so they reset on server reload/restart. 53 | Use for example the player placeholder inside aac's configuration as part of the arbitrarykey. 54 | 55 | ### Multi command / delayed commands 56 | In the 'do' clause of the statement, multiple commands can be executed at once, and selected commands can be delayed if desired. The command delimiter is ```//```, where the integer between ```/``` and ```/``` denotes the delay before the command should be executed in ticks. Here are some examples: 57 | 58 | ``` 59 | /cc konsolas if -aacvl:heuristics->0 do /1200/ ban konsolas 1 minute delayed ban for killaura 60 | /cc konsolas if -perm:some.permission-=1 do /0/ broadcast konsolas has some.permission! /0/ broadcast second broadcast! /20/ broadcast 1 second later! 61 | ``` 62 | 63 | ### Developer mode 64 | By default, ConditionalCommands will suppress details behind parsing errors, etc. to be more user friendly. It will also swallow exceptions generated by executing a command. This behaviour can be changed by changing the ```dev``` option in ```plugins/ConditionalCommands/config.yml``` to true. 65 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | me.konsolas 8 | ConditionalCommands 9 | 1.5-SNAPSHOT 10 | 11 | 12 | 13 | spigot-repo 14 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/ 15 | 16 | 17 | 18 | 19 | 20 | 21 | org.spigotmc 22 | spigot-api 23 | 1.11.2-R0.1-SNAPSHOT 24 | provided 25 | 26 | 27 | 28 | org.bukkit 29 | bukkit 30 | 1.11.2-R0.1-SNAPSHOT 31 | provided 32 | 33 | 34 | 35 | junit 36 | junit 37 | 4.12 38 | test 39 | 40 | 41 | 42 | 43 | clean package 44 | 45 | 46 | maven-compiler-plugin 47 | 3.3 48 | 49 | 1.7 50 | 1.7 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /src/main/java/me/konsolas/conditionalcommands/ConditionalCommands.java: -------------------------------------------------------------------------------- 1 | package me.konsolas.conditionalcommands; 2 | 3 | import me.konsolas.conditionalcommands.placeholders.PlaceholderCooldown; 4 | import org.bukkit.Bukkit; 5 | import org.bukkit.ChatColor; 6 | import org.bukkit.command.Command; 7 | import org.bukkit.command.CommandException; 8 | import org.bukkit.command.CommandSender; 9 | import org.bukkit.entity.Player; 10 | import org.bukkit.plugin.java.JavaPlugin; 11 | 12 | import java.util.regex.Matcher; 13 | import java.util.regex.Pattern; 14 | 15 | @SuppressWarnings("DuplicateExpressions") 16 | public class ConditionalCommands extends JavaPlugin { 17 | private static final Pattern SPLIT_PATTERN = Pattern.compile("/([0-9]*)/"); 18 | 19 | public void onEnable() { 20 | saveDefaultConfig(); 21 | 22 | getLogger().info("Initializing placeholders..."); 23 | for (Placeholders placeholder : Placeholders.values()) { 24 | placeholder.getPlaceholder().init(this); 25 | } 26 | getLogger().info("Ready."); 27 | } 28 | 29 | @Override 30 | public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { 31 | boolean player = (sender instanceof Player); 32 | 33 | if (args.length == 0 || (args.length > 2 && args.length < 5)) { 34 | sender.sendMessage((player ? ChatColor.GOLD : "") + "[ConditionalCommands] > Incorrect number of arguments."); 35 | sender.sendMessage((player ? ChatColor.GOLD : "") + "[ConditionalCommands] >" + (player ? ChatColor.GREEN : "") + " /cc help"); 36 | return false; 37 | } 38 | 39 | if (args.length == 1) { 40 | if (args[0].equalsIgnoreCase("help")) { 41 | sender.sendMessage((player ? ChatColor.GOLD : "") + "--------=ConditionalCommands=--------"); 42 | sender.sendMessage((player ? ChatColor.GREEN : "") + " /cc help"); 43 | sender.sendMessage((player ? ChatColor.GREEN : "") + " /cc reload"); 44 | sender.sendMessage((player ? ChatColor.GREEN : "") + " /cc cooldown "); 45 | sender.sendMessage((player ? ChatColor.GREEN : "") + " /cc unless \"" + (player ? ChatColor.LIGHT_PURPLE : "") + "condition" 46 | + (player ? ChatColor.GREEN : "") + "\" do \"" + (player ? ChatColor.LIGHT_PURPLE : "") + "command" + (player ? ChatColor.GREEN : "") + "\""); 47 | sender.sendMessage((player ? ChatColor.GREEN : "") + " /cc if \"" + (player ? ChatColor.LIGHT_PURPLE : "") + "condition" 48 | + (player ? ChatColor.GREEN : "") + "\" do \"" + (player ? ChatColor.LIGHT_PURPLE : "") + "command" + (player ? ChatColor.GREEN : "") + "\""); 49 | sender.sendMessage((player ? ChatColor.GRAY : "") + "e.g."); 50 | sender.sendMessage((player ? ChatColor.GREEN : "") + " /cc konsolas unless -ping->100|-tps-<10.0 do kick konsolas"); 51 | sender.sendMessage((player ? ChatColor.GRAY : "") + "Please note that conditions cannot include any spaces."); 52 | sender.sendMessage((player ? ChatColor.GRAY : "") + "For debug information, enable dev mode in the configuration file. "); 53 | sender.sendMessage((player ? ChatColor.GOLD : "") + "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-"); 54 | } else if (args[0].equalsIgnoreCase("reload")) { 55 | reloadConfig(); 56 | sender.sendMessage((player ? ChatColor.GOLD : "") + "[ConditionalCommands] > Config reloaded."); 57 | } else { 58 | sender.sendMessage((player ? ChatColor.GOLD : "") + "[ConditionalCommands] > Incorrect subcommand."); 59 | sender.sendMessage((player ? ChatColor.GOLD : "") + "[ConditionalCommands] >" + (player ? ChatColor.GREEN : "") + " /cc help"); 60 | } 61 | return false; 62 | } 63 | if (args.length == 2) { 64 | if (args[0].equalsIgnoreCase("cooldown")) { 65 | String key = args[1]; 66 | ((PlaceholderCooldown) Placeholders.COOLDOWN.getPlaceholder()).putOnCooldown(key); 67 | } else { 68 | sender.sendMessage((player ? ChatColor.GOLD : "") + "[ConditionalCommands] > Incorrect subcommand."); 69 | sender.sendMessage((player ? ChatColor.GOLD : "") + "[ConditionalCommands] >" + (player ? ChatColor.GREEN : "") + " /cc help"); 70 | } 71 | } 72 | 73 | // Sub command 74 | String action = args[1]; 75 | 76 | // Get the player 77 | Player placeholderFor = Bukkit.getPlayer(args[0]); 78 | if (placeholderFor == null || !placeholderFor.isOnline()) { 79 | sender.sendMessage((player ? ChatColor.GOLD : "") + "[ConditionalCommands] > Not dispatching command because " + args[0] + " is not online..."); 80 | return true; 81 | } 82 | 83 | // Get the condition 84 | String modifiedStr = args[2]; 85 | for (Placeholders placeholder : Placeholders.values()) { 86 | if (placeholder.getPlaceholder().shouldApply(modifiedStr)) { 87 | try { 88 | modifiedStr = placeholder.getPlaceholder().doSubstitution(modifiedStr, placeholderFor); 89 | } catch (Exception ex) { 90 | if (getConfig().getBoolean("dev")) { 91 | sender.sendMessage((player ? ChatColor.GOLD : "") + "[ConditionalCommands] > Failed to apply a placeholder: " + ex.getMessage()); 92 | sender.sendMessage((player ? ChatColor.GOLD : "") + "[ConditionalCommands] >" + (player ? ChatColor.GREEN : "") + " /cc help"); 93 | getLogger().severe("An error occurred whilst applying a placeholder."); 94 | ex.printStackTrace(); 95 | return false; 96 | } else { 97 | sender.sendMessage((player ? ChatColor.GOLD : "") + "[ConditionalCommands] > Couldn't process placeholder."); 98 | return true; 99 | } 100 | } 101 | } 102 | } 103 | 104 | // Get the command 105 | StringBuilder command = new StringBuilder(); 106 | for (int i = 4; i < args.length; i++) { 107 | command.append(args[i]).append(' '); 108 | } 109 | 110 | // Make sure there's a 'do' third. 111 | if (!args[3].equals("do")) { 112 | if (getConfig().getBoolean("dev")) { 113 | sender.sendMessage((player ? ChatColor.GOLD : "") + "[ConditionalCommands] > Missing 'do' clause. Make sure the condition has no spaces."); 114 | sender.sendMessage((player ? ChatColor.GOLD : "") + "[ConditionalCommands] >" + (player ? ChatColor.GREEN : "") + " /cc help"); 115 | return false; 116 | } else { 117 | sender.sendMessage((player ? ChatColor.GOLD : "") + "[ConditionalCommands] > Command has no action."); 118 | return true; 119 | } 120 | } 121 | 122 | // Parse the expression 123 | Expression expression; 124 | try { 125 | expression = new Expression(modifiedStr); 126 | } catch (Expression.ParseException ex) { 127 | if (getConfig().getBoolean("dev")) { 128 | sender.sendMessage((player ? ChatColor.GOLD : "") + "[ConditionalCommands] > Failed to parse \"" + modifiedStr + "\": " + ex.getClass().getSimpleName() + ": " + ex.getMessage()); 129 | sender.sendMessage((player ? ChatColor.GOLD : "") + "[ConditionalCommands] > Roughly translated, that means you spelt something wrong or made a syntax error in the condition."); 130 | sender.sendMessage((player ? ChatColor.GOLD : "") + "[ConditionalCommands] >" + ChatColor.GREEN + " /cc help"); 131 | } else { 132 | sender.sendMessage((player ? ChatColor.GOLD : "") + "[ConditionalCommands] > Couldn't process command (syntax error)."); 133 | } 134 | return false; 135 | } 136 | 137 | if (getConfig().getBoolean("dev")) { 138 | getLogger().info("Successfully parsed expression: " + expression.toString()); 139 | } 140 | 141 | switch (action) { 142 | case "unless": 143 | if (!expression.evaluate()) { 144 | dispatchCommand(sender, command.toString()); 145 | } else { 146 | sender.sendMessage((player ? ChatColor.GOLD : "") + "[ConditionalCommands] > Not dispatching command because \"" + args[2] + "\" evaluated to true"); 147 | } 148 | break; 149 | case "if": 150 | if (expression.evaluate()) { 151 | dispatchCommand(sender, command.toString()); 152 | } else { 153 | sender.sendMessage((player ? ChatColor.GOLD : "") + "[ConditionalCommands] > Not dispatching command because \"" + args[2] + "\" evaluated to false"); 154 | } 155 | break; 156 | default: 157 | sender.sendMessage((player ? ChatColor.GOLD : "") + "[ConditionalCommands] > Incorrect subcommand."); 158 | sender.sendMessage((player ? ChatColor.GOLD : "") + "[ConditionalCommands] >" + (player ? ChatColor.GREEN : "") + " /cc help"); 159 | return false; 160 | } 161 | 162 | return true; 163 | } 164 | 165 | private void dispatchCommand(final CommandSender sender, String command) { 166 | boolean player = (sender instanceof Player); 167 | // Delayed/multi command syntax. 168 | Matcher matcher = SPLIT_PATTERN.matcher(command); 169 | 170 | if (!matcher.find()) { 171 | protectedDispatch(sender, command); 172 | } else { 173 | try { 174 | int delay, cmdStart, cmdEnd; 175 | do { 176 | delay = Integer.parseInt(matcher.group(1)); 177 | cmdStart = matcher.end(); 178 | if (!matcher.find()) { 179 | cmdEnd = command.length(); 180 | } else { 181 | cmdEnd = matcher.start(); 182 | } 183 | 184 | final String cmd = command.substring(cmdStart, cmdEnd).trim(); 185 | 186 | if (delay <= 0) { 187 | protectedDispatch(sender, cmd); 188 | } else { 189 | if (getConfig().getBoolean("dev")) { 190 | sender.sendMessage((player ? ChatColor.GOLD : "") + "[ConditionalCommands] > Will dispatch command \"" + cmd + "\" in " + delay + " ticks"); 191 | } 192 | 193 | Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(this, new Runnable() { 194 | @Override 195 | public void run() { 196 | protectedDispatch(sender, cmd); 197 | } 198 | }, delay); 199 | } 200 | } while (cmdEnd != command.length()); 201 | } catch (NumberFormatException e) { 202 | if (getConfig().getBoolean("dev")) { 203 | sender.sendMessage((player ? ChatColor.GOLD : "") + "[ConditionalCommands] > Invalid delay in the format //: " + e.getMessage()); 204 | sender.sendMessage((player ? ChatColor.GOLD : "") + "[ConditionalCommands] > for " + command); 205 | } else { 206 | sender.sendMessage((player ? ChatColor.GOLD : "") + "[ConditionalCommands] > Couldn't process delayed command."); 207 | } 208 | } 209 | } 210 | } 211 | 212 | private void protectedDispatch(CommandSender sender, String command) { 213 | boolean player = (sender instanceof Player); 214 | try { 215 | sender.sendMessage((player ? ChatColor.GOLD : "") + "[ConditionalCommands] > Dispatching command \"" + command + "\""); 216 | this.getServer().dispatchCommand(sender, command); 217 | } catch (CommandException ex) { 218 | if (getConfig().getBoolean("dev")) { 219 | sender.sendMessage((player ? ChatColor.GOLD : "") + "[ConditionalCommands] > An error occured whilst executing the command. The stack trace has been printed to the console."); 220 | this.getLogger().warning("ConditionalCommands executed this command on the main thread through the sender " + sender); 221 | this.getLogger().warning("The command string is: " + command); 222 | this.getLogger().warning("Stack trace follows: "); 223 | ex.printStackTrace(); 224 | } else { 225 | sender.sendMessage((player ? ChatColor.GOLD : "") + "[ConditionalCommands] > There was a problem trying to run the command."); 226 | } 227 | } 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /src/main/java/me/konsolas/conditionalcommands/Expression.java: -------------------------------------------------------------------------------- 1 | package me.konsolas.conditionalcommands; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.StreamTokenizer; 6 | import java.io.StringReader; 7 | 8 | class Expression { 9 | private final BooleanExpression expression; 10 | 11 | Expression(String exp) { 12 | BooleanLexer lex = new BooleanLexer(exp); 13 | BooleanParser parser = new BooleanParser(lex); 14 | this.expression = parser.build(); 15 | } 16 | 17 | boolean evaluate() { 18 | return (boolean) expression.interpret(); 19 | } 20 | 21 | @Override 22 | public String toString() { 23 | return expression.toString(); 24 | } 25 | 26 | private interface BooleanExpression { 27 | Object interpret(); 28 | } 29 | 30 | private static class BooleanLexer { 31 | private final StreamTokenizer input; 32 | 33 | BooleanLexer(String in) { 34 | input = new StreamTokenizer(new BufferedReader(new StringReader(in))); 35 | 36 | // Syntax rules 37 | input.resetSyntax(); 38 | input.wordChars('a', 'z'); 39 | input.wordChars('A', 'Z'); 40 | input.whitespaceChars('\u0000', ' '); 41 | input.whitespaceChars('\n', '\t'); 42 | 43 | input.ordinaryChar('('); 44 | input.ordinaryChar(')'); 45 | input.ordinaryChar('&'); 46 | input.ordinaryChar('|'); 47 | input.ordinaryChar('!'); 48 | 49 | // Comparison 50 | input.ordinaryChar('>'); 51 | input.ordinaryChar('='); 52 | input.ordinaryChar('<'); 53 | 54 | input.parseNumbers(); 55 | } 56 | 57 | SymbolObject nextToken() { 58 | Symbol symbol; 59 | Double data = null; 60 | 61 | try { 62 | int token = input.nextToken(); 63 | switch (token) { 64 | case StreamTokenizer.TT_EOL: 65 | symbol = Symbol.EOL; 66 | break; 67 | case StreamTokenizer.TT_EOF: 68 | symbol = Symbol.EOF; 69 | break; 70 | case StreamTokenizer.TT_NUMBER: 71 | symbol = Symbol.NUMBER; 72 | data = input.nval; 73 | break; 74 | case '(': 75 | symbol = Symbol.LEFT; 76 | break; 77 | case ')': 78 | symbol = Symbol.RIGHT; 79 | break; 80 | case '&': 81 | symbol = Symbol.AND; 82 | break; 83 | case '|': 84 | symbol = Symbol.OR; 85 | break; 86 | case '!': 87 | symbol = Symbol.NOT; 88 | break; 89 | case '>': 90 | symbol = Symbol.CMP_GREATER_THAN; 91 | break; 92 | case '=': 93 | symbol = Symbol.CMP_EQUALS; 94 | break; 95 | case '<': 96 | symbol = Symbol.CMP_LESS_THAN; 97 | break; 98 | default: 99 | throw new RuntimeException("Lexer error: Got an invalid token"); 100 | } 101 | } catch (IOException ex) { 102 | symbol = Symbol.EOF; 103 | } 104 | return new SymbolObject(symbol, data); 105 | } 106 | 107 | // Types of symbol 108 | enum Symbol { 109 | EOF, EOL, OR, AND, NOT, LEFT, RIGHT, CMP_EQUALS, CMP_LESS_THAN, CMP_GREATER_THAN, NUMBER 110 | } 111 | 112 | static class SymbolObject { 113 | private Symbol symbol; 114 | private Double data; 115 | 116 | SymbolObject(Symbol symbol, Double data) { 117 | this.symbol = symbol; 118 | this.data = data == null ? 0 : data; 119 | } 120 | 121 | Symbol getSymbol() { 122 | return symbol; 123 | } 124 | 125 | double getData() { 126 | return data != null ? data : 0; 127 | } 128 | } 129 | } 130 | 131 | private static abstract class Terminal implements BooleanExpression { 132 | final Object value; 133 | 134 | Terminal(Object value) { 135 | this.value = value; 136 | } 137 | 138 | @Override 139 | public Object interpret() { 140 | return value; 141 | } 142 | 143 | @Override 144 | public String toString() { 145 | return String.format("%s", value); 146 | } 147 | } 148 | 149 | private static abstract class NonTerminal implements BooleanExpression { 150 | BooleanExpression left; 151 | BooleanExpression right; 152 | 153 | void setLeft(BooleanExpression left) { 154 | this.left = left; 155 | } 156 | 157 | public void setRight(BooleanExpression right) { 158 | this.right = right; 159 | } 160 | } 161 | 162 | private static final class Number extends Terminal { 163 | Number(double number) { 164 | super(number); 165 | } 166 | } 167 | 168 | private static final class And extends NonTerminal { 169 | @Override 170 | public Object interpret() { 171 | boolean left = (Boolean) this.left.interpret(); 172 | boolean right = (Boolean) this.right.interpret(); 173 | 174 | return left && right; 175 | } 176 | 177 | @Override 178 | public String toString() { 179 | return String.format("(%s & %s)", left, right); 180 | } 181 | } 182 | 183 | private static final class Or extends NonTerminal { 184 | @Override 185 | public Object interpret() { 186 | boolean left = (Boolean) this.left.interpret(); 187 | boolean right = (Boolean) this.right.interpret(); 188 | 189 | return left || right; 190 | } 191 | 192 | @Override 193 | public String toString() { 194 | return String.format("(%s | %s)", left, right); 195 | } 196 | } 197 | 198 | private static final class Not extends NonTerminal { 199 | void setChild(BooleanExpression child) { 200 | setLeft(child); 201 | } 202 | 203 | @Override 204 | public void setRight(BooleanExpression right) { 205 | throw new UnsupportedOperationException(); 206 | } 207 | 208 | public Object interpret() { 209 | return !((Boolean) this.left.interpret()); 210 | } 211 | 212 | public String toString() { 213 | return String.format("!%s", left); 214 | } 215 | } 216 | 217 | private static final class CmpEquals extends NonTerminal { 218 | @Override 219 | public Object interpret() { 220 | double left = (Double) this.left.interpret(); 221 | double right = (Double) this.right.interpret(); 222 | 223 | return left == right; 224 | } 225 | 226 | @Override 227 | public String toString() { 228 | return String.format("(%s = %s)", left, right); 229 | } 230 | } 231 | 232 | private static final class CmpGreaterThan extends NonTerminal { 233 | @Override 234 | public Object interpret() { 235 | double left = (Double) this.left.interpret(); 236 | double right = (Double) this.right.interpret(); 237 | 238 | return left > right; 239 | } 240 | 241 | @Override 242 | public String toString() { 243 | return String.format("(%s > %s)", left, right); 244 | } 245 | } 246 | 247 | private static final class CmpLessThan extends NonTerminal { 248 | @Override 249 | public Object interpret() { 250 | double left = (Double) this.left.interpret(); 251 | double right = (Double) this.right.interpret(); 252 | 253 | return left < right; 254 | } 255 | 256 | @Override 257 | public String toString() { 258 | return String.format("(%s < %s)", left, right); 259 | } 260 | } 261 | 262 | // Errors 263 | static class ParseException extends RuntimeException { 264 | ParseException(String desc) { 265 | super(desc); 266 | } 267 | } 268 | 269 | /* 270 | * Recursive Descent Parser 271 | * 272 | * ::={} 273 | * ::={} 274 | * ::=||() 275 | * ::= 276 | * ::=floating point number or integer 277 | * 278 | * ::='&' 279 | * ::='|' 280 | * ::='!' 281 | * ::='>'|'='|'<' 282 | */ 283 | private static class BooleanParser { 284 | private BooleanLexer lexer; 285 | private BooleanLexer.SymbolObject symbol; 286 | private BooleanExpression root; 287 | 288 | BooleanParser(BooleanLexer lexer) { 289 | this.lexer = lexer; 290 | } 291 | 292 | BooleanExpression build() { 293 | try { 294 | expression(); 295 | root.interpret(); 296 | } catch (NullPointerException ex) { 297 | throw new ParseException("Failed to parse: root expression is null."); 298 | } catch (Exception ex) { 299 | throw new ParseException(ex.getClass().getSimpleName() + ": " + ex.getMessage()); 300 | } 301 | return root; 302 | } 303 | 304 | // Begin the descent.... 305 | private void expression() { 306 | term(); 307 | while (symbol.getSymbol() == BooleanLexer.Symbol.OR) { 308 | Or or = new Or(); 309 | or.setLeft(root); 310 | term(); 311 | or.setRight(root); 312 | root = or; 313 | } 314 | } 315 | 316 | private void term() { 317 | factor(); 318 | while (symbol.getSymbol() == BooleanLexer.Symbol.AND) { 319 | And and = new And(); 320 | and.setLeft(root); 321 | factor(); 322 | and.setRight(root); 323 | root = and; 324 | } 325 | } 326 | 327 | private void factor() { 328 | comparison(); 329 | 330 | if (symbol.getSymbol() == BooleanLexer.Symbol.NOT) { 331 | Not not = new Not(); 332 | factor(); 333 | not.setChild(root); 334 | root = not; 335 | } else if (symbol.getSymbol() == BooleanLexer.Symbol.LEFT) { 336 | expression(); 337 | symbol = lexer.nextToken(); 338 | } 339 | } 340 | 341 | private void comparison() { 342 | constant(); 343 | 344 | if (symbol.getSymbol() == BooleanLexer.Symbol.CMP_EQUALS) { 345 | CmpEquals equals = new CmpEquals(); 346 | equals.setLeft(root); 347 | constant(); 348 | equals.setRight(root); 349 | root = equals; 350 | } else if (symbol.getSymbol() == BooleanLexer.Symbol.CMP_GREATER_THAN) { 351 | CmpGreaterThan greater = new CmpGreaterThan(); 352 | greater.setLeft(root); 353 | constant(); 354 | greater.setRight(root); 355 | root = greater; 356 | } else if (symbol.getSymbol() == BooleanLexer.Symbol.CMP_LESS_THAN) { 357 | CmpLessThan less = new CmpLessThan(); 358 | less.setLeft(root); 359 | constant(); 360 | less.setRight(root); 361 | root = less; 362 | } 363 | } 364 | 365 | private void constant() { 366 | symbol = lexer.nextToken(); 367 | if (symbol.getSymbol() == BooleanLexer.Symbol.NUMBER) { 368 | root = new Number(symbol.getData()); 369 | symbol = lexer.nextToken(); 370 | } 371 | } 372 | } 373 | } -------------------------------------------------------------------------------- /src/main/java/me/konsolas/conditionalcommands/Placeholders.java: -------------------------------------------------------------------------------- 1 | package me.konsolas.conditionalcommands; 2 | 3 | import me.konsolas.conditionalcommands.placeholders.*; 4 | 5 | enum Placeholders { 6 | PING(new PlaceholderPing()), 7 | TPS(new PlaceholderTPS()), 8 | TIME_ONLINE(new PlaceholderTimeOnline()), 9 | PLAYER_COUNT(new PlaceholderPlayerCount()), 10 | UPTIME(new PlaceholderUptime()), 11 | PERM(new PlaceholderPerm()), 12 | PERM_COUNT(new PlaceholderPermCount()), 13 | AACVL(new PlaceholderAACVL()), 14 | CHANCE(new PlaceholderChance()), 15 | COOLDOWN(new PlaceholderCooldown()); 16 | 17 | private final Placeholder placeholder; 18 | 19 | Placeholders(Placeholder placeholder) { 20 | this.placeholder = placeholder; 21 | } 22 | 23 | public Placeholder getPlaceholder() { 24 | return placeholder; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/me/konsolas/conditionalcommands/placeholders/AbstractParameteredPlaceholder.java: -------------------------------------------------------------------------------- 1 | package me.konsolas.conditionalcommands.placeholders; 2 | 3 | import org.bukkit.entity.Player; 4 | import org.bukkit.plugin.Plugin; 5 | 6 | import java.util.regex.Matcher; 7 | import java.util.regex.Pattern; 8 | 9 | public abstract class AbstractParameteredPlaceholder implements Placeholder { 10 | private final Pattern pattern; 11 | 12 | AbstractParameteredPlaceholder(String base) { 13 | this.pattern = Pattern.compile("-" + base + ":([A-Za-z0-9%._]*)-"); 14 | } 15 | 16 | @Override 17 | public boolean shouldApply(String test) { 18 | return pattern.matcher(test).find(); 19 | } 20 | 21 | protected abstract String getSub(Player player, String param); 22 | 23 | @Override 24 | public String doSubstitution(String input, Player player) { 25 | Matcher matcher = pattern.matcher(input); 26 | 27 | while (matcher.find()) { 28 | input = input.replaceAll(Pattern.quote(matcher.group()), getSub(player, matcher.group(1))); 29 | } 30 | 31 | return input; 32 | } 33 | 34 | @Override 35 | public void init(Plugin plugin) { 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/me/konsolas/conditionalcommands/placeholders/AbstractStandardPlaceholder.java: -------------------------------------------------------------------------------- 1 | package me.konsolas.conditionalcommands.placeholders; 2 | 3 | import org.bukkit.entity.Player; 4 | import org.bukkit.plugin.Plugin; 5 | 6 | public abstract class AbstractStandardPlaceholder implements Placeholder { 7 | private String matcher; 8 | 9 | AbstractStandardPlaceholder(String matcher) { 10 | this.matcher = '-' + matcher + '-'; 11 | } 12 | 13 | @Override 14 | public final boolean shouldApply(String test) { 15 | return test.contains(matcher); 16 | } 17 | 18 | @Override 19 | public final String doSubstitution(String input, Player player) { 20 | return input.replaceAll(matcher, Double.toString(getStat(player))); 21 | } 22 | 23 | @Override 24 | public void init(Plugin plugin) { 25 | } 26 | 27 | protected abstract double getStat(Player player); 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/me/konsolas/conditionalcommands/placeholders/Placeholder.java: -------------------------------------------------------------------------------- 1 | package me.konsolas.conditionalcommands.placeholders; 2 | 3 | import org.bukkit.entity.Player; 4 | import org.bukkit.plugin.Plugin; 5 | 6 | public interface Placeholder { 7 | /** 8 | * Checks whether the given string contains a pattern that matches this placeholder 9 | * 10 | * @param test input string 11 | * @return true if this placeholder should be applied to the string 12 | */ 13 | boolean shouldApply(String test); 14 | 15 | /** 16 | * Replaces all instances of this placeholder in the input string with an appropriate statistic for the player 17 | * 18 | * @param input string to replace 19 | * @param player player to use 20 | * @return a new string 21 | */ 22 | String doSubstitution(String input, Player player); 23 | 24 | /** 25 | * Called when the server starts, allowing the placeholder to initialize tasks or counters. 26 | * 27 | * @param plugin instance of a bukkit plugin 28 | */ 29 | void init(Plugin plugin); 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/me/konsolas/conditionalcommands/placeholders/PlaceholderAACVL.java: -------------------------------------------------------------------------------- 1 | package me.konsolas.conditionalcommands.placeholders; 2 | 3 | import org.bukkit.entity.Player; 4 | import org.bukkit.plugin.Plugin; 5 | 6 | import java.lang.reflect.Field; 7 | import java.lang.reflect.InvocationTargetException; 8 | import java.lang.reflect.Method; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | public class PlaceholderAACVL extends AbstractParameteredPlaceholder { 13 | private Map hackTypes = new HashMap<>(); 14 | private Method getAPI; 15 | private Method getViolationLevel; 16 | 17 | public PlaceholderAACVL() { 18 | super("aacvl"); 19 | } 20 | 21 | @Override 22 | protected String getSub(Player player, String param) { 23 | try { 24 | Object obj = getAPI.invoke(null); 25 | int VL = (int) getViolationLevel.invoke(obj, player, hackTypes.get(param.toLowerCase())); 26 | return Integer.toString(VL); 27 | } catch (IllegalAccessException | InvocationTargetException e) { 28 | throw new RuntimeException("Failed to handle AAC placeholder for " + param, e); 29 | } 30 | } 31 | 32 | @Override 33 | public void init(Plugin plugin) { 34 | if (plugin.getServer().getPluginManager().getPlugin("AAC") == null) { 35 | return; // AAC not installed 36 | } 37 | 38 | try { 39 | Class hackTypeClass = Class.forName("me.konsolas.aac.api.HackType"); 40 | 41 | for (Field field : hackTypeClass.getDeclaredFields()) { 42 | if (field.getType().equals(hackTypeClass)) { 43 | hackTypes.put(field.getName().toLowerCase(), field.get(null)); 44 | } 45 | } 46 | 47 | getAPI = Class.forName("me.konsolas.aac.api.AACAPIProvider") 48 | .getDeclaredMethod("getAPI"); 49 | getViolationLevel = Class.forName("me.konsolas.aac.api.AACAPI") 50 | .getDeclaredMethod("getViolationLevel", Player.class, hackTypeClass); 51 | } catch (ClassNotFoundException | IllegalAccessException | NoSuchMethodException e) { 52 | throw new RuntimeException("Failed to initialise AAC placeholders", e); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/me/konsolas/conditionalcommands/placeholders/PlaceholderChance.java: -------------------------------------------------------------------------------- 1 | package me.konsolas.conditionalcommands.placeholders; 2 | 3 | import org.bukkit.entity.Player; 4 | 5 | import java.util.concurrent.ThreadLocalRandom; 6 | 7 | public class PlaceholderChance extends AbstractParameteredPlaceholder { 8 | public PlaceholderChance() { 9 | super("chance"); 10 | } 11 | 12 | @Override 13 | protected String getSub(Player player, String param) { 14 | if (!param.endsWith("%")) { 15 | throw new RuntimeException("Invalid chance parameter format, expecting %"); 16 | } 17 | 18 | double percent; 19 | try { 20 | percent = Double.parseDouble(param.substring(0, param.length() - 1)); 21 | } catch (NumberFormatException e) { 22 | throw new RuntimeException("Invalid number format: " + param, e); 23 | } 24 | 25 | if (percent > 100 || percent < 0) { 26 | throw new RuntimeException("Expecting percentage between 0 and 100"); 27 | } 28 | 29 | double random = ThreadLocalRandom.current().nextDouble() * 100; 30 | if (random < percent) { 31 | return "1.0"; 32 | } else { 33 | return "0.0"; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/me/konsolas/conditionalcommands/placeholders/PlaceholderCooldown.java: -------------------------------------------------------------------------------- 1 | package me.konsolas.conditionalcommands.placeholders; 2 | 3 | import com.google.common.cache.Cache; 4 | import com.google.common.cache.CacheBuilder; 5 | import org.bukkit.entity.Player; 6 | 7 | import java.util.concurrent.TimeUnit; 8 | 9 | public class PlaceholderCooldown extends AbstractParameteredPlaceholder { 10 | private final Cache cooldowns = CacheBuilder.newBuilder() 11 | .expireAfterWrite(12, TimeUnit.HOURS) 12 | .concurrencyLevel(1) 13 | .build(); 14 | 15 | public PlaceholderCooldown() { 16 | super("cooldown"); 17 | } 18 | 19 | @Override 20 | protected String getSub(Player player, String param) { 21 | long currMillis = System.currentTimeMillis(); 22 | Long millis = cooldowns.getIfPresent(param); 23 | if (millis == null) { 24 | return "43200"; 25 | } else { 26 | long diff = TimeUnit.MILLISECONDS.toSeconds(currMillis - millis); 27 | if (diff < 1) { 28 | return "0"; 29 | } else { 30 | return Long.toString(Math.min(diff, 43200)); 31 | } 32 | } 33 | } 34 | 35 | public void putOnCooldown(String key) { 36 | cooldowns.put(key, System.currentTimeMillis()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/me/konsolas/conditionalcommands/placeholders/PlaceholderPerm.java: -------------------------------------------------------------------------------- 1 | package me.konsolas.conditionalcommands.placeholders; 2 | 3 | import org.bukkit.entity.Player; 4 | 5 | public class PlaceholderPerm extends AbstractParameteredPlaceholder { 6 | 7 | public PlaceholderPerm() { 8 | super("perm"); 9 | } 10 | 11 | @Override 12 | protected String getSub(Player player, String param) { 13 | return player.hasPermission(param) ? "1.0" : "0.0"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/me/konsolas/conditionalcommands/placeholders/PlaceholderPermCount.java: -------------------------------------------------------------------------------- 1 | package me.konsolas.conditionalcommands.placeholders; 2 | 3 | import org.bukkit.entity.Player; 4 | 5 | public class PlaceholderPermCount extends AbstractParameteredPlaceholder { 6 | 7 | public PlaceholderPermCount() { 8 | super("perm_count"); 9 | } 10 | 11 | @Override 12 | protected String getSub(Player player, String param) { 13 | int count = 0; 14 | for (Player plr : player.getServer().getOnlinePlayers()) { 15 | if (plr.hasPermission(param)) { 16 | count += 1; 17 | } 18 | } 19 | return Long.toString(count); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/me/konsolas/conditionalcommands/placeholders/PlaceholderPing.java: -------------------------------------------------------------------------------- 1 | package me.konsolas.conditionalcommands.placeholders; 2 | 3 | import org.bukkit.entity.Player; 4 | 5 | import java.lang.reflect.Field; 6 | import java.lang.reflect.Method; 7 | 8 | public class PlaceholderPing extends AbstractStandardPlaceholder { 9 | private Method getHandleMethod; 10 | private Field pingField; 11 | 12 | public PlaceholderPing() { 13 | super("ping"); 14 | } 15 | 16 | @Override 17 | public double getStat(Player player) { 18 | try { 19 | if (getHandleMethod == null) { 20 | getHandleMethod = player.getClass().getDeclaredMethod("getHandle"); 21 | getHandleMethod.setAccessible(true); 22 | } 23 | Object entityPlayer = getHandleMethod.invoke(player); 24 | if (pingField == null) { 25 | pingField = entityPlayer.getClass().getDeclaredField("ping"); 26 | pingField.setAccessible(true); 27 | } 28 | int ping = pingField.getInt(entityPlayer); 29 | 30 | return ping > 0 ? ping : 0; 31 | } catch (Exception e) { 32 | return 0; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/me/konsolas/conditionalcommands/placeholders/PlaceholderPlayerCount.java: -------------------------------------------------------------------------------- 1 | package me.konsolas.conditionalcommands.placeholders; 2 | 3 | import org.bukkit.Bukkit; 4 | import org.bukkit.entity.Player; 5 | 6 | public class PlaceholderPlayerCount extends AbstractStandardPlaceholder { 7 | public PlaceholderPlayerCount() { 8 | super("player_count"); 9 | } 10 | 11 | @Override 12 | public double getStat(Player player) { 13 | return Bukkit.getOnlinePlayers().size(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/me/konsolas/conditionalcommands/placeholders/PlaceholderTPS.java: -------------------------------------------------------------------------------- 1 | package me.konsolas.conditionalcommands.placeholders; 2 | 3 | import org.bukkit.entity.Player; 4 | import org.bukkit.plugin.Plugin; 5 | import org.bukkit.scheduler.BukkitRunnable; 6 | 7 | import java.util.ArrayDeque; 8 | import java.util.Collections; 9 | import java.util.Deque; 10 | 11 | public class PlaceholderTPS extends AbstractStandardPlaceholder { 12 | private TPS tps; 13 | 14 | public PlaceholderTPS() { 15 | super("tps"); 16 | } 17 | 18 | @Override 19 | public void init(Plugin plugin) { 20 | tps = new TPS(plugin); 21 | } 22 | 23 | @Override 24 | public double getStat(Player player) { 25 | return tps.getCurrentTPS(); 26 | } 27 | 28 | private static class TPS extends BukkitRunnable { 29 | private int resolution = 40; 30 | private long lastTick; 31 | private Deque tickIntervals; 32 | 33 | private TPS(Plugin plugin) { 34 | lastTick = System.currentTimeMillis(); 35 | tickIntervals = new ArrayDeque<>(Collections.nCopies(resolution, 50L)); 36 | this.runTaskTimer(plugin, 1, 1); 37 | } 38 | 39 | double getCurrentTPS() { 40 | try { 41 | double tps = 1000D / getDelta(); 42 | return tps > 20D ? 20D : tps; 43 | } catch (Exception e) { 44 | return 20D; 45 | } 46 | } 47 | 48 | @Override 49 | public void run() { 50 | long curr = System.currentTimeMillis(); 51 | long delta = curr - lastTick; 52 | lastTick = curr; 53 | tickIntervals.removeFirst(); 54 | tickIntervals.addLast(delta); 55 | } 56 | 57 | private double getDelta() { 58 | int base = 0; 59 | for (long delta : tickIntervals) { 60 | base += delta; 61 | } 62 | return (double) base / (double) resolution; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/me/konsolas/conditionalcommands/placeholders/PlaceholderTimeOnline.java: -------------------------------------------------------------------------------- 1 | package me.konsolas.conditionalcommands.placeholders; 2 | 3 | import org.bukkit.entity.Player; 4 | import org.bukkit.event.EventHandler; 5 | import org.bukkit.event.Listener; 6 | import org.bukkit.event.player.PlayerJoinEvent; 7 | import org.bukkit.event.player.PlayerKickEvent; 8 | import org.bukkit.event.player.PlayerQuitEvent; 9 | import org.bukkit.plugin.Plugin; 10 | 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | 14 | public class PlaceholderTimeOnline extends AbstractStandardPlaceholder implements Listener { 15 | private final Map loginTime = new HashMap<>(); 16 | 17 | public PlaceholderTimeOnline() { 18 | super("time_online"); 19 | } 20 | 21 | @Override 22 | public void init(Plugin plugin) { 23 | plugin.getServer().getPluginManager().registerEvents(this, plugin); 24 | 25 | // For /reload compatibility 26 | for (Player player : plugin.getServer().getOnlinePlayers()) { 27 | loginTime.put(player, System.currentTimeMillis()); 28 | } 29 | } 30 | 31 | @EventHandler 32 | public void onPlayerJoin(PlayerJoinEvent event) { 33 | loginTime.put(event.getPlayer(), System.currentTimeMillis()); 34 | } 35 | 36 | @EventHandler 37 | public void onPlayerQuit(PlayerQuitEvent event) { 38 | loginTime.remove(event.getPlayer()); 39 | } 40 | 41 | @EventHandler 42 | public void onPlayerKick(PlayerKickEvent event) { 43 | loginTime.remove(event.getPlayer()); 44 | } 45 | 46 | @Override 47 | public double getStat(Player player) { 48 | return System.currentTimeMillis() - loginTime.get(player); 49 | } 50 | } -------------------------------------------------------------------------------- /src/main/java/me/konsolas/conditionalcommands/placeholders/PlaceholderUptime.java: -------------------------------------------------------------------------------- 1 | package me.konsolas.conditionalcommands.placeholders; 2 | 3 | import org.bukkit.entity.Player; 4 | import org.bukkit.plugin.Plugin; 5 | 6 | public class PlaceholderUptime extends AbstractStandardPlaceholder { 7 | private long startupTimeMS; 8 | 9 | public PlaceholderUptime() { 10 | super("uptime"); 11 | } 12 | 13 | @Override 14 | public void init(Plugin plugin) { 15 | this.startupTimeMS = System.currentTimeMillis(); 16 | } 17 | 18 | @Override 19 | public double getStat(Player player) { 20 | return (System.currentTimeMillis() - startupTimeMS) / 50; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/resources/config.yml: -------------------------------------------------------------------------------- 1 | # Enable developer mode for ConditionalCommands. 2 | # Errors are suppressed by default. 3 | dev: false -------------------------------------------------------------------------------- /src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: ConditionalCommands 2 | version: 1.5 3 | main: me.konsolas.conditionalcommands.ConditionalCommands 4 | api-version: 1.13 5 | description: Excecute commands based on conditions. Useful in configured commands from other plugins 6 | author: konsolas 7 | website: https://github.com/konsolas/ConditionalCommands 8 | softdepend: [AAC] 9 | commands: 10 | ccmd: 11 | usage: / [args...] 12 | description: Main command. /cc help 13 | aliases: [cc] 14 | permission: cc.use 15 | permission-message: Such command, no permission. 16 | permissions: 17 | cc.use: 18 | description: Root permission 19 | default: op 20 | -------------------------------------------------------------------------------- /src/test/java/me/konsolas/conditionalcommands/ExpressionTest.java: -------------------------------------------------------------------------------- 1 | package me.konsolas.conditionalcommands; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | public class ExpressionTest { 7 | @Test 8 | public void testEquality() { 9 | Expression expression = new Expression("10 = 10"); 10 | Assert.assertTrue(expression.evaluate()); 11 | } 12 | 13 | @Test 14 | public void testGreaterThan() { 15 | Expression expression = new Expression("10 > 9"); 16 | Assert.assertTrue(expression.evaluate()); 17 | } 18 | 19 | @Test 20 | public void testLessThan() { 21 | Expression expression = new Expression("9 < 11"); 22 | Assert.assertTrue(expression.evaluate()); 23 | } 24 | 25 | @Test 26 | public void testOr() { 27 | Expression expression = new Expression("9 < 11 | 9 = 9"); 28 | Assert.assertTrue(expression.evaluate()); 29 | } 30 | 31 | @Test 32 | public void testAnd() { 33 | Expression expression = new Expression("9 < 11 | 9 = 9"); 34 | Assert.assertTrue(expression.evaluate()); 35 | } 36 | 37 | @Test 38 | public void testNot() { 39 | Expression expression = new Expression("!(1 > 10)"); 40 | Assert.assertTrue(expression.evaluate()); 41 | } 42 | 43 | @Test 44 | public void testBrackets() { 45 | Expression expression = new Expression("!(1 < 1 | 2 < 1) & 4 > 1"); 46 | Assert.assertTrue(expression.evaluate()); 47 | } 48 | 49 | @Test(expected = Expression.ParseException.class) 50 | public void testMissedPlaceholder() { 51 | Expression expression = new Expression("(1 = 1 | 2 < 1) & -ping- > 1"); 52 | Assert.assertTrue(expression.evaluate()); 53 | } 54 | } 55 | --------------------------------------------------------------------------------