Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Annotations Reloaded for 10.0 #597

Draft
wants to merge 3 commits into
base: dev/annotations-reloaded
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 139 additions & 0 deletions commandapi-annotations-reloaded-tests/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright 2018, 2021 Jorel Ali (Skepter) - MIT License Permission is
hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software
without restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies of the
Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions: The above copyright notice and this
permission notice shall be included in all copies or substantial portions
of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE. -->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>dev.jorel</groupId>
<artifactId>commandapi</artifactId>
<version>9.6.0-SNAPSHOT</version>
</parent>

<artifactId>commandapi-annotations-reloaded-tests</artifactId>

<properties>
<maven.compiler.source>16</maven.compiler.source>
<maven.compiler.target>16</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<repositories>
<repository>
<id>codemc-repo</id>
<url>https://repo.codemc.org/repository/maven-public/</url>
<layout>default</layout>
</repository>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository>

<!-- Testing -->
<repository>
<id>minecraft-libraries</id>
<url>https://libraries.minecraft.net</url>
</repository>
</repositories>

<dependencies>

<!-- Testing (classes that the CommandAPI needs) -->
<dependency>
<groupId>com.mojang</groupId>
<artifactId>brigadier</artifactId>
<version>1.0.18</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>dev.jorel</groupId>
<artifactId>commandapi-bukkit-plugin</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>

<!-- Main dev dependencies -->
<dependency>
<groupId>dev.jorel</groupId>
<artifactId>commandapi-bukkit-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.jorel</groupId>
<artifactId>commandapi-annotations-reloaded</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>

<dependency>
<groupId>com.google.auto.service</groupId>
<artifactId>auto-service</artifactId>
<version>${auto-service.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>${paper.version}</version>
<scope>provided</scope>
</dependency>
<!-- <dependency>
<groupId>de.tr7zw</groupId>
<artifactId>item-nbt-api-plugin</artifactId>
<version>${nbtapi.version}</version>
</dependency> -->

<!-- Testing -->
<dependency>
<groupId>com.google.testing.compile</groupId>
<artifactId>compile-testing</artifactId>
<version>0.19</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>testCompile</goal>
</goals>
<configuration>
<annotationProcessors>
<annotationProcessor>dev.jorel.commandapi.annotations.reloaded.Annotations</annotationProcessor>
</annotationProcessors>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M6</version>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
/*******************************************************************************
* Copyright 2018, 2021 Jorel Ali (Skepter) - MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*******************************************************************************/
package dev.jorel.commandapi.annotations.reloaded.test;

import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

import dev.jorel.commandapi.annotations.reloaded.annotations.Executes;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.NamespacedKey;
import org.bukkit.boss.BarColor;
import org.bukkit.boss.BarStyle;
import org.bukkit.boss.BossBar;
import org.bukkit.boss.KeyedBossBar;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;

import dev.jorel.commandapi.CommandAPI;
import dev.jorel.commandapi.annotations.reloaded.annotations.Command;
import dev.jorel.commandapi.annotations.reloaded.annotations.Subcommand;
import dev.jorel.commandapi.annotations.reloaded.arguments.ABooleanArgument;
import dev.jorel.commandapi.annotations.reloaded.arguments.AChatComponentArgument;
import dev.jorel.commandapi.annotations.reloaded.arguments.AEntitySelectorArgument;
import dev.jorel.commandapi.annotations.reloaded.arguments.AIntegerArgument;
import dev.jorel.commandapi.annotations.reloaded.arguments.AMultiLiteralArgument;
import dev.jorel.commandapi.annotations.reloaded.arguments.ANamespacedKeyArgument;
import dev.jorel.commandapi.exceptions.WrapperCommandSyntaxException;
import net.md_5.bungee.api.chat.BaseComponent;

/**
* The annotated equivalent of BetterBossBars from the
* examples/bukkit/commandtrees directory
*/
@Command("betterbossbar")
public class AnnotatedCommandBossbar {

// /betterbossbar set <id> players <targets>
// /betterbossbar set <id> style (notched_6|notched_10|notched_12|notched_20|progress)
// /betterbossbar set <id> value <value>
// /betterbossbar set <id> visible <visible>

// /betterbossbar remove <id>

// /betterbossbar get <id> players
// /betterbossbar get <id> visible
// /betterbossbar get <id> max
// /betterbossbar get <id> value

// /betterbossbar add <id> <name>

// /betterbossbar list

private Map<NamespacedKey, Integer> maxValues;

public AnnotatedCommandBossbar() {
this.maxValues = new HashMap<>();
}

@Subcommand("set")
class SetSubcommand {

@ANamespacedKeyArgument
NamespacedKey id;

@Subcommand("players")
public void players(CommandSender sender,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should have the @Executes annotation
Same with a lot of these executor methods.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From reading the spec doc, I'm not entirely clear on the purpose of @Executes over @Subcommand or why both would need to be included. What does this actually mean and how should it affect the generated code?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, @Executes is supposed to mark a method as executable.
The usage of @Subcommand can change a bit. You can either use this multiple times on an inner class to not create too many nested classes, or you actually do all those nested class and put one @Subcommand per inner class.
Or, in cases like this one, you can omit the inner class and put the @Subcommand annotation on the method - alongside @Executes

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking about what is nicest for the user then, I think the @Executes on this method can be inferred from the presence of @Subcommand. The intention is clear, so it'd be better if this just works rather than requiring them to add an extra annotation to make it work.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, but I don't think it should be that way since the way the @Subcommand annotation is used here is syntactic sugar for this

@Command
public class Blah {
    @Subcommand
    public class BlahSub {
        @Subcommand
        @Executes
        public void executorMethod(Player player) {}
    }
}

instead of this:

@Command
public class Blah {
    @Subcommand
    public class BlahSub {
        @Subcommand
        public class BlahSubSub {
            @Executes
            public void executorMethod(Player player) {}
        }
    }
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like I am missing something. Yes, it's syntactic sugar. I don't see why that means we should make the user add an extra annotation that can be inferred. What problem does this cause?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, my point is that because it's syntactic sugar we should require the extra @Executes annotation.
I don't know, maybe that's just my personal preference or maybe I just want to stick to the spec.
Maybe we just need other opinions. I think I've stated mine.

@AEntitySelectorArgument.ManyPlayers Collection<Player> targets) {

Bukkit.getBossBar(id).removeAll();
for (Player player : targets) {
Bukkit.getBossBar(id).addPlayer(player);
}
}

@Subcommand("style")
public void style(CommandSender sender,
@AMultiLiteralArgument({ "notched_6", "notched_10", "notched_12", "notched_20", "progress" }) String style) throws WrapperCommandSyntaxException {

Bukkit.getBossBar(id).setStyle(
switch (style) {
case "notched_6" -> BarStyle.SEGMENTED_6;
case "notched_10" -> BarStyle.SEGMENTED_10;
case "notched_12" -> BarStyle.SEGMENTED_12;
case "notched_20" -> BarStyle.SEGMENTED_20;
case "progress" -> BarStyle.SOLID;
default -> throw CommandAPI.failWithString(style + " is an invalid bossbar style");
});
}

@Subcommand("value")
public void value(CommandSender sender,
@AIntegerArgument int value) {

if (maxValues.containsKey(id)) {
Bukkit.getBossBar(id).setProgress((double) value / (double) maxValues.get(id));
} else {
maxValues.put(id, value);
Bukkit.getBossBar(id).setProgress(1.0D);
}
}

@Subcommand("visible")
public void visible(CommandSender sender,
@ABooleanArgument boolean visible) {

Bukkit.getBossBar(id).setVisible(visible);
}

}

@Subcommand("remove")
public void remove(CommandSender sender,
@ANamespacedKeyArgument NamespacedKey id) {

sender.sendMessage("Removed custom bossbar [" + Bukkit.getBossBar(id).getTitle() + "]");
Bukkit.getBossBar(id).removeAll();
Bukkit.removeBossBar(id);
}

@Subcommand("get")
class GetSubcommand {

@ANamespacedKeyArgument
NamespacedKey id;

@Subcommand("players")
public void players(CommandSender sender) {
List<Player> bossBarPlayers = Bukkit.getBossBar(id).getPlayers();
String players = bossBarPlayers.stream().map(Player::getName).collect(Collectors.joining(", "));
sender.sendMessage("Custom bossbar [" + Bukkit.getBossBar(id).getTitle() + "] has " + bossBarPlayers.size() + " players currently online: " + players);
}

@Subcommand("visible")
public void visible(CommandSender sender) {
BossBar bossBar = Bukkit.getBossBar(id);
sender.sendMessage("Custom bossbar [" + bossBar.getTitle() + "] is currently " + (bossBar.isVisible() ? "shown" : "hidden"));
}

@Subcommand("max")
public void max(CommandSender sender) {
sender.sendMessage("Custom bossbar [" + Bukkit.getBossBar(id).getTitle() + "] has a maximum of " + maxValues.getOrDefault(id, 100));
}

@Subcommand("value")
public void value(CommandSender sender) {
BossBar bossBar = Bukkit.getBossBar(id);

int value = (int) (bossBar.getProgress() * maxValues.getOrDefault(id, 100));
sender.sendMessage("Custom bossbar [" + Bukkit.getBossBar(id).getTitle() + "] has a value of " + value);
}
}

@Subcommand("add")
class AddSubcommand {

@Executes
public void addBossbar(CommandSender sender,
@ANamespacedKeyArgument NamespacedKey id,
@AChatComponentArgument BaseComponent[] name) {

Bukkit.createBossBar(id, BaseComponent.toLegacyText(name), BarColor.WHITE, BarStyle.SOLID);
maxValues.put(id, 100);
sender.sendMessage("Created custom bossbar [" + BaseComponent.toLegacyText(name) + ChatColor.WHITE + "]");
}

}

@Subcommand("list")
public void ListSubcommand(CommandSender sender) {
Iterable<KeyedBossBar> bossBars = Bukkit::getBossBars;
sender.sendMessage("List of custom bossbars: " +
StreamSupport
.stream(bossBars.spliterator(), false)
.map(KeyedBossBar::getKey)
.map(NamespacedKey::toString)
.collect(Collectors.joining(", "))
);
}

}
Loading
Loading