Skip to content

Commit

Permalink
Merge pull request #2 from DatAsianBoi123/dev
Browse files Browse the repository at this point in the history
version 2.1.0
  • Loading branch information
DatAsianBoi123 authored Nov 2, 2023
2 parents 18eb0f6 + 45a2039 commit 11865c3
Show file tree
Hide file tree
Showing 24 changed files with 4,817 additions and 59 deletions.
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.datasiqn</groupId>
<artifactId>commandcore</artifactId>
<version>2.0.0</version>
<version>2.1.0</version>

<properties>
<maven.compiler.source>8</maven.compiler.source>
Expand Down Expand Up @@ -102,7 +102,7 @@
<dependency>
<groupId>com.github.DatAsianBoi123</groupId>
<artifactId>ResultAPI</artifactId>
<version>99ce07576b</version>
<version>1.2.0</version>
<scope>compile</scope>
</dependency>

Expand Down
35 changes: 16 additions & 19 deletions src/main/java/com/datasiqn/commandcore/CommandCore.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
import com.datasiqn.commandcore.argument.type.ArgumentType;
import com.datasiqn.commandcore.command.Command;
import com.datasiqn.commandcore.command.CommandContext;
import com.datasiqn.commandcore.command.CommandSource;
import com.datasiqn.commandcore.command.builder.ArgumentBuilder;
import com.datasiqn.commandcore.command.builder.CommandBuilder;
import com.datasiqn.commandcore.command.source.*;
import com.datasiqn.commandcore.managers.CommandManager;
import com.datasiqn.resultapi.Result;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.BlockCommandSender;
import org.bukkit.command.CommandMap;
import org.bukkit.command.CommandSender;
import org.bukkit.command.PluginCommand;
Expand Down Expand Up @@ -83,7 +83,10 @@ public void sendHelpMenu(@NotNull CommandSender sender) {
sender.sendMessage(ChatColor.GOLD + (options.hasCustomPluginName() ? options.getPluginName() : plugin.getName()) + " Commands");
commandManager.getCommandNames(false).stream().sorted().forEach(name -> {
Command command = commandManager.getCommand(name, false);
if (!command.hasPermission() || sender.hasPermission(command.getPermissionString())) sender.sendMessage(ChatColor.YELLOW + " " + name, ChatColor.GRAY + " ↳ " + command.getDescription());
if (!command.hasPermission() || sender.hasPermission(command.getPermissionString())) {
String description = command.hasDescription() ? command.getDescription() : "No description provided";
sender.sendMessage(ChatColor.YELLOW + " " + name, ChatColor.GRAY + " ↳ " + description);
}
});
}

Expand Down Expand Up @@ -157,6 +160,7 @@ public static CommandCore getInstance() {
constructor.setAccessible(true);
command = constructor.newInstance(rootCommand, plugin);
command.setAliases(options.getAliases());
command.setDescription("Base plugin command");
commandMap.register(rootCommand, plugin.getName(), command);
} catch (NoSuchFieldException | IllegalAccessException | InvocationTargetException |
InstantiationException | NoSuchMethodException e) {
Expand Down Expand Up @@ -227,21 +231,14 @@ public static CommandCore getInstance() {
*/
@Contract(value = "_ -> new", pure = true)
public static @NotNull CommandSource createSource(CommandSender sender) {
return new CommandSource() {
@Override
public @NotNull Result<Player, String> getPlayerChecked() {
return Result.resolve(() -> (Player) sender, error -> "Sender is not a player");
}

@Override
public @NotNull Result<Entity, String> getEntityChecked() {
return Result.resolve(() -> (Entity) sender, error -> "Sender is not an entity");
}

@Override
public @NotNull CommandSender getSender() {
return sender;
}
};
if (sender instanceof Player) {
return new PlayerCommandSource(((Player) sender));
} else if (sender instanceof Entity) {
return new EntityCommandSource(((Entity) sender));
} else if (sender instanceof BlockCommandSender) {
return new BlockCommandSource(((BlockCommandSender) sender));
} else {
return new GenericCommandSource(sender);
}
}
}
11 changes: 11 additions & 0 deletions src/main/java/com/datasiqn/commandcore/argument/Arguments.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,15 @@ public interface Arguments {
* @return The newly created {@code ArgumentReader}
*/
@NotNull ArgumentReader asReader();

/**
* Checks if {@code i} is smaller than {@code size} and at least 0
* @param i The number to check
* @param size The total size
* @throws IndexOutOfBoundsException If {@code i} {@literal >=} {@code size} or {@code i} {@literal <} 0
*/
static void checkBounds(int i, int size) {
if (i >= size) throw new IndexOutOfBoundsException("index (" + i + ") is greater than total size (" + size + ")");
if (i < 0) throw new IndexOutOfBoundsException("index cannot be negative");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,9 @@ public void jumpTo(int index) {
}
return builder.toString();
}

@Override
public String toString() {
return arg;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,21 @@
import java.util.List;

/**
* Represents a list of arguments that are already parsed
* Represents a list of arguments.
* <p>
* Internally, this uses a {@code List<String>} to store arguments and parses the string it every time the user wants an argument.
*/
public class StringArguments implements Arguments {
protected final List<String> allArguments;
private final List<String> allArguments;
private final String stringArguments;

/**
* Creates a new {@code ListArguments}
* @param args The arguments in a string list
*/
public StringArguments(List<String> args) {
allArguments = args;
stringArguments = String.join(" ", allArguments);
}

@Override
Expand All @@ -27,23 +31,19 @@ public int size() {

@Override
public @NotNull <T> Result<T, String> getChecked(int i, @NotNull ArgumentType<T> type) {
checkBounds(i);
Arguments.checkBounds(i, size());
return type.parse(new StringArgumentReader(allArguments.get(i)));
}

@Override
public @NotNull String getString(int i) {
checkBounds(i);
Arguments.checkBounds(i, size());
return allArguments.get(i);
}

@Override
public @NotNull ArgumentReader asReader() {
return new StringArgumentReader(String.join(" ", allArguments));
return new StringArgumentReader(stringArguments);
}

private void checkBounds(int i) {
if (i >= size()) throw new IndexOutOfBoundsException("index (" + i + ") is greater than total size (" + size() + ")");
if (i < 0) throw new IndexOutOfBoundsException("index cannot be negative");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@
import com.datasiqn.commandcore.command.CommandContext;
import com.datasiqn.resultapi.None;
import com.datasiqn.resultapi.Result;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.loot.LootTable;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
Expand Down Expand Up @@ -64,6 +69,33 @@ public interface ArgumentType<T> {
*/
ArgumentType<Vector> VECTOR = new VectorArgumentType();

/**
* {@code ArgumentType} that represents a loaded world
*/
ArgumentType<World> WORLD = new WorldArgumentType();

/**
* {@code ArgumentType} that represents an entity
*/
ArgumentType<EntityType> ENTITY = new EnumArgumentType<>(EntityType.class, "entity");

/**
* {@code ArgumentType} that represents an entity that is living
*/
ArgumentType<EntityType> LIVING_ENTITY = new FilteredEnumArgumentType<>(EntityType.class, EntityType::isAlive, "living entity");

/**
* {@code ArgumentType} that represents an entity that can be spawned using {@link org.bukkit.World#spawnEntity(Location, EntityType)}.
* <br>
* This is the same as the {@code ENTITY} argument type, except that this omits the {@code Player} entity type
*/
ArgumentType<EntityType> SPAWNABLE_ENTITY = new FilteredEnumArgumentType<>(EntityType.class, entityType -> entityType != EntityType.PLAYER, "living entity");

/**
* {@code ArgumentType} that represents a loot table
*/
ArgumentType<LootTable> LOOT_TABLE = new LootTableArgumentType();

/**
* {@code ArgumentType} that represents a material
*/
Expand Down Expand Up @@ -136,6 +168,7 @@ class EnumArgumentType<T extends Enum<T>> implements SimpleArgumentType<T> {
private final Class<T> enumClass;
private final String enumName;
private final List<String> tabCompletes;
private final boolean uppercaseValues;

/**
* Creates a new {@code ArgumentType}
Expand All @@ -153,6 +186,17 @@ public EnumArgumentType(@NotNull Class<T> enumClass, @NotNull String enumName) {
this.enumClass = enumClass;
this.enumName = enumName;
this.tabCompletes = Arrays.stream(enumClass.getEnumConstants()).map(val -> val.name().toLowerCase(Locale.ROOT)).collect(Collectors.toList());

for (T enumConstant : enumClass.getEnumConstants()) {
for (char letter : enumConstant.name().toCharArray()) {
if (Character.isLetter(letter) && !Character.isUpperCase(letter)) {
this.uppercaseValues = false;
Bukkit.getLogger().warning("[CommandCore] Enum " + enumName + " includes values that aren't in uppercase!");
return;
}
}
}
this.uppercaseValues = true;
}

@Override
Expand All @@ -162,7 +206,7 @@ public EnumArgumentType(@NotNull Class<T> enumClass, @NotNull String enumName) {

@Override
public @NotNull Result<T, None> parseWord(String word) {
return Result.resolve(() -> EnumUtils.findEnumInsensitiveCase(enumClass, word));
return Result.resolve(() -> uppercaseValues ? Enum.valueOf(enumClass, word.toUpperCase()) : EnumUtils.findEnumInsensitiveCase(enumClass, word));
}

@Override
Expand All @@ -175,10 +219,8 @@ public EnumArgumentType(@NotNull Class<T> enumClass, @NotNull String enumName) {
* Represents a custom {@code ArgumentType} that parses to a filtered enum
* @param <T> The type of the enum
*/
class FilteredEnumArgumentType<T extends Enum<T>> implements SimpleArgumentType<T> {
class FilteredEnumArgumentType<T extends Enum<T>> extends EnumArgumentType<T> {
private final Predicate<T> filter;
private final String enumName;
private final Class<T> enumClass;
private final List<String> tabCompletes;

/**
Expand All @@ -188,21 +230,14 @@ class FilteredEnumArgumentType<T extends Enum<T>> implements SimpleArgumentType<
* @param enumName The name of the enum. This is used when displaying an error message (Invalid {{@code enumName}} '{val}'
*/
public FilteredEnumArgumentType(@NotNull Class<T> enumClass, Predicate<T> filter, String enumName) {
this.enumClass = enumClass;
super(enumClass, enumName);
this.filter = filter;
this.enumName = enumName;
this.tabCompletes = Arrays.stream(enumClass.getEnumConstants()).filter(filter).map(val -> val.name().toLowerCase(Locale.ROOT)).collect(Collectors.toList());
}

@Override
public @NotNull String getTypeName() {
return enumName;
}

@Override
public @NotNull Result<T, None> parseWord(String word) {
return Result.resolve(() -> EnumUtils.findEnumInsensitiveCase(enumClass, word))
.andThen(val -> filter.test(val) ? Result.ok(val) : Result.error());
return super.parseWord(word).andThen(val -> filter.test(val) ? Result.ok(val) : Result.error());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.datasiqn.commandcore.argument.type;

import com.datasiqn.commandcore.command.CommandContext;
import com.datasiqn.resultapi.None;
import com.datasiqn.resultapi.Result;
import org.bukkit.loot.LootTable;
import org.bukkit.loot.LootTables;
import org.jetbrains.annotations.NotNull;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

class LootTableArgumentType implements SimpleArgumentType<LootTable> {
private final List<String> tabCompletes = Arrays.stream(LootTables.values()).map(LootTables::name).collect(Collectors.toList());

@Override
public @NotNull String getTypeName() {
return "loot table";
}

@Override
public @NotNull Result<LootTable, None> parseWord(String word) {
return Result.resolve(() -> LootTables.valueOf(word.toUpperCase())).map(LootTables::getLootTable);
}

@Override
public @NotNull List<String> getTabComplete(@NotNull CommandContext context) {
return tabCompletes;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public interface SimpleArgumentType<T> extends ArgumentType<T> {
* Parses just a single word
*
* @param word The word
* @return An {@code Optional} containing the parsed value
* @return A {@code Result} containing the parsed value
*/
@NotNull Result<T, None> parseWord(String word);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.datasiqn.commandcore.argument.type;

import com.datasiqn.commandcore.command.CommandContext;
import com.datasiqn.resultapi.None;
import com.datasiqn.resultapi.Result;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.jetbrains.annotations.NotNull;

import java.util.List;
import java.util.stream.Collectors;

class WorldArgumentType implements SimpleArgumentType<World> {
@Override
public @NotNull String getTypeName() {
return "world";
}

@Override
public @NotNull Result<World, None> parseWord(String word) {
return Result.ofNullable(Bukkit.getWorld(word), None.NONE);
}

@Override
public @NotNull List<String> getTabComplete(@NotNull CommandContext context) {
return Bukkit.getWorlds().stream().map(World::getName).collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.datasiqn.commandcore.command;

import com.datasiqn.commandcore.argument.Arguments;
import com.datasiqn.commandcore.command.source.CommandSource;
import org.jetbrains.annotations.NotNull;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,17 @@ public BuilderCommand(@NotNull CommandBuilder commandBuilder, List<String> usage
Result<CommandNode<?>, List<String>> resultNode = current.node;
if (resultNode.isError()) {
if (current.extraInput) {
return Result.error(Collections.singletonList("Expected end of input, but got extra args instead"));
return Result.error(Collections.singletonList("Expected end of input, but got extra parameters instead"));
}
List<String> exceptions = resultNode.unwrapError();
List<String> messages = new ArrayList<>();
List<String> matches = current.args;
messages.add("Invalid parameter '" + matches.get(matches.size() - 1) + "' at position " + matches.size() + ": ");
messages.addAll(exceptions);
return Result.error(messages);
if (exceptions.isEmpty()) exceptions.add("Incorrect argument '" + reader.splice(reader.index()) + "'");
String rootCommand = CommandCore.getInstance().getOptions().getRootCommand();
String label = context.getLabel();
String correctSection = ChatColor.GRAY + rootCommand + " " + label + " " + reader.splice(0, reader.index());
String incorrectParameter = ChatColor.RED.toString() + ChatColor.UNDERLINE + reader.splice(reader.index());
exceptions.add(correctSection + incorrectParameter + ChatColor.RESET + ChatColor.RED + ChatColor.ITALIC + " <--[HERE]");
exceptions.add("");
return Result.error(exceptions);
}
CommandNode<?> node = resultNode.unwrap();
CommandContext newContext = buildContext(context, current);
Expand Down
Loading

0 comments on commit 11865c3

Please sign in to comment.