From 3632de6cb14505cd0e20cf3943b93030205c1a77 Mon Sep 17 00:00:00 2001 From: Joshua Date: Sat, 21 Mar 2020 19:58:05 -0400 Subject: [PATCH 01/54] started work on maven plugin which auto generates main plugin class and plugin.yml and adds it to the generated-sources directory --- .../mcspring/annotation/PluginDepend.java | 13 +++ .../mcspring/economy/VaultEconomyService.java | 3 + .../processor/util/MainClassCreator.java | 1 + .../mcspring-maven-plugin-test/pom.xml | 72 ++++++++++++++ .../test/StartupMessagePlugin.java | 18 ++++ .../mcspring/testpackage/TestComponent.java | 7 ++ mcspring-build/mcspring-maven-plugin/pom.xml | 66 +++++++++++++ .../autogenerator/AutoGenerationPlugin.java | 89 ++++++++++++++++++ .../PluginDependencyResolver.java | 92 ++++++++++++++++++ .../PluginMainClassGenerator.java | 46 +++++++++ .../autogenerator/PluginYamlAttributes.java | 77 +++++++++++++++ .../autogenerator/ProjectClassScanner.java | 93 +++++++++++++++++++ .../src/main/resources/MainTemplate.java | 35 +++++++ mcspring-build/pom.xml | 2 + pom.xml | 12 +++ 15 files changed, 626 insertions(+) create mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/annotation/PluginDepend.java create mode 100644 mcspring-build/mcspring-maven-plugin-test/pom.xml create mode 100644 mcspring-build/mcspring-maven-plugin-test/src/main/java/in/kyle/mcspring/mavenplugin/test/StartupMessagePlugin.java create mode 100644 mcspring-build/mcspring-maven-plugin-test/src/main/java/in/kyle/mcspring/testpackage/TestComponent.java create mode 100644 mcspring-build/mcspring-maven-plugin/pom.xml create mode 100644 mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/AutoGenerationPlugin.java create mode 100644 mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginDependencyResolver.java create mode 100644 mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginMainClassGenerator.java create mode 100644 mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginYamlAttributes.java create mode 100644 mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/ProjectClassScanner.java create mode 100644 mcspring-build/mcspring-maven-plugin/src/main/resources/MainTemplate.java diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/annotation/PluginDepend.java b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/annotation/PluginDepend.java new file mode 100644 index 0000000..ee2cb46 --- /dev/null +++ b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/annotation/PluginDepend.java @@ -0,0 +1,13 @@ +package in.kyle.mcspring.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface PluginDepend { + String[] plugins(); + boolean soft() default false; +} diff --git a/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/VaultEconomyService.java b/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/VaultEconomyService.java index d09b204..06809e0 100644 --- a/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/VaultEconomyService.java +++ b/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/VaultEconomyService.java @@ -1,11 +1,13 @@ package in.kyle.mcspring.economy; +import in.kyle.mcspring.annotation.PluginDepend; import net.milkbowl.vault.economy.Economy; import net.milkbowl.vault.economy.EconomyResponse; import org.bukkit.OfflinePlayer; import org.bukkit.Server; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.context.annotation.DependsOn; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; @@ -14,6 +16,7 @@ @Lazy @Service @ConditionalOnClass(Economy.class) +@PluginDepend(plugins = "Vault") class VaultEconomyService implements EconomyService { private final Economy economy; diff --git a/mcspring-build/mcspring-annotations/src/main/java/in/kyle/mcspring/processor/util/MainClassCreator.java b/mcspring-build/mcspring-annotations/src/main/java/in/kyle/mcspring/processor/util/MainClassCreator.java index a545509..f17a46c 100644 --- a/mcspring-build/mcspring-annotations/src/main/java/in/kyle/mcspring/processor/util/MainClassCreator.java +++ b/mcspring-build/mcspring-annotations/src/main/java/in/kyle/mcspring/processor/util/MainClassCreator.java @@ -12,6 +12,7 @@ import javax.tools.FileObject; public class MainClassCreator { + public static void generateMain(FileObject main, String fqn, String packageName, diff --git a/mcspring-build/mcspring-maven-plugin-test/pom.xml b/mcspring-build/mcspring-maven-plugin-test/pom.xml new file mode 100644 index 0000000..4907be7 --- /dev/null +++ b/mcspring-build/mcspring-maven-plugin-test/pom.xml @@ -0,0 +1,72 @@ + + + + + mcspring-starter + in.kyle.mcspring + 0.0.9 + + + 4.0.0 + + mcspring-maven-plugin-test + + + 1.15.1-R0.1-SNAPSHOT + 8 + 8 + + + + + in.kyle.mcspring + mcspring-base + + + in.kyle.mcspring + mcspring-vault + ${project.version} + + + org.projectlombok + lombok + + + + + + in.kyle.mcspring + mcspring-maven-plugin + ${project.version} + + + + mcspring-auto-generator + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.0.0 + + + generate-sources + + add-source + + + + ${project.build.directory}/generated-sources/org + ${project.build.directory}/generated-sources/plugin.yml + + + + + + + + \ No newline at end of file diff --git a/mcspring-build/mcspring-maven-plugin-test/src/main/java/in/kyle/mcspring/mavenplugin/test/StartupMessagePlugin.java b/mcspring-build/mcspring-maven-plugin-test/src/main/java/in/kyle/mcspring/mavenplugin/test/StartupMessagePlugin.java new file mode 100644 index 0000000..e97ef34 --- /dev/null +++ b/mcspring-build/mcspring-maven-plugin-test/src/main/java/in/kyle/mcspring/mavenplugin/test/StartupMessagePlugin.java @@ -0,0 +1,18 @@ +package in.kyle.mcspring.mavenplugin.test; + +import in.kyle.mcspring.annotation.PluginDepend; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; + +@Component +@PluginDepend(plugins = "Vault", soft = true) +public class StartupMessagePlugin { + + @PostConstruct + void message() { + Player player = Bukkit.getPlayer(""); + } +} diff --git a/mcspring-build/mcspring-maven-plugin-test/src/main/java/in/kyle/mcspring/testpackage/TestComponent.java b/mcspring-build/mcspring-maven-plugin-test/src/main/java/in/kyle/mcspring/testpackage/TestComponent.java new file mode 100644 index 0000000..e321e35 --- /dev/null +++ b/mcspring-build/mcspring-maven-plugin-test/src/main/java/in/kyle/mcspring/testpackage/TestComponent.java @@ -0,0 +1,7 @@ +package in.kyle.mcspring.testpackage; + +import org.springframework.stereotype.Component; + +@Component +public class TestComponent { +} diff --git a/mcspring-build/mcspring-maven-plugin/pom.xml b/mcspring-build/mcspring-maven-plugin/pom.xml new file mode 100644 index 0000000..5ca4479 --- /dev/null +++ b/mcspring-build/mcspring-maven-plugin/pom.xml @@ -0,0 +1,66 @@ + + + + mcspring-build + in.kyle.mcspring + 0.0.9 + + + 4.0.0 + mcspring-maven-plugin + maven-plugin + + mcspring-auto-generator + + + + org.apache.maven + maven-plugin-api + 3.6.2 + + + org.apache.maven.plugin-tools + maven-plugin-annotations + 3.6.0 + provided + + + org.yaml + snakeyaml + 1.26 + + + org.apache.maven + maven-project + 3.0-alpha-2 + + + in.kyle.mcspring + mcspring-base + ${project.version} + + + org.projectlombok + lombok + + + + + + + org.apache.maven.plugins + maven-plugin-plugin + 3.6.0 + + + org.apache.maven.plugins + maven-compiler-plugin + + 11 + 11 + + + + + diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/AutoGenerationPlugin.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/AutoGenerationPlugin.java new file mode 100644 index 0000000..f804214 --- /dev/null +++ b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/AutoGenerationPlugin.java @@ -0,0 +1,89 @@ +package in.kyle.mcspring.autogenerator; + +import lombok.SneakyThrows; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.apache.maven.project.MavenProject; + +import java.io.File; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + + +@Mojo(name = "mcspring-auto-generator", defaultPhase = LifecyclePhase.TEST, + requiresDependencyCollection = ResolutionScope.COMPILE_PLUS_RUNTIME, + requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME) +public class AutoGenerationPlugin extends AbstractMojo { + + public static final String ROOT_PACKAGE = "org.springframework.boot.loader"; + private static final List VALID_SCOPES = Arrays.asList("provided", "compile", "runtime"); + @Parameter(defaultValue = "${project}", required = true) + private MavenProject project; + + public static String getMainClassLocation(MavenProject project) { + String groupId = project.getGroupId().replace("-", ""); + String artifactId = project.getArtifactId().replace("-", ""); + return AutoGenerationPlugin.ROOT_PACKAGE + "." + groupId + "." + artifactId; + } + + @SneakyThrows + public void execute() { + preparePluginYml(); + preparePluginMainClass(); + } + + private void preparePluginYml() { + getLog().info("Scanning for project dependencies in qualifying scope"); + Set artifacts = getDependencyArtifacts(); + getLog().info(String.format("Dependency scan complete. Found %d dependencies", artifacts.size())); + PluginDependencyResolver resolver = new PluginDependencyResolver(artifacts); + PluginYamlAttributes attributes = new PluginYamlAttributes(project, resolver, getLog()); + attributes.loadAttributes(); + getLog().info("Finished obtaining data for plugin.yml"); + getLog().info("======================================="); + attributes.getAttributes().forEach((key, data) -> getLog().info(key + ": " + data.toString())); + getLog().info("======================================="); + getLog().info("Writing plugin.yml to generated-sources"); + File pluginFile = new File(getGeneratedSourcesFolder(), "plugin.yml"); + attributes.writeToFile(pluginFile); + getLog().info("Write completed"); + } + + private void preparePluginMainClass() { + PluginDependencyResolver resolver = new PluginDependencyResolver(getDependencyArtifacts()); + getLog().info("Scanning project sources for spring annotations"); + ProjectClassScanner scanner = new ProjectClassScanner(getSourceClassesFolder(), resolver.getDependencyURLs()); + scanner.findPackages(); + Set packages = scanner.getPackagesThatUseSpring(); + getLog().info(String.format("Scan complete. Found %d packages with spring annotation", packages.size())); + getLog().info("Preparing to generate main class"); + File destination = new File(getGeneratedSourcesFolder(), getMainClassLocation(project).replace(".", "/").concat(".java")); + destination.getParentFile().mkdirs(); + PluginMainClassGenerator generator = new PluginMainClassGenerator(project.getName().replace("-", ""), packages, destination); + generator.generate(); + getLog().info("Default main class has been added to generated-sources"); + getLog().info("Auto generation process complete"); + } + + private File getSourceClassesFolder() { + return new File(project.getBasedir(), "/target/classes/"); + } + + private File getGeneratedSourcesFolder() { + return new File(project.getBasedir(), "/target/generated-sources/"); + } + + @SuppressWarnings("unchecked") + private Set getDependencyArtifacts() { + Set artifacts = project.getArtifacts(); + return artifacts.stream() + .filter(artifact -> VALID_SCOPES.contains(artifact.getScope())) + .collect(Collectors.toSet()); + } +} diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginDependencyResolver.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginDependencyResolver.java new file mode 100644 index 0000000..824160b --- /dev/null +++ b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginDependencyResolver.java @@ -0,0 +1,92 @@ +package in.kyle.mcspring.autogenerator; + +import in.kyle.mcspring.annotation.PluginDepend; +import lombok.SneakyThrows; +import org.apache.maven.artifact.Artifact; + +import java.io.File; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Enumeration; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.stream.Collectors; + +public class PluginDependencyResolver { + + private final List jars; + + public PluginDependencyResolver(Collection dependencies) { + this.jars = dependencies.stream().map(Artifact::getFile).collect(Collectors.toList()); + } + + public List getDependencyURLs() { + return jars.stream().map(this::createJarFileURL).collect(Collectors.toList()); + } + + public List resolveAllDependencies() { + List dependencies = new ArrayList<>(); + URLClassLoader classLoader = createDependencyClassLoader(); + jars.forEach(file -> dependencies.addAll(findDependencies(file, classLoader))); + return dependencies; + } + + @SneakyThrows + private List findDependencies(File file, URLClassLoader classLoader) { + JarFile jarFile = new JarFile(file); + return findDependencies(jarFile, classLoader); + } + + @SneakyThrows + private List findDependencies(JarFile jarFile, URLClassLoader classLoader) { + List dependList = new ArrayList<>(); + Enumeration enumeration = jarFile.entries(); + while(enumeration.hasMoreElements()) { + JarEntry entry = enumeration.nextElement(); + if(isClassFile(entry)) { + String className = getClassName(entry); + addDependencyIfPresent(className, classLoader, dependList); + } + } + return dependList; + } + + private void addDependencyIfPresent(String className, URLClassLoader loader, List destination) { + try { + Class clazz = loader.loadClass(className); + if(clazz.isAnnotationPresent(PluginDepend.class)) { + destination.add(clazz.getAnnotation(PluginDepend.class)); + } + //Catching a throwable for optional dependencies not being present. + //It throws a ClassNotFoundException but for some reason we can't catch + //then handle it because WONK. + } catch (Throwable ignored) { + + } + } + + @SneakyThrows + private URLClassLoader createDependencyClassLoader() { + ClassLoader parent = getClass().getClassLoader(); + URL[] urls = getDependencyURLs().toArray(URL[]::new); + return new URLClassLoader(urls, parent); + } + + @SneakyThrows + private URL createJarFileURL(File jarFile) { + return new URL("jar:file:" + jarFile.getPath() + "!/"); + } + + private boolean isClassFile(JarEntry entry) { + return entry.getName().endsWith(".class"); + } + + private String getClassName(JarEntry entry) { + String fullName = entry.getName(); + String excludeClassExtension = fullName.substring(0, fullName.length() - 6); + return excludeClassExtension.replace("/", "."); + } +} diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginMainClassGenerator.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginMainClassGenerator.java new file mode 100644 index 0000000..a988311 --- /dev/null +++ b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginMainClassGenerator.java @@ -0,0 +1,46 @@ +package in.kyle.mcspring.autogenerator; + +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; + +import java.io.*; +import java.util.Set; +import java.util.stream.Collectors; + +@RequiredArgsConstructor +public class PluginMainClassGenerator { + + private final String mainClassName; + private final Set packages; + private final File destination; + + public void generate() { + String templateContent = getTemplateContent(); + templateContent = templateContent.replace("{name}", mainClassName); + templateContent = templateContent.replace("{scans}", createPackageScanList()); + write(templateContent); + } + + private String createPackageScanList() { + return packages.stream() + .map(s -> String.format("\"%s\"", s)) + .collect(Collectors.joining(",\n ")); + } + + @SneakyThrows + private void write(String completedTemplate) { + FileWriter writer = new FileWriter(destination); + writer.write(completedTemplate); + writer.flush(); + writer.close(); + } + + private String getTemplateContent() { + InputStream resource = PluginMainClassGenerator.class.getResourceAsStream("/MainTemplate.java"); + InputStreamReader inputStreamReader = new InputStreamReader(resource); + BufferedReader bufferedReader = new BufferedReader(inputStreamReader); + StringBuilder builder = new StringBuilder(); + bufferedReader.lines().forEach(line -> builder.append(line).append("\n")); + return builder.toString(); + } +} diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginYamlAttributes.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginYamlAttributes.java new file mode 100644 index 0000000..7a0141d --- /dev/null +++ b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginYamlAttributes.java @@ -0,0 +1,77 @@ +package in.kyle.mcspring.autogenerator; + +import in.kyle.mcspring.annotation.PluginDepend; +import lombok.SneakyThrows; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.logging.Log; +import org.apache.maven.project.MavenProject; +import org.yaml.snakeyaml.Yaml; + +import java.io.File; +import java.io.FileWriter; +import java.util.*; +import java.util.stream.Collectors; + +@SuppressWarnings("unchecked") +public class PluginYamlAttributes { + + private static final String NAME = "name"; + private static final String MAIN = "main"; + private static final String VERSION = "version"; + private static final String DESC = "description"; + private static final String SOFT_DEPENDENCIES = "softdepend"; + private static final String REQUIRED_DEPENDENCIES = "depend"; + + private final Map attributes = new HashMap<>(); + private final MavenProject project; + private final PluginDependencyResolver resolver; + private final Log logger; + + PluginYamlAttributes(MavenProject project, PluginDependencyResolver resolver, Log logger) { + this.project = project; + this.resolver = resolver; + this.logger = logger; + attributes.put(SOFT_DEPENDENCIES, new ArrayList<>()); + attributes.put(REQUIRED_DEPENDENCIES, new ArrayList<>()); + } + + public Map getAttributes() { + return Collections.unmodifiableMap(attributes); + } + + public void loadAttributes() { + attributes.put(NAME, project.getName()); + attributes.put(MAIN, AutoGenerationPlugin.getMainClassLocation(project)); + attributes.put(VERSION, project.getVersion()); + attributes.put(DESC, project.getDescription()); + setDependencies(); + } + + private void setDependencies() { + logger.info("Scanning dependency classes for PluginDepend annotation"); + List list = resolver.resolveAllDependencies(); + logger.info(String.format("Scan complete. Found %d classes with PluginDepend annotation", list.size())); + list.forEach(this::addDependency); + } + + private List getDependencyList(String type) { + return (List) attributes.get(type); + } + + private void addDependency(PluginDepend depend) { + List plugins = Arrays.stream(depend.plugins()).collect(Collectors.toList()); + if(depend.soft()) { + getDependencyList(SOFT_DEPENDENCIES).addAll(plugins); + } else { + getDependencyList(REQUIRED_DEPENDENCIES).addAll(plugins); + } + } + + @SneakyThrows + public void writeToFile(File destination) { + FileWriter writer = new FileWriter(destination); + Yaml yaml = new Yaml(); + //Pretty sure #dump closes the writer automatically + yaml.dump(attributes, writer); + } +} diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/ProjectClassScanner.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/ProjectClassScanner.java new file mode 100644 index 0000000..4ca11f3 --- /dev/null +++ b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/ProjectClassScanner.java @@ -0,0 +1,93 @@ +package in.kyle.mcspring.autogenerator; + +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Controller; +import org.springframework.stereotype.Service; + +import java.io.File; +import java.lang.annotation.Annotation; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.*; +import java.util.stream.Collectors; + + +//Objective: Scan all classes in the provider folder and check for spring annotations. +//If present, get the class package and add it to a list for springs package scan at runtime +@RequiredArgsConstructor +class ProjectClassScanner { + + private static final List> SPRING_ANNOTATIONS = Arrays.asList( + Service.class, + Controller.class, + Component.class + ); + + private final File classesFolder; + private final List projectDependencyJars; + private final Set packages = new HashSet<>(); + + public Set getPackagesThatUseSpring() { + return Collections.unmodifiableSet(packages); + } + + public void findPackages() { + List classFiles = getClassFiles(); + URLClassLoader classLoader = createClassLoader(); + classFiles.forEach(file -> addPackageIfFound(classLoader, file)); + } + + private void addPackageIfFound(URLClassLoader classLoader, File classFile) { + String className = getClassName(classFile); + try { + Class clazz = classLoader.loadClass(className); + if(hasSpringAnnotation(clazz)) { + packages.add(clazz.getPackage().getName()); + } + } catch (Throwable ignored){ + //Proper error handling here. + } + } + + + private String getClassName(File classFile) { + String fullPath = classFile.getPath(); + String classNameWithPackage = fullPath.replace(classesFolder.getPath(), "").replace("\\", "."); + return classNameWithPackage.substring(1, classNameWithPackage.length() - 6); + } + + private List getClassFiles() { + return getFilesInDirectory(classesFolder) + .stream() + .filter(file -> file.getName().endsWith(".class")) + .collect(Collectors.toList()); + } + + private boolean hasSpringAnnotation(Class clazz) { + return SPRING_ANNOTATIONS.stream().anyMatch(clazz::isAnnotationPresent); + } + + @SneakyThrows + private URLClassLoader createClassLoader() { + List classPathList = new ArrayList<>(projectDependencyJars); + classPathList.add(classesFolder.toURI().toURL()); + return new URLClassLoader(classPathList.toArray(new URL[0]), getClass().getClassLoader()); + } + + private List getFilesInDirectory(File directory) { + List files = new ArrayList<>(); + File[] filesInDirectory = directory.listFiles(); + if(filesInDirectory != null) { + for(File file : filesInDirectory) { + if(file.isDirectory()) { + files.addAll(getFilesInDirectory(file)); + } else { + files.add(file); + } + } + } + return files; + } +} diff --git a/mcspring-build/mcspring-maven-plugin/src/main/resources/MainTemplate.java b/mcspring-build/mcspring-maven-plugin/src/main/resources/MainTemplate.java new file mode 100644 index 0000000..8ce5644 --- /dev/null +++ b/mcspring-build/mcspring-maven-plugin/src/main/resources/MainTemplate.java @@ -0,0 +1,35 @@ +import org.springframework.boot.autoconfigure.SpringBootApplication; + +// @formatter::off +import org.bukkit.plugin.java.JavaPlugin; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.loader.mcspring.McSpringLoader; + +import in.kyle.mcspring.SpringPlugin; +// @formatter::on + +public class {name} extends JavaPlugin { + + private McSpringLoader loader; + + public void onEnable() { + try { + new McSpringLoader().launch(getClassLoader()); + SpringPlugin.setup(this, MainPluginConfig.class); + } catch (Exception ignored){ + getLogger().warning("MCSpring Failed to load " + getName()); + // error will be logged by Spring + } + } + + public void onDisable() { + SpringPlugin.teardown(this); + //if (loader != null) loader.close(); + loader = null; + } + + @SpringBootApplication(scanBasePackages = {{scans}}) + static class MainPluginConfig { + + } +} diff --git a/mcspring-build/pom.xml b/mcspring-build/pom.xml index 328dfbf..87c83d3 100644 --- a/mcspring-build/pom.xml +++ b/mcspring-build/pom.xml @@ -15,6 +15,8 @@ mcspring-archetype mcspring-annotations mcspring-starter + mcspring-maven-plugin + mcspring-maven-plugin-test 4.0.0 diff --git a/pom.xml b/pom.xml index f3ae3b8..acd9f6f 100644 --- a/pom.xml +++ b/pom.xml @@ -149,6 +149,18 @@ + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.0.0-M3 + + + + + + + From 3e7ae015833660c834b28c50b8be243283468aff Mon Sep 17 00:00:00 2001 From: Joshua Date: Mon, 6 Apr 2020 19:57:59 -0400 Subject: [PATCH 02/54] fixed issue where generated main class was not compiling into final jar --- .../mcspring-maven-plugin-test/pom.xml | 5 +-- .../autogenerator/AutoGenerationPlugin.java | 43 ++++++++++++------- .../autogenerator/MainClassUtilities.java | 23 ++++++++++ .../PluginMainClassGenerator.java | 9 +++- .../autogenerator/PluginYamlAttributes.java | 3 +- 5 files changed, 61 insertions(+), 22 deletions(-) create mode 100644 mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/MainClassUtilities.java diff --git a/mcspring-build/mcspring-maven-plugin-test/pom.xml b/mcspring-build/mcspring-maven-plugin-test/pom.xml index 4907be7..5a9f2ea 100644 --- a/mcspring-build/mcspring-maven-plugin-test/pom.xml +++ b/mcspring-build/mcspring-maven-plugin-test/pom.xml @@ -54,14 +54,13 @@ 3.0.0 - generate-sources + generate-resources add-source - ${project.build.directory}/generated-sources/org - ${project.build.directory}/generated-sources/plugin.yml + ${project.build.directory}/generated-sources/mc-spring/ diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/AutoGenerationPlugin.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/AutoGenerationPlugin.java index f804214..4ca20da 100644 --- a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/AutoGenerationPlugin.java +++ b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/AutoGenerationPlugin.java @@ -16,28 +16,34 @@ import java.util.stream.Collectors; -@Mojo(name = "mcspring-auto-generator", defaultPhase = LifecyclePhase.TEST, +@Mojo(name = "mcspring-auto-generator", defaultPhase = LifecyclePhase.PROCESS_CLASSES, requiresDependencyCollection = ResolutionScope.COMPILE_PLUS_RUNTIME, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME) public class AutoGenerationPlugin extends AbstractMojo { - public static final String ROOT_PACKAGE = "org.springframework.boot.loader"; private static final List VALID_SCOPES = Arrays.asList("provided", "compile", "runtime"); @Parameter(defaultValue = "${project}", required = true) private MavenProject project; - public static String getMainClassLocation(MavenProject project) { - String groupId = project.getGroupId().replace("-", ""); - String artifactId = project.getArtifactId().replace("-", ""); - return AutoGenerationPlugin.ROOT_PACKAGE + "." + groupId + "." + artifactId; - } - @SneakyThrows public void execute() { + addGeneratedSourcesDirectory(); preparePluginYml(); preparePluginMainClass(); } + private File getSourcesOutputDirectory() { + return new File(getGeneratedSourcesFolder(), "mc-spring/"); + } + + private void addGeneratedSourcesDirectory() { + File output = getSourcesOutputDirectory(); + if(!output.exists()) { + output.mkdirs(); + } + project.addCompileSourceRoot(output.getPath()); + } + private void preparePluginYml() { getLog().info("Scanning for project dependencies in qualifying scope"); Set artifacts = getDependencyArtifacts(); @@ -46,11 +52,11 @@ private void preparePluginYml() { PluginYamlAttributes attributes = new PluginYamlAttributes(project, resolver, getLog()); attributes.loadAttributes(); getLog().info("Finished obtaining data for plugin.yml"); - getLog().info("======================================="); + getLog().info("----------------------------------------------------------------"); attributes.getAttributes().forEach((key, data) -> getLog().info(key + ": " + data.toString())); - getLog().info("======================================="); + getLog().info("----------------------------------------------------------------"); getLog().info("Writing plugin.yml to generated-sources"); - File pluginFile = new File(getGeneratedSourcesFolder(), "plugin.yml"); + File pluginFile = new File(getSourcesOutputDirectory(), "plugin.yml"); attributes.writeToFile(pluginFile); getLog().info("Write completed"); } @@ -63,14 +69,21 @@ private void preparePluginMainClass() { Set packages = scanner.getPackagesThatUseSpring(); getLog().info(String.format("Scan complete. Found %d packages with spring annotation", packages.size())); getLog().info("Preparing to generate main class"); - File destination = new File(getGeneratedSourcesFolder(), getMainClassLocation(project).replace(".", "/").concat(".java")); - destination.getParentFile().mkdirs(); - PluginMainClassGenerator generator = new PluginMainClassGenerator(project.getName().replace("-", ""), packages, destination); - generator.generate(); + writePluginMain(packages); getLog().info("Default main class has been added to generated-sources"); getLog().info("Auto generation process complete"); } + private void writePluginMain(Set packages) { + String mainClass = MainClassUtilities.getMainClassLocation(project); + File destination = new File(getSourcesOutputDirectory(), mainClass.replace(".", "/").concat(".java")); + if(!destination.getParentFile().exists()) { + destination.getParentFile().mkdirs(); + } + PluginMainClassGenerator generator = new PluginMainClassGenerator(project, packages, destination); + generator.generate(); + } + private File getSourceClassesFolder() { return new File(project.getBasedir(), "/target/classes/"); } diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/MainClassUtilities.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/MainClassUtilities.java new file mode 100644 index 0000000..b4e9c39 --- /dev/null +++ b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/MainClassUtilities.java @@ -0,0 +1,23 @@ +package in.kyle.mcspring.autogenerator; + +import lombok.experimental.UtilityClass; +import org.apache.maven.project.MavenProject; + +@UtilityClass +public class MainClassUtilities { + + public static final String ROOT_PACKAGE = "org.springframework.boot.loader"; + + public static String getMainClassName(MavenProject project) { + return project.getArtifactId().replace("-", ""); + } + + public static String getMainClassLocation(MavenProject project) { + return getMainClassPackage(project) + "." + getMainClassName(project); + } + + public static String getMainClassPackage(MavenProject project) { + String groupId = project.getGroupId().replace("-", ""); + return ROOT_PACKAGE + "." + groupId; + } +} diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginMainClassGenerator.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginMainClassGenerator.java index a988311..8ee0ac6 100644 --- a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginMainClassGenerator.java +++ b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginMainClassGenerator.java @@ -2,6 +2,7 @@ import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; +import org.apache.maven.project.MavenProject; import java.io.*; import java.util.Set; @@ -10,13 +11,13 @@ @RequiredArgsConstructor public class PluginMainClassGenerator { - private final String mainClassName; + private final MavenProject project; private final Set packages; private final File destination; public void generate() { String templateContent = getTemplateContent(); - templateContent = templateContent.replace("{name}", mainClassName); + templateContent = templateContent.replace("{name}", MainClassUtilities.getMainClassName(project)); templateContent = templateContent.replace("{scans}", createPackageScanList()); write(templateContent); } @@ -40,6 +41,10 @@ private String getTemplateContent() { InputStreamReader inputStreamReader = new InputStreamReader(resource); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); StringBuilder builder = new StringBuilder(); + builder.append("package ") + .append(MainClassUtilities.getMainClassPackage(project)) + .append(";") + .append("\n"); bufferedReader.lines().forEach(line -> builder.append(line).append("\n")); return builder.toString(); } diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginYamlAttributes.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginYamlAttributes.java index 7a0141d..7fe4aed 100644 --- a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginYamlAttributes.java +++ b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginYamlAttributes.java @@ -2,7 +2,6 @@ import in.kyle.mcspring.annotation.PluginDepend; import lombok.SneakyThrows; -import org.apache.maven.artifact.Artifact; import org.apache.maven.plugin.logging.Log; import org.apache.maven.project.MavenProject; import org.yaml.snakeyaml.Yaml; @@ -41,7 +40,7 @@ public Map getAttributes() { public void loadAttributes() { attributes.put(NAME, project.getName()); - attributes.put(MAIN, AutoGenerationPlugin.getMainClassLocation(project)); + attributes.put(MAIN, MainClassUtilities.getMainClassLocation(project)); attributes.put(VERSION, project.getVersion()); attributes.put(DESC, project.getDescription()); setDependencies(); From e08e0e09e604b8f6b535094cb4ce9faaa1cb0e7f Mon Sep 17 00:00:00 2001 From: kylepls Date: Mon, 6 Apr 2020 20:48:52 -0600 Subject: [PATCH 03/54] Replace annotation processor dep with plugin --- .../javax.annotation.processing.Processor | 1 - mcspring-build/mcspring-annotations/pom.xml | 66 ------- .../processor/AnnotationProcessor.java | 172 ------------------ .../processor/annotation/PluginAuthor.java | 5 - .../processor/annotation/PluginDepend.java | 6 - .../processor/annotation/SpringPlugin.java | 7 - .../processor/util/MainClassCreator.java | 38 ---- .../src/main/resources/Main.java | 34 ---- mcspring-build/mcspring-starter/pom.xml | 14 +- mcspring-build/pom.xml | 1 - 10 files changed, 4 insertions(+), 340 deletions(-) delete mode 100644 mcspring-build/mcspring-annotations/goddammit-maven/META-INF/services/javax.annotation.processing.Processor delete mode 100644 mcspring-build/mcspring-annotations/pom.xml delete mode 100644 mcspring-build/mcspring-annotations/src/main/java/in/kyle/mcspring/processor/AnnotationProcessor.java delete mode 100644 mcspring-build/mcspring-annotations/src/main/java/in/kyle/mcspring/processor/annotation/PluginAuthor.java delete mode 100644 mcspring-build/mcspring-annotations/src/main/java/in/kyle/mcspring/processor/annotation/PluginDepend.java delete mode 100644 mcspring-build/mcspring-annotations/src/main/java/in/kyle/mcspring/processor/annotation/SpringPlugin.java delete mode 100644 mcspring-build/mcspring-annotations/src/main/java/in/kyle/mcspring/processor/util/MainClassCreator.java delete mode 100644 mcspring-build/mcspring-annotations/src/main/resources/Main.java diff --git a/mcspring-build/mcspring-annotations/goddammit-maven/META-INF/services/javax.annotation.processing.Processor b/mcspring-build/mcspring-annotations/goddammit-maven/META-INF/services/javax.annotation.processing.Processor deleted file mode 100644 index bcce3a5..0000000 --- a/mcspring-build/mcspring-annotations/goddammit-maven/META-INF/services/javax.annotation.processing.Processor +++ /dev/null @@ -1 +0,0 @@ -in.kyle.mcspring.processor.AnnotationProcessor diff --git a/mcspring-build/mcspring-annotations/pom.xml b/mcspring-build/mcspring-annotations/pom.xml deleted file mode 100644 index 66efbe7..0000000 --- a/mcspring-build/mcspring-annotations/pom.xml +++ /dev/null @@ -1,66 +0,0 @@ - - - - mcspring-build - in.kyle.mcspring - 0.0.9 - - - 4.0.0 - - mcspring-annotations - - - - commons-io - commons-io - 2.6 - - - in.kyle.mcspring - mcspring-base - provided - - - commons-lang - commons-lang - 2.6 - compile - - - org.spigotmc - spigot-api - compile - - - - - - - - maven-resources-plugin - 3.1.0 - - - copy-resources - process-classes - - copy-resources - - - ${project.build.outputDirectory} - - - ${basedir}/goddammit-maven - false - - - - - - - - - diff --git a/mcspring-build/mcspring-annotations/src/main/java/in/kyle/mcspring/processor/AnnotationProcessor.java b/mcspring-build/mcspring-annotations/src/main/java/in/kyle/mcspring/processor/AnnotationProcessor.java deleted file mode 100644 index 75036f3..0000000 --- a/mcspring-build/mcspring-annotations/src/main/java/in/kyle/mcspring/processor/AnnotationProcessor.java +++ /dev/null @@ -1,172 +0,0 @@ -package in.kyle.mcspring.processor; - -import org.apache.commons.lang.exception.ExceptionUtils; -import org.springframework.stereotype.Component; -import org.springframework.stereotype.Controller; -import org.springframework.stereotype.Service; - -import java.io.IOException; -import java.io.Writer; -import java.lang.annotation.Annotation; -import java.util.Arrays; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.function.Function; -import java.util.stream.Collectors; - -import javax.annotation.processing.AbstractProcessor; -import javax.annotation.processing.RoundEnvironment; -import javax.annotation.processing.SupportedAnnotationTypes; -import javax.annotation.processing.SupportedSourceVersion; -import javax.lang.model.SourceVersion; -import javax.lang.model.element.Element; -import javax.lang.model.element.TypeElement; -import javax.tools.Diagnostic; -import javax.tools.FileObject; -import javax.tools.StandardLocation; - -import in.kyle.mcspring.processor.annotation.PluginDepend; -import in.kyle.mcspring.processor.annotation.SpringPlugin; -import in.kyle.mcspring.processor.util.MainClassCreator; - -// Adapted from https://hub.spigotmc.org/stash/projects/SPIGOT/repos/plugin-annotations/browse -// /src/main/java/org/bukkit/plugin/java/annotation/PluginAnnotationProcessor.java -@SupportedAnnotationTypes("*") -@SupportedSourceVersion(SourceVersion.RELEASE_8) -public class AnnotationProcessor extends AbstractProcessor { - - private Writer yml; - private String mainClass = "PluginMain"; - private boolean created = false; - - private void setArtifactId() { - processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, processingEnv.getOptions().toString()); - mainClass = Objects.requireNonNull(processingEnv.getOptions().get("artifactId")) - .replace("-", "") - .replace(".", ""); - } - - @Override - public boolean process(Set annotations, RoundEnvironment env) { - if (created) { - return true; - } - try { - setArtifactId(); - this.processingEnv.getMessager() - .printMessage(Diagnostic.Kind.NOTE, "Generating Plugin Data..."); - process(env); - created = true; - } catch (Exception e) { - this.processingEnv.getMessager() - .printMessage(Diagnostic.Kind.ERROR, ExceptionUtils.getStackTrace(e)); - return false; - } - return true; - } - - private Set findPackage(RoundEnvironment env) { - Set elements = env.getElementsAnnotatedWith(Component.class); - elements.addAll((Set) env.getElementsAnnotatedWith(Controller.class)); - elements.addAll((Set) env.getElementsAnnotatedWith(Service.class)); - Set packages = new HashSet<>(); - for (Element element : elements) { - if (element instanceof TypeElement) { - TypeElement te = (TypeElement) element; - String packageName = getPackageFromFqn(te.getQualifiedName().toString()); - packages.add(packageName); - } - } - return packages; - } - - private String getRootPackage(Set packages) { - return packages.stream().min(Comparator.comparingInt(String::length)).orElse("ignore"); - } - - private String getPackageFromFqn(String fqn) { - if (fqn.contains(".")) { - return fqn.substring(0, fqn.lastIndexOf(".")); - } else { - return fqn; - } - } - - private void process(RoundEnvironment env) throws Exception { - FileObject ymlFile = processingEnv.getFiler() - .createResource(StandardLocation.CLASS_OUTPUT, "", "plugin.yml"); - yml = ymlFile.openWriter(); - Set packages = findPackage(env); - String rootPackage = "org.springframework.boot.loader"; - String appendRoot = getRootPackage(packages); - if (!appendRoot.isEmpty()) { - rootPackage += "." + appendRoot; - } - mainClass = rootPackage + "." + mainClass; - FileObject main = processingEnv.getFiler().createSourceFile(mainClass); - MainClassCreator.generateMain(main, mainClass, rootPackage, packages); - - addRequired(env); - addDependencies(env); - yml.flush(); - yml.close(); - } - - private void addRequired(RoundEnvironment env) throws IOException { - yml.write(String.format("main: %s\n", mainClass)); - String name = processOne(env, - SpringPlugin.class, - SpringPlugin::name, - "spring-plugin-default-name"); - yml.write(String.format("name: %s\n", name)); - String version = processOne(env, SpringPlugin.class, SpringPlugin::version, "0.0.1"); - yml.write(String.format("version: %s\n", version)); - String description = processOne(env, SpringPlugin.class, SpringPlugin::description, ""); - yml.write(String.format("description: %s\n", description)); - } - - private void addDependencies(RoundEnvironment env) throws IOException { - Set hardDepend = new HashSet<>(); - Set softDepend = new HashSet<>(); - processAll(env, PluginDepend.class, depend -> { - if (depend.soft()) { - return softDepend.addAll(Arrays.asList(depend.plugins())); - } else { - return hardDepend.addAll(Arrays.asList(depend.plugins())); - } - }); - if (!hardDepend.isEmpty()) { - String dependString = String.join(", ", hardDepend); - yml.write(String.format("depend: [%s]\n", dependString)); - } - if (!softDepend.isEmpty()) { - String dependString = String.join(", ", softDepend); - yml.write(String.format("softdepend: [%s]\n", dependString)); - } - } - - private static R processOne(RoundEnvironment env, - Class annotation, - Function function, - R defaultValue) { - List rs = processAll(env, annotation, function); - if (!rs.isEmpty()) { - return rs.get(0); - } else { - return defaultValue; - } - } - - private static List processAll(RoundEnvironment env, - Class annotation, - Function consumer) { - Set elements = env.getElementsAnnotatedWith(annotation); - return elements.stream() - .flatMap(e -> Arrays.stream(e.getAnnotationsByType(annotation))) - .map(consumer) - .collect(Collectors.toList()); - } -} diff --git a/mcspring-build/mcspring-annotations/src/main/java/in/kyle/mcspring/processor/annotation/PluginAuthor.java b/mcspring-build/mcspring-annotations/src/main/java/in/kyle/mcspring/processor/annotation/PluginAuthor.java deleted file mode 100644 index 820307b..0000000 --- a/mcspring-build/mcspring-annotations/src/main/java/in/kyle/mcspring/processor/annotation/PluginAuthor.java +++ /dev/null @@ -1,5 +0,0 @@ -package in.kyle.mcspring.processor.annotation; - -public @interface PluginAuthor { - String[] authors(); -} diff --git a/mcspring-build/mcspring-annotations/src/main/java/in/kyle/mcspring/processor/annotation/PluginDepend.java b/mcspring-build/mcspring-annotations/src/main/java/in/kyle/mcspring/processor/annotation/PluginDepend.java deleted file mode 100644 index 869945d..0000000 --- a/mcspring-build/mcspring-annotations/src/main/java/in/kyle/mcspring/processor/annotation/PluginDepend.java +++ /dev/null @@ -1,6 +0,0 @@ -package in.kyle.mcspring.processor.annotation; - -public @interface PluginDepend { - String[] plugins(); - boolean soft() default false; -} diff --git a/mcspring-build/mcspring-annotations/src/main/java/in/kyle/mcspring/processor/annotation/SpringPlugin.java b/mcspring-build/mcspring-annotations/src/main/java/in/kyle/mcspring/processor/annotation/SpringPlugin.java deleted file mode 100644 index f0426ba..0000000 --- a/mcspring-build/mcspring-annotations/src/main/java/in/kyle/mcspring/processor/annotation/SpringPlugin.java +++ /dev/null @@ -1,7 +0,0 @@ -package in.kyle.mcspring.processor.annotation; - -public @interface SpringPlugin { - String name(); - String version() default "0.0.1"; - String description() default "A Spring plugin"; -} diff --git a/mcspring-build/mcspring-annotations/src/main/java/in/kyle/mcspring/processor/util/MainClassCreator.java b/mcspring-build/mcspring-annotations/src/main/java/in/kyle/mcspring/processor/util/MainClassCreator.java deleted file mode 100644 index f17a46c..0000000 --- a/mcspring-build/mcspring-annotations/src/main/java/in/kyle/mcspring/processor/util/MainClassCreator.java +++ /dev/null @@ -1,38 +0,0 @@ -package in.kyle.mcspring.processor.util; - -import org.apache.commons.io.IOUtils; - -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Writer; -import java.util.Set; -import java.util.stream.Collectors; - -import javax.tools.FileObject; - -public class MainClassCreator { - - public static void generateMain(FileObject main, - String fqn, - String packageName, - Set packages) throws IOException { - InputStream resource = MainClassCreator.class.getResourceAsStream("/Main.java"); - try (Writer writer = main.openWriter()) { - String template = IOUtils.toString(new InputStreamReader(resource)); - String name = fqn; - if (fqn.contains(".")) { - template = String.format("package %s;\n", packageName) + template; - name = fqn.substring(fqn.lastIndexOf(".") + 1); - } - String scans = makeScanStrings(packages); - writer.write(template.replace("{name}", name).replace("{scans}", scans)); - } - } - - private static String makeScanStrings(Set strings) { - return strings.stream() - .map(s -> String.format("\"%s\"", s)) - .collect(Collectors.joining(",\n ")); - } -} diff --git a/mcspring-build/mcspring-annotations/src/main/resources/Main.java b/mcspring-build/mcspring-annotations/src/main/resources/Main.java deleted file mode 100644 index bca3692..0000000 --- a/mcspring-build/mcspring-annotations/src/main/resources/Main.java +++ /dev/null @@ -1,34 +0,0 @@ -import org.springframework.boot.autoconfigure.SpringBootApplication; - -// @formatter::off -import org.bukkit.plugin.java.JavaPlugin; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.loader.mcspring.McSpringLoader; - -import in.kyle.mcspring.SpringPlugin; -// @formatter::on - -public class {name} extends JavaPlugin { - - private McSpringLoader loader; - - public void onEnable() { - try { - new McSpringLoader().launch(getClassLoader()); - SpringPlugin.setup(this, MainPluginConfig.class); - } catch (Exception ignored){ - getLogger().warning("MCSpring Failed to load " + getName()); - // error will be logged by Spring - } - } - - public void onDisable() { - SpringPlugin.teardown(this); -// if (loader != null) loader.close(); - loader = null; - } - - @SpringBootApplication(scanBasePackages = {{scans}}) - static class MainPluginConfig { - } -} diff --git a/mcspring-build/mcspring-starter/pom.xml b/mcspring-build/mcspring-starter/pom.xml index 7f0999a..408deea 100644 --- a/mcspring-build/mcspring-starter/pom.xml +++ b/mcspring-build/mcspring-starter/pom.xml @@ -30,12 +30,6 @@ mcspring-subcommands 0.0.9 - - in.kyle.mcspring - mcspring-annotations - 0.0.9 - provided - @@ -177,14 +171,14 @@ com.googlecode.maven-download-plugin download-maven-plugin + + in.kyle.mcspring + mcspring-auto-generator + ${project.artifactId} - - in.kyle.mcspring - mcspring-annotations - in.kyle.mcspring mcspring-base diff --git a/mcspring-build/pom.xml b/mcspring-build/pom.xml index 87c83d3..387e0ec 100644 --- a/mcspring-build/pom.xml +++ b/mcspring-build/pom.xml @@ -13,7 +13,6 @@ mcspring-plugin-layout mcspring-archetype - mcspring-annotations mcspring-starter mcspring-maven-plugin mcspring-maven-plugin-test From 62c34f3454e415afa103d2a011fb59857866ab8b Mon Sep 17 00:00:00 2001 From: Joshua Date: Tue, 7 Apr 2020 19:39:35 -0400 Subject: [PATCH 04/54] fixed poms for mcspring-maven-plugin --- .../mcspring-maven-plugin-test/pom.xml | 3 +- mcspring-build/mcspring-maven-plugin/pom.xml | 19 ++++- ...onPlugin.java => McSpringMavenPlugin.java} | 4 +- .../TestPluginDependencyResolver.java | 24 +++++++ .../src/test/resources/pom.xml | 71 +++++++++++++++++++ mcspring-build/mcspring-starter/pom.xml | 7 +- 6 files changed, 121 insertions(+), 7 deletions(-) rename mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/{AutoGenerationPlugin.java => McSpringMavenPlugin.java} (96%) create mode 100644 mcspring-build/mcspring-maven-plugin/src/test/java/in/kyle/mcspring/autogenerator/TestPluginDependencyResolver.java create mode 100644 mcspring-build/mcspring-maven-plugin/src/test/resources/pom.xml diff --git a/mcspring-build/mcspring-maven-plugin-test/pom.xml b/mcspring-build/mcspring-maven-plugin-test/pom.xml index 5a9f2ea..8d6b496 100644 --- a/mcspring-build/mcspring-maven-plugin-test/pom.xml +++ b/mcspring-build/mcspring-maven-plugin-test/pom.xml @@ -39,11 +39,10 @@ in.kyle.mcspring mcspring-maven-plugin - ${project.version} - mcspring-auto-generator + mcspring-maven-plugin diff --git a/mcspring-build/mcspring-maven-plugin/pom.xml b/mcspring-build/mcspring-maven-plugin/pom.xml index 5ca4479..cd31b49 100644 --- a/mcspring-build/mcspring-maven-plugin/pom.xml +++ b/mcspring-build/mcspring-maven-plugin/pom.xml @@ -11,13 +11,17 @@ mcspring-maven-plugin maven-plugin - mcspring-auto-generator + mcspring-maven-plugin + + + 3.3.9 + org.apache.maven maven-plugin-api - 3.6.2 + ${maven.version} org.apache.maven.plugin-tools @@ -30,6 +34,11 @@ snakeyaml 1.26 + + org.apache.maven + maven-core + 3.3.9 + org.apache.maven maven-project @@ -44,6 +53,12 @@ org.projectlombok lombok + + org.apache.maven.plugin-testing + maven-plugin-testing-harness + 3.3.0 + test + diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/AutoGenerationPlugin.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/McSpringMavenPlugin.java similarity index 96% rename from mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/AutoGenerationPlugin.java rename to mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/McSpringMavenPlugin.java index 4ca20da..a33e7d7 100644 --- a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/AutoGenerationPlugin.java +++ b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/McSpringMavenPlugin.java @@ -16,10 +16,10 @@ import java.util.stream.Collectors; -@Mojo(name = "mcspring-auto-generator", defaultPhase = LifecyclePhase.PROCESS_CLASSES, +@Mojo(name = "mcspring-maven-plugin", defaultPhase = LifecyclePhase.PROCESS_CLASSES, requiresDependencyCollection = ResolutionScope.COMPILE_PLUS_RUNTIME, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME) -public class AutoGenerationPlugin extends AbstractMojo { +public class McSpringMavenPlugin extends AbstractMojo { private static final List VALID_SCOPES = Arrays.asList("provided", "compile", "runtime"); @Parameter(defaultValue = "${project}", required = true) diff --git a/mcspring-build/mcspring-maven-plugin/src/test/java/in/kyle/mcspring/autogenerator/TestPluginDependencyResolver.java b/mcspring-build/mcspring-maven-plugin/src/test/java/in/kyle/mcspring/autogenerator/TestPluginDependencyResolver.java new file mode 100644 index 0000000..75af80e --- /dev/null +++ b/mcspring-build/mcspring-maven-plugin/src/test/java/in/kyle/mcspring/autogenerator/TestPluginDependencyResolver.java @@ -0,0 +1,24 @@ +package in.kyle.mcspring.autogenerator; + +import lombok.SneakyThrows; +import org.apache.maven.plugin.testing.AbstractMojoTestCase; + +import java.io.File; + +public class TestPluginDependencyResolver extends AbstractMojoTestCase { + + @Override + protected void setUp() throws Exception { + + } + + @Override + protected void tearDown() throws Exception { + + } + + @SneakyThrows + public void testResolution() { + + } +} diff --git a/mcspring-build/mcspring-maven-plugin/src/test/resources/pom.xml b/mcspring-build/mcspring-maven-plugin/src/test/resources/pom.xml new file mode 100644 index 0000000..5a9f2ea --- /dev/null +++ b/mcspring-build/mcspring-maven-plugin/src/test/resources/pom.xml @@ -0,0 +1,71 @@ + + + + + mcspring-starter + in.kyle.mcspring + 0.0.9 + + + 4.0.0 + + mcspring-maven-plugin-test + + + 1.15.1-R0.1-SNAPSHOT + 8 + 8 + + + + + in.kyle.mcspring + mcspring-base + + + in.kyle.mcspring + mcspring-vault + ${project.version} + + + org.projectlombok + lombok + + + + + + in.kyle.mcspring + mcspring-maven-plugin + ${project.version} + + + + mcspring-auto-generator + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.0.0 + + + generate-resources + + add-source + + + + ${project.build.directory}/generated-sources/mc-spring/ + + + + + + + + \ No newline at end of file diff --git a/mcspring-build/mcspring-starter/pom.xml b/mcspring-build/mcspring-starter/pom.xml index 408deea..fc6909f 100644 --- a/mcspring-build/mcspring-starter/pom.xml +++ b/mcspring-build/mcspring-starter/pom.xml @@ -36,6 +36,11 @@ + + in.kyle.mcspring + mcspring-maven-plugin + 0.0.9 + org.springframework.boot spring-boot-maven-plugin @@ -173,7 +178,7 @@ in.kyle.mcspring - mcspring-auto-generator + mcspring-maven-plugin ${project.artifactId} From 531478abd884efa30b31a74e3c250393470e973e Mon Sep 17 00:00:00 2001 From: Joshua Date: Sat, 2 May 2020 15:57:14 -0400 Subject: [PATCH 05/54] now scans project source for @PluginDepend --- .../mcspring-maven-plugin-test/pom.xml | 2 +- .../mcspring-maven-plugin/README.md | 14 ++++ mcspring-build/mcspring-maven-plugin/pom.xml | 12 +-- .../autogenerator/ClassLoadingUtility.java | 30 +++++++ .../mcspring/autogenerator/FileUtility.java | 26 ++++++ .../autogenerator/MainClassUtilities.java | 5 +- .../autogenerator/McSpringMavenPlugin.java | 26 ++++-- .../PluginDependencyResolver.java | 82 +++++-------------- ...java => ProjectSpringPackageResolver.java} | 39 ++------- .../scan/JarFileDependencyScanner.java | 64 +++++++++++++++ .../scan/ProjectDependencyScanner.java | 44 ++++++++++ .../TestPluginDependencyResolver.java | 24 ------ .../src/test/resources/pom.xml | 71 ---------------- .../java/in/kyle/mcspring/manager/Main.java | 6 +- .../simple-factions-addon/pom.xml | 34 ++++++++ .../java/test/other/stats/FactionsStats.java | 5 +- .../org/example/factions/api/Faction.java | 1 - .../org/example/factions/api/FactionsApi.java | 3 +- pom.xml | 5 ++ 19 files changed, 275 insertions(+), 218 deletions(-) create mode 100644 mcspring-build/mcspring-maven-plugin/README.md create mode 100644 mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/ClassLoadingUtility.java create mode 100644 mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/FileUtility.java rename mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/{ProjectClassScanner.java => ProjectSpringPackageResolver.java} (60%) create mode 100644 mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/JarFileDependencyScanner.java create mode 100644 mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/ProjectDependencyScanner.java delete mode 100644 mcspring-build/mcspring-maven-plugin/src/test/java/in/kyle/mcspring/autogenerator/TestPluginDependencyResolver.java delete mode 100644 mcspring-build/mcspring-maven-plugin/src/test/resources/pom.xml diff --git a/mcspring-build/mcspring-maven-plugin-test/pom.xml b/mcspring-build/mcspring-maven-plugin-test/pom.xml index 8d6b496..9922622 100644 --- a/mcspring-build/mcspring-maven-plugin-test/pom.xml +++ b/mcspring-build/mcspring-maven-plugin-test/pom.xml @@ -42,7 +42,7 @@ - mcspring-maven-plugin + generate-files diff --git a/mcspring-build/mcspring-maven-plugin/README.md b/mcspring-build/mcspring-maven-plugin/README.md new file mode 100644 index 0000000..072f958 --- /dev/null +++ b/mcspring-build/mcspring-maven-plugin/README.md @@ -0,0 +1,14 @@ +##MC-Spring Auto Generation Plugin for Maven + +###What does this do? +This plugin removes the need to create a plugin.yml and main class. +At compile time we resolve all dependencies that your plugin uses. +Those dependencies will be inserted into the plugin.yml, along with +various maven properties that you can define in the pom.xml. + +###How does it work? +Step 1: Resolve all maven project dependencies and find the corresponding +jars associated with them to add to a class loader. Load all classes +from the jars and check for the @PluginDepend annotation provided by MC-Spring. + +Step 2: Profit \ No newline at end of file diff --git a/mcspring-build/mcspring-maven-plugin/pom.xml b/mcspring-build/mcspring-maven-plugin/pom.xml index cd31b49..8812cc5 100644 --- a/mcspring-build/mcspring-maven-plugin/pom.xml +++ b/mcspring-build/mcspring-maven-plugin/pom.xml @@ -11,7 +11,7 @@ mcspring-maven-plugin maven-plugin - mcspring-maven-plugin + ${project.artifactId} 3.3.9 @@ -53,12 +53,6 @@ org.projectlombok lombok - - org.apache.maven.plugin-testing - maven-plugin-testing-harness - 3.3.0 - test - @@ -72,8 +66,8 @@ org.apache.maven.plugins maven-compiler-plugin - 11 - 11 + 8 + 8 diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/ClassLoadingUtility.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/ClassLoadingUtility.java new file mode 100644 index 0000000..2307e00 --- /dev/null +++ b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/ClassLoadingUtility.java @@ -0,0 +1,30 @@ +package in.kyle.mcspring.autogenerator; + +import lombok.SneakyThrows; + +import java.io.File; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class ClassLoadingUtility { + + public static List getJarFileURLs(List jarFiles) { + return jarFiles.stream().map(ClassLoadingUtility::createJarFileURL).collect(Collectors.toList()); + } + + @SneakyThrows + private static URL createJarFileURL(File jarFile) { + return new URL("jar:file:" + jarFile.getPath() + "!/"); + } + + //Creates a class loader with all jar file classes and classes in the sourceFolder parameter. + @SneakyThrows + public static URLClassLoader createClassLoader(ClassLoader parent, List jarFiles, File sourceFolder) { + List urls = new ArrayList<>(getJarFileURLs(jarFiles)); + urls.add(sourceFolder.toURI().toURL()); + return new URLClassLoader(urls.toArray(new URL[0]), parent); + } +} diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/FileUtility.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/FileUtility.java new file mode 100644 index 0000000..c5902b4 --- /dev/null +++ b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/FileUtility.java @@ -0,0 +1,26 @@ +package in.kyle.mcspring.autogenerator; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +public class FileUtility { + + private FileUtility() { + } + + public static List getFilesInDirectory(File directory) { + List files = new ArrayList<>(); + File[] filesInDirectory = directory.listFiles(); + if (filesInDirectory != null) { + for (File file : filesInDirectory) { + if (file.isDirectory()) { + files.addAll(getFilesInDirectory(file)); + } else { + files.add(file); + } + } + } + return files; + } +} diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/MainClassUtilities.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/MainClassUtilities.java index b4e9c39..a163c0f 100644 --- a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/MainClassUtilities.java +++ b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/MainClassUtilities.java @@ -1,13 +1,14 @@ package in.kyle.mcspring.autogenerator; -import lombok.experimental.UtilityClass; import org.apache.maven.project.MavenProject; -@UtilityClass public class MainClassUtilities { public static final String ROOT_PACKAGE = "org.springframework.boot.loader"; + private MainClassUtilities() { + } + public static String getMainClassName(MavenProject project) { return project.getArtifactId().replace("-", ""); } diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/McSpringMavenPlugin.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/McSpringMavenPlugin.java index a33e7d7..b88bb30 100644 --- a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/McSpringMavenPlugin.java +++ b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/McSpringMavenPlugin.java @@ -10,13 +10,14 @@ import org.apache.maven.project.MavenProject; import java.io.File; +import java.net.URLClassLoader; import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.stream.Collectors; -@Mojo(name = "mcspring-maven-plugin", defaultPhase = LifecyclePhase.PROCESS_CLASSES, +@Mojo(name = "generate-files", defaultPhase = LifecyclePhase.PROCESS_CLASSES, requiresDependencyCollection = ResolutionScope.COMPILE_PLUS_RUNTIME, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME) public class McSpringMavenPlugin extends AbstractMojo { @@ -24,21 +25,33 @@ public class McSpringMavenPlugin extends AbstractMojo { private static final List VALID_SCOPES = Arrays.asList("provided", "compile", "runtime"); @Parameter(defaultValue = "${project}", required = true) private MavenProject project; + private URLClassLoader fullyQualifiedClassLoader; @SneakyThrows public void execute() { + initializeClassLoader(); addGeneratedSourcesDirectory(); preparePluginYml(); preparePluginMainClass(); } + //Initializes a class loader with all maven dependency classes and project classes. + private void initializeClassLoader() { + ClassLoader parent = getClass().getClassLoader(); + File projectSource = getSourceClassesFolder(); + List artifactFiles = getDependencyArtifacts().stream() + .map(Artifact::getFile) + .collect(Collectors.toList()); + this.fullyQualifiedClassLoader = ClassLoadingUtility.createClassLoader(parent, artifactFiles, projectSource); + } + private File getSourcesOutputDirectory() { return new File(getGeneratedSourcesFolder(), "mc-spring/"); } private void addGeneratedSourcesDirectory() { File output = getSourcesOutputDirectory(); - if(!output.exists()) { + if (!output.exists()) { output.mkdirs(); } project.addCompileSourceRoot(output.getPath()); @@ -48,7 +61,7 @@ private void preparePluginYml() { getLog().info("Scanning for project dependencies in qualifying scope"); Set artifacts = getDependencyArtifacts(); getLog().info(String.format("Dependency scan complete. Found %d dependencies", artifacts.size())); - PluginDependencyResolver resolver = new PluginDependencyResolver(artifacts); + PluginDependencyResolver resolver = new PluginDependencyResolver(fullyQualifiedClassLoader, getSourceClassesFolder(), artifacts); PluginYamlAttributes attributes = new PluginYamlAttributes(project, resolver, getLog()); attributes.loadAttributes(); getLog().info("Finished obtaining data for plugin.yml"); @@ -62,10 +75,9 @@ private void preparePluginYml() { } private void preparePluginMainClass() { - PluginDependencyResolver resolver = new PluginDependencyResolver(getDependencyArtifacts()); getLog().info("Scanning project sources for spring annotations"); - ProjectClassScanner scanner = new ProjectClassScanner(getSourceClassesFolder(), resolver.getDependencyURLs()); - scanner.findPackages(); + ProjectSpringPackageResolver scanner = new ProjectSpringPackageResolver(fullyQualifiedClassLoader, getSourceClassesFolder()); + scanner.findPackagesThatUseSpring(); Set packages = scanner.getPackagesThatUseSpring(); getLog().info(String.format("Scan complete. Found %d packages with spring annotation", packages.size())); getLog().info("Preparing to generate main class"); @@ -77,7 +89,7 @@ private void preparePluginMainClass() { private void writePluginMain(Set packages) { String mainClass = MainClassUtilities.getMainClassLocation(project); File destination = new File(getSourcesOutputDirectory(), mainClass.replace(".", "/").concat(".java")); - if(!destination.getParentFile().exists()) { + if (!destination.getParentFile().exists()) { destination.getParentFile().mkdirs(); } PluginMainClassGenerator generator = new PluginMainClassGenerator(project, packages, destination); diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginDependencyResolver.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginDependencyResolver.java index 824160b..6ee72c1 100644 --- a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginDependencyResolver.java +++ b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginDependencyResolver.java @@ -1,92 +1,50 @@ package in.kyle.mcspring.autogenerator; import in.kyle.mcspring.annotation.PluginDepend; +import in.kyle.mcspring.autogenerator.scan.JarFileDependencyScanner; +import in.kyle.mcspring.autogenerator.scan.ProjectDependencyScanner; import lombok.SneakyThrows; import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.logging.Log; import java.io.File; -import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Collection; -import java.util.Enumeration; import java.util.List; -import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.stream.Collectors; public class PluginDependencyResolver { + private final URLClassLoader fullyQualifiedClassLoader; + private final File sourcesFolder; private final List jars; - public PluginDependencyResolver(Collection dependencies) { + public PluginDependencyResolver(URLClassLoader fullyQualifiedClassLoader, File sourcesFolder, Collection dependencies) { + this.fullyQualifiedClassLoader = fullyQualifiedClassLoader; + this.sourcesFolder = sourcesFolder; this.jars = dependencies.stream().map(Artifact::getFile).collect(Collectors.toList()); } - public List getDependencyURLs() { - return jars.stream().map(this::createJarFileURL).collect(Collectors.toList()); - } - - public List resolveAllDependencies() { - List dependencies = new ArrayList<>(); - URLClassLoader classLoader = createDependencyClassLoader(); - jars.forEach(file -> dependencies.addAll(findDependencies(file, classLoader))); - return dependencies; - } - - @SneakyThrows - private List findDependencies(File file, URLClassLoader classLoader) { - JarFile jarFile = new JarFile(file); - return findDependencies(jarFile, classLoader); - } - @SneakyThrows - private List findDependencies(JarFile jarFile, URLClassLoader classLoader) { - List dependList = new ArrayList<>(); - Enumeration enumeration = jarFile.entries(); - while(enumeration.hasMoreElements()) { - JarEntry entry = enumeration.nextElement(); - if(isClassFile(entry)) { - String className = getClassName(entry); - addDependencyIfPresent(className, classLoader, dependList); - } - } - return dependList; - } - - private void addDependencyIfPresent(String className, URLClassLoader loader, List destination) { - try { - Class clazz = loader.loadClass(className); - if(clazz.isAnnotationPresent(PluginDepend.class)) { - destination.add(clazz.getAnnotation(PluginDepend.class)); - } - //Catching a throwable for optional dependencies not being present. - //It throws a ClassNotFoundException but for some reason we can't catch - //then handle it because WONK. - } catch (Throwable ignored) { - - } - } - - @SneakyThrows - private URLClassLoader createDependencyClassLoader() { - ClassLoader parent = getClass().getClassLoader(); - URL[] urls = getDependencyURLs().toArray(URL[]::new); - return new URLClassLoader(urls, parent); + private List getJarFiles() { + return jars.stream().map(this::getAsJarFile).collect(Collectors.toList()); } @SneakyThrows - private URL createJarFileURL(File jarFile) { - return new URL("jar:file:" + jarFile.getPath() + "!/"); + private JarFile getAsJarFile(File file) { + return new JarFile(file); } - private boolean isClassFile(JarEntry entry) { - return entry.getName().endsWith(".class"); + public List resolveAllDependencies() { + List dependencies = new ArrayList<>(); + List jars = getJarFiles(); + JarFileDependencyScanner jarFileDependencyScanner = new JarFileDependencyScanner(fullyQualifiedClassLoader, jars); + ProjectDependencyScanner projectDependencyScanner = new ProjectDependencyScanner(fullyQualifiedClassLoader, sourcesFolder); + dependencies.addAll(jarFileDependencyScanner.getPluginDependAnnotations()); + dependencies.addAll(projectDependencyScanner.getPluginDependAnnotations()); + return dependencies; } - private String getClassName(JarEntry entry) { - String fullName = entry.getName(); - String excludeClassExtension = fullName.substring(0, fullName.length() - 6); - return excludeClassExtension.replace("/", "."); - } } diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/ProjectClassScanner.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/ProjectSpringPackageResolver.java similarity index 60% rename from mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/ProjectClassScanner.java rename to mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/ProjectSpringPackageResolver.java index 4ca11f3..7f00b5b 100644 --- a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/ProjectClassScanner.java +++ b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/ProjectSpringPackageResolver.java @@ -1,14 +1,12 @@ package in.kyle.mcspring.autogenerator; import lombok.RequiredArgsConstructor; -import lombok.SneakyThrows; import org.springframework.stereotype.Component; import org.springframework.stereotype.Controller; import org.springframework.stereotype.Service; import java.io.File; import java.lang.annotation.Annotation; -import java.net.URL; import java.net.URLClassLoader; import java.util.*; import java.util.stream.Collectors; @@ -17,7 +15,7 @@ //Objective: Scan all classes in the provider folder and check for spring annotations. //If present, get the class package and add it to a list for springs package scan at runtime @RequiredArgsConstructor -class ProjectClassScanner { +class ProjectSpringPackageResolver { private static final List> SPRING_ANNOTATIONS = Arrays.asList( Service.class, @@ -25,24 +23,23 @@ class ProjectClassScanner { Component.class ); + private final URLClassLoader fullyQualifiedClassLoader; private final File classesFolder; - private final List projectDependencyJars; private final Set packages = new HashSet<>(); public Set getPackagesThatUseSpring() { return Collections.unmodifiableSet(packages); } - public void findPackages() { + public void findPackagesThatUseSpring() { List classFiles = getClassFiles(); - URLClassLoader classLoader = createClassLoader(); - classFiles.forEach(file -> addPackageIfFound(classLoader, file)); + classFiles.forEach(this::addPackageIfFound); } - private void addPackageIfFound(URLClassLoader classLoader, File classFile) { + private void addPackageIfFound(File classFile) { String className = getClassName(classFile); try { - Class clazz = classLoader.loadClass(className); + Class clazz = fullyQualifiedClassLoader.loadClass(className); if(hasSpringAnnotation(clazz)) { packages.add(clazz.getPackage().getName()); } @@ -59,7 +56,7 @@ private String getClassName(File classFile) { } private List getClassFiles() { - return getFilesInDirectory(classesFolder) + return FileUtility.getFilesInDirectory(classesFolder) .stream() .filter(file -> file.getName().endsWith(".class")) .collect(Collectors.toList()); @@ -68,26 +65,4 @@ private List getClassFiles() { private boolean hasSpringAnnotation(Class clazz) { return SPRING_ANNOTATIONS.stream().anyMatch(clazz::isAnnotationPresent); } - - @SneakyThrows - private URLClassLoader createClassLoader() { - List classPathList = new ArrayList<>(projectDependencyJars); - classPathList.add(classesFolder.toURI().toURL()); - return new URLClassLoader(classPathList.toArray(new URL[0]), getClass().getClassLoader()); - } - - private List getFilesInDirectory(File directory) { - List files = new ArrayList<>(); - File[] filesInDirectory = directory.listFiles(); - if(filesInDirectory != null) { - for(File file : filesInDirectory) { - if(file.isDirectory()) { - files.addAll(getFilesInDirectory(file)); - } else { - files.add(file); - } - } - } - return files; - } } diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/JarFileDependencyScanner.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/JarFileDependencyScanner.java new file mode 100644 index 0000000..c9145e6 --- /dev/null +++ b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/JarFileDependencyScanner.java @@ -0,0 +1,64 @@ +package in.kyle.mcspring.autogenerator.scan; + +import in.kyle.mcspring.annotation.PluginDepend; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; + +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +@RequiredArgsConstructor +public class JarFileDependencyScanner { + + private final URLClassLoader classLoader; + private final List jarFiles; + + public List getPluginDependAnnotations() { + List list = new ArrayList<>(); + jarFiles.forEach(jarFile -> list.addAll(findJarFileDependencies(jarFile))); + return list; + } + + @SneakyThrows + private List findJarFileDependencies(JarFile jarFile) { + List dependList = new ArrayList<>(); + Enumeration enumeration = jarFile.entries(); + while(enumeration.hasMoreElements()) { + JarEntry entry = enumeration.nextElement(); + if(isClassFile(entry)) { + String className = getClassName(entry); + addDependencyIfPresent(className, dependList); + } + } + return dependList; + } + + private void addDependencyIfPresent(String className, List destination) { + try { + Class clazz = classLoader.loadClass(className); + if(clazz.isAnnotationPresent(PluginDepend.class)) { + destination.add(clazz.getAnnotation(PluginDepend.class)); + } + //Catching a throwable for optional dependencies not being present. + //It throws a ClassNotFoundException but for some reason we can't catch + //then handle it because WONK. + } catch (Throwable ignored) { + + } + } + + private boolean isClassFile(JarEntry entry) { + return entry.getName().endsWith(".class"); + } + + private String getClassName(JarEntry entry) { + String fullName = entry.getName(); + String excludeClassExtension = fullName.substring(0, fullName.length() - 6); + return excludeClassExtension.replace("/", "."); + } + +} diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/ProjectDependencyScanner.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/ProjectDependencyScanner.java new file mode 100644 index 0000000..e682594 --- /dev/null +++ b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/ProjectDependencyScanner.java @@ -0,0 +1,44 @@ +package in.kyle.mcspring.autogenerator.scan; + +import in.kyle.mcspring.annotation.PluginDepend; +import in.kyle.mcspring.autogenerator.FileUtility; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; + +import java.io.File; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.List; + +@RequiredArgsConstructor +public class ProjectDependencyScanner { + + private final URLClassLoader classLoader; + private final File sourcesFolder; + + @SneakyThrows + public List getPluginDependAnnotations() { + List list = new ArrayList<>(); + List files = FileUtility.getFilesInDirectory(sourcesFolder); + for(File file : files) { + if(isClass(file)) { + String className = getClassName(file); + Class clazz = classLoader.loadClass(className); + if(clazz.isAnnotationPresent(PluginDepend.class)) { + list.add(clazz.getAnnotation(PluginDepend.class)); + } + } + } + return list; + } + + private String getClassName(File classFile) { + String fullPath = classFile.getPath(); + String classNameWithPackage = fullPath.replace(sourcesFolder.getPath(), "").replace("\\", "."); + return classNameWithPackage.substring(1, classNameWithPackage.length() - 6); + } + + private boolean isClass(File file) { + return file.getName().endsWith(".class"); + } +} diff --git a/mcspring-build/mcspring-maven-plugin/src/test/java/in/kyle/mcspring/autogenerator/TestPluginDependencyResolver.java b/mcspring-build/mcspring-maven-plugin/src/test/java/in/kyle/mcspring/autogenerator/TestPluginDependencyResolver.java deleted file mode 100644 index 75af80e..0000000 --- a/mcspring-build/mcspring-maven-plugin/src/test/java/in/kyle/mcspring/autogenerator/TestPluginDependencyResolver.java +++ /dev/null @@ -1,24 +0,0 @@ -package in.kyle.mcspring.autogenerator; - -import lombok.SneakyThrows; -import org.apache.maven.plugin.testing.AbstractMojoTestCase; - -import java.io.File; - -public class TestPluginDependencyResolver extends AbstractMojoTestCase { - - @Override - protected void setUp() throws Exception { - - } - - @Override - protected void tearDown() throws Exception { - - } - - @SneakyThrows - public void testResolution() { - - } -} diff --git a/mcspring-build/mcspring-maven-plugin/src/test/resources/pom.xml b/mcspring-build/mcspring-maven-plugin/src/test/resources/pom.xml deleted file mode 100644 index 5a9f2ea..0000000 --- a/mcspring-build/mcspring-maven-plugin/src/test/resources/pom.xml +++ /dev/null @@ -1,71 +0,0 @@ - - - - - mcspring-starter - in.kyle.mcspring - 0.0.9 - - - 4.0.0 - - mcspring-maven-plugin-test - - - 1.15.1-R0.1-SNAPSHOT - 8 - 8 - - - - - in.kyle.mcspring - mcspring-base - - - in.kyle.mcspring - mcspring-vault - ${project.version} - - - org.projectlombok - lombok - - - - - - in.kyle.mcspring - mcspring-maven-plugin - ${project.version} - - - - mcspring-auto-generator - - - - - - org.codehaus.mojo - build-helper-maven-plugin - 3.0.0 - - - generate-resources - - add-source - - - - ${project.build.directory}/generated-sources/mc-spring/ - - - - - - - - \ No newline at end of file diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/Main.java b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/Main.java index 5659770..2b984df 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/Main.java +++ b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/Main.java @@ -1,9 +1,7 @@ package in.kyle.mcspring.manager; -import in.kyle.mcspring.processor.annotation.SpringPlugin; - // TODO: 2020-03-07 Pull this information from the pom.xml in a build plugin -@SpringPlugin(name = "mcspring-plugin-manager", - description = "Provides simple debugging and plugin management functions") +//@SpringPlugin(name = "mcspring-plugin-manager", +// description = "Provides simple debugging and plugin management functions") interface Main { } diff --git a/mcspring-examples/simple-factions-addon/pom.xml b/mcspring-examples/simple-factions-addon/pom.xml index ea38461..87656c4 100644 --- a/mcspring-examples/simple-factions-addon/pom.xml +++ b/mcspring-examples/simple-factions-addon/pom.xml @@ -33,4 +33,38 @@ lombok + + + + + in.kyle.mcspring + mcspring-maven-plugin + + + + generate-files + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.0.0 + + + generate-resources + + add-source + + + + ${project.build.directory}/generated-sources/mc-spring/ + + + + + + + diff --git a/mcspring-examples/simple-factions-addon/src/main/java/test/other/stats/FactionsStats.java b/mcspring-examples/simple-factions-addon/src/main/java/test/other/stats/FactionsStats.java index d1967c0..b060b51 100644 --- a/mcspring-examples/simple-factions-addon/src/main/java/test/other/stats/FactionsStats.java +++ b/mcspring-examples/simple-factions-addon/src/main/java/test/other/stats/FactionsStats.java @@ -1,10 +1,9 @@ package test.other.stats; +import in.kyle.mcspring.annotation.PluginDepend; import org.bukkit.event.EventHandler; import org.bukkit.event.player.PlayerMoveEvent; import in.kyle.mcspring.command.Command; -import in.kyle.mcspring.processor.annotation.PluginDepend; -import in.kyle.mcspring.processor.annotation.SpringPlugin; import org.example.factions.api.FactionsApi; import org.springframework.stereotype.Component; @@ -14,7 +13,7 @@ @Component @RequiredArgsConstructor @PluginDepend(plugins = "factions") -@SpringPlugin(name = "faction-stats", description = "Shows stats for factions") +//@SpringPlugin(name = "faction-stats", description = "Shows stats for factions") class FactionsStats { private final FactionsApi factionsApi; diff --git a/mcspring-examples/simple-factions/src/main/java/org/example/factions/api/Faction.java b/mcspring-examples/simple-factions/src/main/java/org/example/factions/api/Faction.java index f1a0404..3d4ab4c 100644 --- a/mcspring-examples/simple-factions/src/main/java/org/example/factions/api/Faction.java +++ b/mcspring-examples/simple-factions/src/main/java/org/example/factions/api/Faction.java @@ -6,7 +6,6 @@ import java.util.Map; import java.util.UUID; -import in.kyle.mcspring.processor.annotation.SpringPlugin; import lombok.Getter; @Getter diff --git a/mcspring-examples/simple-factions/src/main/java/org/example/factions/api/FactionsApi.java b/mcspring-examples/simple-factions/src/main/java/org/example/factions/api/FactionsApi.java index 66f6155..a5badb6 100644 --- a/mcspring-examples/simple-factions/src/main/java/org/example/factions/api/FactionsApi.java +++ b/mcspring-examples/simple-factions/src/main/java/org/example/factions/api/FactionsApi.java @@ -1,12 +1,11 @@ package org.example.factions.api; import org.bukkit.entity.Player; -import in.kyle.mcspring.processor.annotation.SpringPlugin; import java.util.List; import java.util.Optional; -@SpringPlugin(name = "factions", description = "A simple factions plugin") +//@SpringPlugin(name = "factions", description = "A simple factions plugin") public interface FactionsApi { void addFaction(Faction faction); diff --git a/pom.xml b/pom.xml index acd9f6f..a315bc2 100644 --- a/pom.xml +++ b/pom.xml @@ -67,6 +67,11 @@ mail@kyle.in kylepls + + Josh + joshuabetz12@gmail.com + Joshbpls + From aebb2cb28e01d5ae4aa9b83da174afc75b4bc0c5 Mon Sep 17 00:00:00 2001 From: Joshua Date: Sat, 2 May 2020 16:10:57 -0400 Subject: [PATCH 06/54] now properly compiles with plugin.yml --- mcspring-examples/simple-factions-addon/pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mcspring-examples/simple-factions-addon/pom.xml b/mcspring-examples/simple-factions-addon/pom.xml index 87656c4..7e2ae6c 100644 --- a/mcspring-examples/simple-factions-addon/pom.xml +++ b/mcspring-examples/simple-factions-addon/pom.xml @@ -56,11 +56,17 @@ generate-resources add-source + add-resource ${project.build.directory}/generated-sources/mc-spring/ + + + ${project.build.directory}/generated-sources/mc-spring/ + + From e9aad7005e67d3056c10c5eec801c485c0c7f329 Mon Sep 17 00:00:00 2001 From: kylepls Date: Sat, 2 May 2020 15:45:03 -0600 Subject: [PATCH 07/54] Start adding tests --- .../mcspring-maven-plugin-test/pom.xml | 70 ------------------- .../test/StartupMessagePlugin.java | 18 ----- .../mcspring/testpackage/TestComponent.java | 7 -- mcspring-build/mcspring-maven-plugin/pom.xml | 12 ++++ .../mcspring/autogenerator/TestGenerator.java | 22 ++++++ .../basic-test/basic-test-plugin-config.xml | 10 +++ mcspring-build/pom.xml | 1 - 7 files changed, 44 insertions(+), 96 deletions(-) delete mode 100644 mcspring-build/mcspring-maven-plugin-test/pom.xml delete mode 100644 mcspring-build/mcspring-maven-plugin-test/src/main/java/in/kyle/mcspring/mavenplugin/test/StartupMessagePlugin.java delete mode 100644 mcspring-build/mcspring-maven-plugin-test/src/main/java/in/kyle/mcspring/testpackage/TestComponent.java create mode 100644 mcspring-build/mcspring-maven-plugin/src/test/java/in/kyle/mcspring/autogenerator/TestGenerator.java create mode 100644 mcspring-build/mcspring-maven-plugin/src/test/resources/unit/basic-test/basic-test-plugin-config.xml diff --git a/mcspring-build/mcspring-maven-plugin-test/pom.xml b/mcspring-build/mcspring-maven-plugin-test/pom.xml deleted file mode 100644 index 9922622..0000000 --- a/mcspring-build/mcspring-maven-plugin-test/pom.xml +++ /dev/null @@ -1,70 +0,0 @@ - - - - - mcspring-starter - in.kyle.mcspring - 0.0.9 - - - 4.0.0 - - mcspring-maven-plugin-test - - - 1.15.1-R0.1-SNAPSHOT - 8 - 8 - - - - - in.kyle.mcspring - mcspring-base - - - in.kyle.mcspring - mcspring-vault - ${project.version} - - - org.projectlombok - lombok - - - - - - in.kyle.mcspring - mcspring-maven-plugin - - - - generate-files - - - - - - org.codehaus.mojo - build-helper-maven-plugin - 3.0.0 - - - generate-resources - - add-source - - - - ${project.build.directory}/generated-sources/mc-spring/ - - - - - - - - \ No newline at end of file diff --git a/mcspring-build/mcspring-maven-plugin-test/src/main/java/in/kyle/mcspring/mavenplugin/test/StartupMessagePlugin.java b/mcspring-build/mcspring-maven-plugin-test/src/main/java/in/kyle/mcspring/mavenplugin/test/StartupMessagePlugin.java deleted file mode 100644 index e97ef34..0000000 --- a/mcspring-build/mcspring-maven-plugin-test/src/main/java/in/kyle/mcspring/mavenplugin/test/StartupMessagePlugin.java +++ /dev/null @@ -1,18 +0,0 @@ -package in.kyle.mcspring.mavenplugin.test; - -import in.kyle.mcspring.annotation.PluginDepend; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; -import org.springframework.stereotype.Component; - -import javax.annotation.PostConstruct; - -@Component -@PluginDepend(plugins = "Vault", soft = true) -public class StartupMessagePlugin { - - @PostConstruct - void message() { - Player player = Bukkit.getPlayer(""); - } -} diff --git a/mcspring-build/mcspring-maven-plugin-test/src/main/java/in/kyle/mcspring/testpackage/TestComponent.java b/mcspring-build/mcspring-maven-plugin-test/src/main/java/in/kyle/mcspring/testpackage/TestComponent.java deleted file mode 100644 index e321e35..0000000 --- a/mcspring-build/mcspring-maven-plugin-test/src/main/java/in/kyle/mcspring/testpackage/TestComponent.java +++ /dev/null @@ -1,7 +0,0 @@ -package in.kyle.mcspring.testpackage; - -import org.springframework.stereotype.Component; - -@Component -public class TestComponent { -} diff --git a/mcspring-build/mcspring-maven-plugin/pom.xml b/mcspring-build/mcspring-maven-plugin/pom.xml index 8812cc5..d085aea 100644 --- a/mcspring-build/mcspring-maven-plugin/pom.xml +++ b/mcspring-build/mcspring-maven-plugin/pom.xml @@ -49,6 +49,18 @@ mcspring-base ${project.version} + + org.apache.maven.plugin-testing + maven-plugin-testing-harness + 3.3.0 + test + + + org.apache.maven + maven-compat + 3.6.3 + test + org.projectlombok lombok diff --git a/mcspring-build/mcspring-maven-plugin/src/test/java/in/kyle/mcspring/autogenerator/TestGenerator.java b/mcspring-build/mcspring-maven-plugin/src/test/java/in/kyle/mcspring/autogenerator/TestGenerator.java new file mode 100644 index 0000000..50b8fd1 --- /dev/null +++ b/mcspring-build/mcspring-maven-plugin/src/test/java/in/kyle/mcspring/autogenerator/TestGenerator.java @@ -0,0 +1,22 @@ +package in.kyle.mcspring.autogenerator; + +import org.apache.maven.plugin.testing.AbstractMojoTestCase; + +import java.io.File; + +public class TestGenerator extends AbstractMojoTestCase { + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + public void testMojoGoal() throws Exception { + File testPom = new File(getBasedir(), + "src/test/resources/unit/basic-test/basic-test-plugin-config.xml"); + McSpringMavenPlugin mojo = (McSpringMavenPlugin) lookupEmptyMojo("generate-files", testPom); + + assertNotNull(mojo); + mojo.execute(); + } +} diff --git a/mcspring-build/mcspring-maven-plugin/src/test/resources/unit/basic-test/basic-test-plugin-config.xml b/mcspring-build/mcspring-maven-plugin/src/test/resources/unit/basic-test/basic-test-plugin-config.xml new file mode 100644 index 0000000..d431930 --- /dev/null +++ b/mcspring-build/mcspring-maven-plugin/src/test/resources/unit/basic-test/basic-test-plugin-config.xml @@ -0,0 +1,10 @@ + + + + + in.kyle.mcspring + mcspring-maven-plugin + + + + diff --git a/mcspring-build/pom.xml b/mcspring-build/pom.xml index 387e0ec..92b8041 100644 --- a/mcspring-build/pom.xml +++ b/mcspring-build/pom.xml @@ -15,7 +15,6 @@ mcspring-archetype mcspring-starter mcspring-maven-plugin - mcspring-maven-plugin-test 4.0.0 From 6a93b286c3ab7ba47135848a8af14e54af9db159 Mon Sep 17 00:00:00 2001 From: kylepls Date: Sat, 2 May 2020 15:47:59 -0600 Subject: [PATCH 08/54] Big change for mah boi josh --- .../kyle/mcspring/autogenerator/McSpringMavenPlugin.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/McSpringMavenPlugin.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/McSpringMavenPlugin.java index b88bb30..1d1253e 100644 --- a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/McSpringMavenPlugin.java +++ b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/McSpringMavenPlugin.java @@ -1,11 +1,10 @@ package in.kyle.mcspring.autogenerator; -import lombok.SneakyThrows; import org.apache.maven.artifact.Artifact; import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugins.annotations.Component; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; -import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.ResolutionScope; import org.apache.maven.project.MavenProject; @@ -16,6 +15,8 @@ import java.util.Set; import java.util.stream.Collectors; +import lombok.SneakyThrows; + @Mojo(name = "generate-files", defaultPhase = LifecyclePhase.PROCESS_CLASSES, requiresDependencyCollection = ResolutionScope.COMPILE_PLUS_RUNTIME, @@ -23,7 +24,7 @@ public class McSpringMavenPlugin extends AbstractMojo { private static final List VALID_SCOPES = Arrays.asList("provided", "compile", "runtime"); - @Parameter(defaultValue = "${project}", required = true) + @Component private MavenProject project; private URLClassLoader fullyQualifiedClassLoader; From d20b252de9400ea789faa81218ecce71da3770be Mon Sep 17 00:00:00 2001 From: Joshua Date: Sat, 2 May 2020 18:44:48 -0400 Subject: [PATCH 09/54] removed private constructors on utility classes closes #24 --- .../main/java/in/kyle/mcspring/autogenerator/FileUtility.java | 3 --- .../in/kyle/mcspring/autogenerator/MainClassUtilities.java | 3 --- 2 files changed, 6 deletions(-) diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/FileUtility.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/FileUtility.java index c5902b4..1d8d696 100644 --- a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/FileUtility.java +++ b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/FileUtility.java @@ -6,9 +6,6 @@ public class FileUtility { - private FileUtility() { - } - public static List getFilesInDirectory(File directory) { List files = new ArrayList<>(); File[] filesInDirectory = directory.listFiles(); diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/MainClassUtilities.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/MainClassUtilities.java index a163c0f..3653bb5 100644 --- a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/MainClassUtilities.java +++ b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/MainClassUtilities.java @@ -6,9 +6,6 @@ public class MainClassUtilities { public static final String ROOT_PACKAGE = "org.springframework.boot.loader"; - private MainClassUtilities() { - } - public static String getMainClassName(MavenProject project) { return project.getArtifactId().replace("-", ""); } From 5c4321c7cd810eb9abde83dd83ff3c898aea13cf Mon Sep 17 00:00:00 2001 From: Joshua Date: Sat, 2 May 2020 18:45:44 -0400 Subject: [PATCH 10/54] fixed up README.md --- mcspring-build/mcspring-maven-plugin/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mcspring-build/mcspring-maven-plugin/README.md b/mcspring-build/mcspring-maven-plugin/README.md index 072f958..35a0341 100644 --- a/mcspring-build/mcspring-maven-plugin/README.md +++ b/mcspring-build/mcspring-maven-plugin/README.md @@ -1,12 +1,12 @@ -##MC-Spring Auto Generation Plugin for Maven +## MC-Spring Auto Generation Plugin for Maven -###What does this do? +### What does this do? This plugin removes the need to create a plugin.yml and main class. At compile time we resolve all dependencies that your plugin uses. Those dependencies will be inserted into the plugin.yml, along with various maven properties that you can define in the pom.xml. -###How does it work? +### How does it work? Step 1: Resolve all maven project dependencies and find the corresponding jars associated with them to add to a class loader. Load all classes from the jars and check for the @PluginDepend annotation provided by MC-Spring. From 399bb78b28a1f52eeaa6efd986a7315c783431b2 Mon Sep 17 00:00:00 2001 From: Joshua Date: Sun, 3 May 2020 23:39:17 -0400 Subject: [PATCH 11/54] re-organized project structure and cleaned up some classes --- ...avenPlugin.java => GenerateFilesMojo.java} | 11 ++-- .../PluginMainClassGenerator.java | 1 + .../autogenerator/PluginYamlAttributes.java | 57 ++++++++----------- ...er.java => ProjectDependencyResolver.java} | 9 ++- .../scan/JarFileDependencyScanner.java | 15 ++--- .../scan/PluginDependAnnotationScanner.java | 11 ++++ .../scan/ProjectDependencyScanner.java | 34 +++++------ .../ProjectSourceAnnotationScanner.java} | 5 +- .../{ => util}/ClassLoadingUtility.java | 2 +- .../autogenerator/{ => util}/FileUtility.java | 2 +- .../{ => util}/MainClassUtilities.java | 2 +- .../mcspring/autogenerator/TestGenerator.java | 2 +- 12 files changed, 77 insertions(+), 74 deletions(-) rename mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/{McSpringMavenPlugin.java => GenerateFilesMojo.java} (89%) rename mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/{PluginDependencyResolver.java => ProjectDependencyResolver.java} (80%) create mode 100644 mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/PluginDependAnnotationScanner.java rename mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/{ProjectSpringPackageResolver.java => scan/ProjectSourceAnnotationScanner.java} (93%) rename mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/{ => util}/ClassLoadingUtility.java (95%) rename mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/{ => util}/FileUtility.java (93%) rename mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/{ => util}/MainClassUtilities.java (93%) diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/McSpringMavenPlugin.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/GenerateFilesMojo.java similarity index 89% rename from mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/McSpringMavenPlugin.java rename to mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/GenerateFilesMojo.java index 1d1253e..80b7f30 100644 --- a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/McSpringMavenPlugin.java +++ b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/GenerateFilesMojo.java @@ -1,5 +1,8 @@ package in.kyle.mcspring.autogenerator; +import in.kyle.mcspring.autogenerator.scan.ProjectSourceAnnotationScanner; +import in.kyle.mcspring.autogenerator.util.ClassLoadingUtility; +import in.kyle.mcspring.autogenerator.util.MainClassUtilities; import org.apache.maven.artifact.Artifact; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugins.annotations.Component; @@ -21,7 +24,7 @@ @Mojo(name = "generate-files", defaultPhase = LifecyclePhase.PROCESS_CLASSES, requiresDependencyCollection = ResolutionScope.COMPILE_PLUS_RUNTIME, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME) -public class McSpringMavenPlugin extends AbstractMojo { +public class GenerateFilesMojo extends AbstractMojo { private static final List VALID_SCOPES = Arrays.asList("provided", "compile", "runtime"); @Component @@ -62,8 +65,8 @@ private void preparePluginYml() { getLog().info("Scanning for project dependencies in qualifying scope"); Set artifacts = getDependencyArtifacts(); getLog().info(String.format("Dependency scan complete. Found %d dependencies", artifacts.size())); - PluginDependencyResolver resolver = new PluginDependencyResolver(fullyQualifiedClassLoader, getSourceClassesFolder(), artifacts); - PluginYamlAttributes attributes = new PluginYamlAttributes(project, resolver, getLog()); + ProjectDependencyResolver resolver = new ProjectDependencyResolver(fullyQualifiedClassLoader, getSourceClassesFolder(), artifacts); + PluginYamlAttributes attributes = new PluginYamlAttributes(project, resolver); attributes.loadAttributes(); getLog().info("Finished obtaining data for plugin.yml"); getLog().info("----------------------------------------------------------------"); @@ -77,7 +80,7 @@ private void preparePluginYml() { private void preparePluginMainClass() { getLog().info("Scanning project sources for spring annotations"); - ProjectSpringPackageResolver scanner = new ProjectSpringPackageResolver(fullyQualifiedClassLoader, getSourceClassesFolder()); + ProjectSourceAnnotationScanner scanner = new ProjectSourceAnnotationScanner(fullyQualifiedClassLoader, getSourceClassesFolder()); scanner.findPackagesThatUseSpring(); Set packages = scanner.getPackagesThatUseSpring(); getLog().info(String.format("Scan complete. Found %d packages with spring annotation", packages.size())); diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginMainClassGenerator.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginMainClassGenerator.java index 8ee0ac6..37556df 100644 --- a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginMainClassGenerator.java +++ b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginMainClassGenerator.java @@ -1,5 +1,6 @@ package in.kyle.mcspring.autogenerator; +import in.kyle.mcspring.autogenerator.util.MainClassUtilities; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import org.apache.maven.project.MavenProject; diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginYamlAttributes.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginYamlAttributes.java index 7fe4aed..65d3fec 100644 --- a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginYamlAttributes.java +++ b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginYamlAttributes.java @@ -1,37 +1,27 @@ package in.kyle.mcspring.autogenerator; import in.kyle.mcspring.annotation.PluginDepend; +import in.kyle.mcspring.autogenerator.util.MainClassUtilities; import lombok.SneakyThrows; -import org.apache.maven.plugin.logging.Log; import org.apache.maven.project.MavenProject; import org.yaml.snakeyaml.Yaml; import java.io.File; import java.io.FileWriter; import java.util.*; -import java.util.stream.Collectors; @SuppressWarnings("unchecked") public class PluginYamlAttributes { - private static final String NAME = "name"; - private static final String MAIN = "main"; - private static final String VERSION = "version"; - private static final String DESC = "description"; - private static final String SOFT_DEPENDENCIES = "softdepend"; - private static final String REQUIRED_DEPENDENCIES = "depend"; - private final Map attributes = new HashMap<>(); + private final List softDependencies = new ArrayList<>(); + private final List requiredDependencies = new ArrayList<>(); private final MavenProject project; - private final PluginDependencyResolver resolver; - private final Log logger; + private final ProjectDependencyResolver resolver; - PluginYamlAttributes(MavenProject project, PluginDependencyResolver resolver, Log logger) { + PluginYamlAttributes(MavenProject project, ProjectDependencyResolver resolver) { this.project = project; this.resolver = resolver; - this.logger = logger; - attributes.put(SOFT_DEPENDENCIES, new ArrayList<>()); - attributes.put(REQUIRED_DEPENDENCIES, new ArrayList<>()); } public Map getAttributes() { @@ -39,33 +29,34 @@ public Map getAttributes() { } public void loadAttributes() { - attributes.put(NAME, project.getName()); - attributes.put(MAIN, MainClassUtilities.getMainClassLocation(project)); - attributes.put(VERSION, project.getVersion()); - attributes.put(DESC, project.getDescription()); - setDependencies(); + attributes.put("name", project.getName()); + attributes.put("main", MainClassUtilities.getMainClassLocation(project)); + attributes.put("version", project.getVersion()); + attributes.put("description", project.getDescription()); + loadDependencies(); } - private void setDependencies() { - logger.info("Scanning dependency classes for PluginDepend annotation"); + private void loadDependencies() { List list = resolver.resolveAllDependencies(); - logger.info(String.format("Scan complete. Found %d classes with PluginDepend annotation", list.size())); - list.forEach(this::addDependency); + loadSoftDependencies(list); + loadRequiredDependencies(list); + attributes.put("softdepend", softDependencies); + attributes.put("depend", requiredDependencies); } - private List getDependencyList(String type) { - return (List) attributes.get(type); + private void loadSoftDependencies(List dependencies) { + dependencies.stream().filter(PluginDepend::soft) + .flatMap(pluginDepend -> Arrays.stream(pluginDepend.plugins())) + .forEach(softDependencies::add); } - private void addDependency(PluginDepend depend) { - List plugins = Arrays.stream(depend.plugins()).collect(Collectors.toList()); - if(depend.soft()) { - getDependencyList(SOFT_DEPENDENCIES).addAll(plugins); - } else { - getDependencyList(REQUIRED_DEPENDENCIES).addAll(plugins); - } + private void loadRequiredDependencies(List dependencies) { + dependencies.stream().filter(pluginDepend -> !pluginDepend.soft()) + .flatMap(pluginDepend -> Arrays.stream(pluginDepend.plugins())) + .forEach(requiredDependencies::add); } + @SneakyThrows public void writeToFile(File destination) { FileWriter writer = new FileWriter(destination); diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginDependencyResolver.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/ProjectDependencyResolver.java similarity index 80% rename from mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginDependencyResolver.java rename to mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/ProjectDependencyResolver.java index 6ee72c1..a0c7293 100644 --- a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginDependencyResolver.java +++ b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/ProjectDependencyResolver.java @@ -5,7 +5,6 @@ import in.kyle.mcspring.autogenerator.scan.ProjectDependencyScanner; import lombok.SneakyThrows; import org.apache.maven.artifact.Artifact; -import org.apache.maven.plugin.logging.Log; import java.io.File; import java.net.URLClassLoader; @@ -15,13 +14,13 @@ import java.util.jar.JarFile; import java.util.stream.Collectors; -public class PluginDependencyResolver { +public class ProjectDependencyResolver { private final URLClassLoader fullyQualifiedClassLoader; private final File sourcesFolder; private final List jars; - public PluginDependencyResolver(URLClassLoader fullyQualifiedClassLoader, File sourcesFolder, Collection dependencies) { + public ProjectDependencyResolver(URLClassLoader fullyQualifiedClassLoader, File sourcesFolder, Collection dependencies) { this.fullyQualifiedClassLoader = fullyQualifiedClassLoader; this.sourcesFolder = sourcesFolder; this.jars = dependencies.stream().map(Artifact::getFile).collect(Collectors.toList()); @@ -42,8 +41,8 @@ public List resolveAllDependencies() { List jars = getJarFiles(); JarFileDependencyScanner jarFileDependencyScanner = new JarFileDependencyScanner(fullyQualifiedClassLoader, jars); ProjectDependencyScanner projectDependencyScanner = new ProjectDependencyScanner(fullyQualifiedClassLoader, sourcesFolder); - dependencies.addAll(jarFileDependencyScanner.getPluginDependAnnotations()); - dependencies.addAll(projectDependencyScanner.getPluginDependAnnotations()); + dependencies.addAll(jarFileDependencyScanner.getScannedAnnotations()); + dependencies.addAll(projectDependencyScanner.getScannedAnnotations()); return dependencies; } diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/JarFileDependencyScanner.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/JarFileDependencyScanner.java index c9145e6..8f6fb1b 100644 --- a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/JarFileDependencyScanner.java +++ b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/JarFileDependencyScanner.java @@ -12,12 +12,13 @@ import java.util.jar.JarFile; @RequiredArgsConstructor -public class JarFileDependencyScanner { +public class JarFileDependencyScanner implements PluginDependAnnotationScanner { private final URLClassLoader classLoader; private final List jarFiles; - public List getPluginDependAnnotations() { + @Override + public List getScannedAnnotations() { List list = new ArrayList<>(); jarFiles.forEach(jarFile -> list.addAll(findJarFileDependencies(jarFile))); return list; @@ -43,12 +44,7 @@ private void addDependencyIfPresent(String className, List destina if(clazz.isAnnotationPresent(PluginDepend.class)) { destination.add(clazz.getAnnotation(PluginDepend.class)); } - //Catching a throwable for optional dependencies not being present. - //It throws a ClassNotFoundException but for some reason we can't catch - //then handle it because WONK. - } catch (Throwable ignored) { - - } + } catch (Throwable ignored) { } } private boolean isClassFile(JarEntry entry) { @@ -60,5 +56,4 @@ private String getClassName(JarEntry entry) { String excludeClassExtension = fullName.substring(0, fullName.length() - 6); return excludeClassExtension.replace("/", "."); } - -} +} \ No newline at end of file diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/PluginDependAnnotationScanner.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/PluginDependAnnotationScanner.java new file mode 100644 index 0000000..d4be184 --- /dev/null +++ b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/PluginDependAnnotationScanner.java @@ -0,0 +1,11 @@ +package in.kyle.mcspring.autogenerator.scan; + +import in.kyle.mcspring.annotation.PluginDepend; + +import java.util.List; + +public interface PluginDependAnnotationScanner { + + List getScannedAnnotations(); + +} diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/ProjectDependencyScanner.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/ProjectDependencyScanner.java index e682594..ae1cc5f 100644 --- a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/ProjectDependencyScanner.java +++ b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/ProjectDependencyScanner.java @@ -1,35 +1,37 @@ package in.kyle.mcspring.autogenerator.scan; import in.kyle.mcspring.annotation.PluginDepend; -import in.kyle.mcspring.autogenerator.FileUtility; +import in.kyle.mcspring.autogenerator.util.FileUtility; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import java.io.File; import java.net.URLClassLoader; -import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; @RequiredArgsConstructor -public class ProjectDependencyScanner { +public class ProjectDependencyScanner implements PluginDependAnnotationScanner { private final URLClassLoader classLoader; private final File sourcesFolder; @SneakyThrows - public List getPluginDependAnnotations() { - List list = new ArrayList<>(); - List files = FileUtility.getFilesInDirectory(sourcesFolder); - for(File file : files) { - if(isClass(file)) { - String className = getClassName(file); - Class clazz = classLoader.loadClass(className); - if(clazz.isAnnotationPresent(PluginDepend.class)) { - list.add(clazz.getAnnotation(PluginDepend.class)); - } - } - } - return list; + @Override + public List getScannedAnnotations() { + return FileUtility.getFilesInDirectory(sourcesFolder) + .stream() + .filter(this::isClass) + .map(this::getClassName) + .map(this::loadClass) + .filter(clazz -> clazz.isAnnotationPresent(PluginDepend.class)) + .map(clazz -> clazz.getAnnotation(PluginDepend.class)) + .collect(Collectors.toList()); + } + + @SneakyThrows + private Class loadClass(String className) { + return classLoader.loadClass(className); } private String getClassName(File classFile) { diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/ProjectSpringPackageResolver.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/ProjectSourceAnnotationScanner.java similarity index 93% rename from mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/ProjectSpringPackageResolver.java rename to mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/ProjectSourceAnnotationScanner.java index 7f00b5b..d16c0b3 100644 --- a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/ProjectSpringPackageResolver.java +++ b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/ProjectSourceAnnotationScanner.java @@ -1,5 +1,6 @@ -package in.kyle.mcspring.autogenerator; +package in.kyle.mcspring.autogenerator.scan; +import in.kyle.mcspring.autogenerator.util.FileUtility; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; import org.springframework.stereotype.Controller; @@ -15,7 +16,7 @@ //Objective: Scan all classes in the provider folder and check for spring annotations. //If present, get the class package and add it to a list for springs package scan at runtime @RequiredArgsConstructor -class ProjectSpringPackageResolver { +public class ProjectSourceAnnotationScanner { private static final List> SPRING_ANNOTATIONS = Arrays.asList( Service.class, diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/ClassLoadingUtility.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/util/ClassLoadingUtility.java similarity index 95% rename from mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/ClassLoadingUtility.java rename to mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/util/ClassLoadingUtility.java index 2307e00..916bcd7 100644 --- a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/ClassLoadingUtility.java +++ b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/util/ClassLoadingUtility.java @@ -1,4 +1,4 @@ -package in.kyle.mcspring.autogenerator; +package in.kyle.mcspring.autogenerator.util; import lombok.SneakyThrows; diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/FileUtility.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/util/FileUtility.java similarity index 93% rename from mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/FileUtility.java rename to mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/util/FileUtility.java index 1d8d696..706b018 100644 --- a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/FileUtility.java +++ b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/util/FileUtility.java @@ -1,4 +1,4 @@ -package in.kyle.mcspring.autogenerator; +package in.kyle.mcspring.autogenerator.util; import java.io.File; import java.util.ArrayList; diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/MainClassUtilities.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/util/MainClassUtilities.java similarity index 93% rename from mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/MainClassUtilities.java rename to mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/util/MainClassUtilities.java index 3653bb5..ca9c378 100644 --- a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/MainClassUtilities.java +++ b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/util/MainClassUtilities.java @@ -1,4 +1,4 @@ -package in.kyle.mcspring.autogenerator; +package in.kyle.mcspring.autogenerator.util; import org.apache.maven.project.MavenProject; diff --git a/mcspring-build/mcspring-maven-plugin/src/test/java/in/kyle/mcspring/autogenerator/TestGenerator.java b/mcspring-build/mcspring-maven-plugin/src/test/java/in/kyle/mcspring/autogenerator/TestGenerator.java index 50b8fd1..6446a8a 100644 --- a/mcspring-build/mcspring-maven-plugin/src/test/java/in/kyle/mcspring/autogenerator/TestGenerator.java +++ b/mcspring-build/mcspring-maven-plugin/src/test/java/in/kyle/mcspring/autogenerator/TestGenerator.java @@ -14,7 +14,7 @@ protected void setUp() throws Exception { public void testMojoGoal() throws Exception { File testPom = new File(getBasedir(), "src/test/resources/unit/basic-test/basic-test-plugin-config.xml"); - McSpringMavenPlugin mojo = (McSpringMavenPlugin) lookupEmptyMojo("generate-files", testPom); + GenerateFilesMojo mojo = (GenerateFilesMojo) lookupEmptyMojo("generate-files", testPom); assertNotNull(mojo); mojo.execute(); From 0616c2aea1d92998aa66b7306cf548ac965b6047 Mon Sep 17 00:00:00 2001 From: Joshua Date: Mon, 4 May 2020 00:50:32 -0400 Subject: [PATCH 12/54] FOR KYLE --- .../kyle/mcspring/autogenerator/PluginYamlAttributes.java | 3 +-- .../mcspring/autogenerator/ProjectDependencyResolver.java | 6 +++--- .../java/in/kyle/mcspring/autogenerator/TestGenerator.java | 1 - 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginYamlAttributes.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginYamlAttributes.java index 65d3fec..a890337 100644 --- a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginYamlAttributes.java +++ b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginYamlAttributes.java @@ -10,10 +10,9 @@ import java.io.FileWriter; import java.util.*; -@SuppressWarnings("unchecked") public class PluginYamlAttributes { - private final Map attributes = new HashMap<>(); + private final Map attributes = new LinkedHashMap<>(); private final List softDependencies = new ArrayList<>(); private final List requiredDependencies = new ArrayList<>(); private final MavenProject project; diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/ProjectDependencyResolver.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/ProjectDependencyResolver.java index a0c7293..5e5db50 100644 --- a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/ProjectDependencyResolver.java +++ b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/ProjectDependencyResolver.java @@ -13,6 +13,7 @@ import java.util.List; import java.util.jar.JarFile; import java.util.stream.Collectors; +import lombok.val; public class ProjectDependencyResolver { @@ -39,11 +40,10 @@ private JarFile getAsJarFile(File file) { public List resolveAllDependencies() { List dependencies = new ArrayList<>(); List jars = getJarFiles(); - JarFileDependencyScanner jarFileDependencyScanner = new JarFileDependencyScanner(fullyQualifiedClassLoader, jars); - ProjectDependencyScanner projectDependencyScanner = new ProjectDependencyScanner(fullyQualifiedClassLoader, sourcesFolder); + val jarFileDependencyScanner = new JarFileDependencyScanner(fullyQualifiedClassLoader, jars); + val projectDependencyScanner = new ProjectDependencyScanner(fullyQualifiedClassLoader, sourcesFolder); dependencies.addAll(jarFileDependencyScanner.getScannedAnnotations()); dependencies.addAll(projectDependencyScanner.getScannedAnnotations()); return dependencies; } - } diff --git a/mcspring-build/mcspring-maven-plugin/src/test/java/in/kyle/mcspring/autogenerator/TestGenerator.java b/mcspring-build/mcspring-maven-plugin/src/test/java/in/kyle/mcspring/autogenerator/TestGenerator.java index 6446a8a..77ca9e3 100644 --- a/mcspring-build/mcspring-maven-plugin/src/test/java/in/kyle/mcspring/autogenerator/TestGenerator.java +++ b/mcspring-build/mcspring-maven-plugin/src/test/java/in/kyle/mcspring/autogenerator/TestGenerator.java @@ -15,7 +15,6 @@ public void testMojoGoal() throws Exception { File testPom = new File(getBasedir(), "src/test/resources/unit/basic-test/basic-test-plugin-config.xml"); GenerateFilesMojo mojo = (GenerateFilesMojo) lookupEmptyMojo("generate-files", testPom); - assertNotNull(mojo); mojo.execute(); } From 28e917133558b531cd366b83450d4039b45744b3 Mon Sep 17 00:00:00 2001 From: kylepls Date: Thu, 7 May 2020 14:49:26 -0600 Subject: [PATCH 13/54] Start conversion --- mcspring-api/mcspring-base/pom.xml | 15 +- .../in/kyle/mcspring/command/Resolver.java | 8 - .../java/in/kyle/mcspring/command/Resolver.kt | 5 + .../command/SimpleCommandFactory.java | 61 ---- .../mcspring/command/SimpleCommandFactory.kt | 56 ++++ .../command/SimpleMethodInjection.java | 82 ----- .../mcspring/command/SimpleMethodInjection.kt | 56 ++++ .../command/SimpleSpringResolver.java | 26 -- .../mcspring/command/SimpleSpringResolver.kt | 18 + .../boot/loader/mcspring/McSpringLoader.java | 5 - .../mcspring/command/TabCommandFactory.java | 114 +++---- .../kyle/mcspring/subcommands/Executors.java | 78 ----- .../mcspring/subcommands/PluginCommand.java | 172 ---------- .../subcommands/PluginCommandBase.java | 261 --------------- .../subcommands/PluginCommandResolver.java | 30 -- .../subcommands/PluginCommandResolver.kt | 29 ++ .../PluginCommandTabCompletable.java | 206 ++++++------ .../plugincommand/PluginCommand.kt | 41 +++ .../plugincommand/PluginCommandBase.kt | 32 ++ .../plugincommand/PluginCommandExecutors.kt | 51 +++ .../plugincommand/PluginCommandRequires.kt | 28 ++ .../plugincommand/PluginCommandWith.kt | 61 ++++ .../javacompat/HighIQExecutors.kt | 74 +++++ .../PluginCommandExecutorsJavaSupport.kt | 194 +++++++++++ .../PluginCommandWithJavaSupport.kt | 21 ++ .../subcommands/tab/TabDiscovery.java | 82 ++--- .../mcspring/subcommands/TestConsole.java | 3 +- .../subcommands/TestJavaPluginCommand.java | 53 +++ .../subcommands/TestPluginCommand.java | 208 ------------ .../mcspring/subcommands/TestPluginCommand.kt | 179 ++++++++++ .../subcommands/TestTabCompletion.java | 310 +++++++++--------- .../test/command/TestCommandRegistration.java | 68 ---- .../test/command/TestCommandRegistration.kt | 71 ++++ mcspring-api/pom.xml | 52 +++ .../mcspring/autogenerator/TestGenerator.java | 12 +- .../mcspring-plugin-manager/pom.xml | 2 + .../java/in/kyle/mcspring/manager/Main.java | 7 - .../manager/commands/CommandAbout.java | 22 -- .../mcspring/manager/commands/CommandAbout.kt | 20 ++ .../manager/commands/CommandClassLoader.java | 31 -- .../manager/commands/CommandClassLoader.kt | 26 ++ .../manager/commands/CommandGamemode.java | 47 --- .../manager/commands/CommandGamemode.kt | 36 ++ .../manager/commands/CommandHeal.java | 26 -- .../mcspring/manager/commands/CommandHeal.kt | 22 ++ .../mcspring/manager/commands/CommandOp.java | 26 -- .../mcspring/manager/commands/CommandOp.kt | 21 ++ .../manager/commands/CommandPlugin.java | 80 ----- .../manager/commands/CommandPlugin.kt | 63 ++++ .../manager/commands/CommandSpeed.java | 27 -- .../mcspring/manager/commands/CommandSpeed.kt | 24 ++ .../controller/BukkitPluginUnloader.java | 96 ------ .../controller/BukkitPluginUnloader.kt | 78 +++++ .../manager/controller/LogFileController.java | 33 -- .../manager/controller/LogFileController.kt | 22 ++ .../manager/controller/PluginController.java | 100 ------ .../manager/controller/PluginController.kt | 86 +++++ .../manager/commands/TestCommandAbout.java | 28 -- .../manager/commands/TestCommandAbout.kt | 24 ++ .../commands/TestCommandClassLoader.java | 30 -- .../commands/TestCommandClassLoader.kt | 22 ++ .../manager/commands/TestCommandGamemode.java | 58 ---- .../manager/commands/TestCommandGamemode.kt | 53 +++ .../manager/commands/TestCommandHeal.java | 24 -- .../manager/commands/TestCommandHeal.kt | 20 ++ .../manager/commands/TestCommandOp.java | 35 -- .../manager/commands/TestCommandOp.kt | 30 ++ .../manager/commands/TestCommandSpeed.java | 40 --- .../manager/commands/TestCommandSpeed.kt | 35 ++ mcspring-build/mcspring-starter/pom.xml | 8 + mcspring-build/pom.xml | 47 +++ .../factions/commands/FactionCommand.java | 8 +- 72 files changed, 2016 insertions(+), 2103 deletions(-) delete mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/Resolver.java create mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/Resolver.kt delete mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleCommandFactory.java create mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleCommandFactory.kt delete mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleMethodInjection.java create mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleMethodInjection.kt delete mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleSpringResolver.java create mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleSpringResolver.kt delete mode 100644 mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/Executors.java delete mode 100644 mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/PluginCommand.java delete mode 100644 mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/PluginCommandBase.java delete mode 100644 mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/PluginCommandResolver.java create mode 100644 mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/PluginCommandResolver.kt create mode 100644 mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommand.kt create mode 100644 mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandBase.kt create mode 100644 mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandExecutors.kt create mode 100644 mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandRequires.kt create mode 100644 mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandWith.kt create mode 100644 mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/javacompat/HighIQExecutors.kt create mode 100644 mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/javacompat/PluginCommandExecutorsJavaSupport.kt create mode 100644 mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/javacompat/PluginCommandWithJavaSupport.kt create mode 100644 mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestJavaPluginCommand.java delete mode 100644 mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestPluginCommand.java create mode 100644 mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestPluginCommand.kt delete mode 100644 mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/command/TestCommandRegistration.java create mode 100644 mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/command/TestCommandRegistration.kt delete mode 100644 mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/Main.java delete mode 100644 mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandAbout.java create mode 100644 mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandAbout.kt delete mode 100644 mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandClassLoader.java create mode 100644 mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandClassLoader.kt delete mode 100644 mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandGamemode.java create mode 100644 mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandGamemode.kt delete mode 100644 mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandHeal.java create mode 100644 mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandHeal.kt delete mode 100644 mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandOp.java create mode 100644 mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandOp.kt delete mode 100644 mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandPlugin.java create mode 100644 mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandPlugin.kt delete mode 100644 mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandSpeed.java create mode 100644 mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandSpeed.kt delete mode 100644 mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/BukkitPluginUnloader.java create mode 100644 mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/BukkitPluginUnloader.kt delete mode 100644 mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/LogFileController.java create mode 100644 mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/LogFileController.kt delete mode 100644 mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/PluginController.java create mode 100644 mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/PluginController.kt delete mode 100644 mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandAbout.java create mode 100644 mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandAbout.kt delete mode 100644 mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandClassLoader.java create mode 100644 mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandClassLoader.kt delete mode 100644 mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandGamemode.java create mode 100644 mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandGamemode.kt delete mode 100644 mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandHeal.java create mode 100644 mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandHeal.kt delete mode 100644 mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandOp.java create mode 100644 mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandOp.kt delete mode 100644 mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandSpeed.java create mode 100644 mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandSpeed.kt diff --git a/mcspring-api/mcspring-base/pom.xml b/mcspring-api/mcspring-base/pom.xml index 420d80f..74d3300 100644 --- a/mcspring-api/mcspring-base/pom.xml +++ b/mcspring-api/mcspring-base/pom.xml @@ -20,6 +20,10 @@ + + org.projectlombok + lombok + org.spigotmc spigot-api @@ -29,6 +33,11 @@ mcspring-jar-loader ${project.version} + + net.jodah + typetools + 0.6.2 + org.springframework.boot spring-boot-loader @@ -59,8 +68,10 @@ test - org.projectlombok - lombok + org.jetbrains.kotlin + kotlin-reflect + 1.3.61 + compile diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/Resolver.java b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/Resolver.java deleted file mode 100644 index c46e9b7..0000000 --- a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/Resolver.java +++ /dev/null @@ -1,8 +0,0 @@ -package in.kyle.mcspring.command; - -import java.lang.reflect.Parameter; -import java.util.Optional; - -public interface Resolver { - Optional resolve(Parameter parameter); -} diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/Resolver.kt b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/Resolver.kt new file mode 100644 index 0000000..7db96cf --- /dev/null +++ b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/Resolver.kt @@ -0,0 +1,5 @@ +package `in`.kyle.mcspring.command + +interface Resolver { + operator fun invoke(parameter: Class<*>): Any? +} diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleCommandFactory.java b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleCommandFactory.java deleted file mode 100644 index 2446180..0000000 --- a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleCommandFactory.java +++ /dev/null @@ -1,61 +0,0 @@ -package in.kyle.mcspring.command; - -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.PluginCommand; -import org.bukkit.plugin.Plugin; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.stereotype.Component; - -import java.lang.reflect.Method; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -import lombok.RequiredArgsConstructor; -import lombok.SneakyThrows; -import lombok.var; - -@Component -@RequiredArgsConstructor -@ConditionalOnBean(Plugin.class) -class SimpleCommandFactory implements BukkitCommandRegistration.CommandFactory { - - private final SimpleMethodInjection methodInjection; - private final Set commandResolvers; - private final Plugin plugin; - - @SneakyThrows - public org.bukkit.command.Command makeCommand(Method method, Object object, String name) { - var constructor = PluginCommand.class.getDeclaredConstructor(String.class, Plugin.class); - constructor.setAccessible(true); - PluginCommand command = constructor.newInstance(name, plugin); - CommandExecutor executor = makeExecutor(method, object); - command.setExecutor(executor); - return command; - } - - CommandExecutor makeExecutor(Method method, Object object) { - return (commandSender, bukkitCommand, label, args) -> { - try { - CommandResolver.Command command = - new CommandResolver.Command(commandSender, args, label); - List contextResolvers = commandResolvers.stream() - .map(r -> r.makeResolver(command)) - .collect(Collectors.toList()); - Object result = methodInjection.invoke(method, - object, - contextResolvers, - commandSender, - args, - label); - if (result != null) { - commandSender.sendMessage(result.toString()); - } - } catch (RuntimeException exception) { - throw new RuntimeException("Could not invoke method " + method.getName(), - exception); - } - return true; - }; - } -} diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleCommandFactory.kt b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleCommandFactory.kt new file mode 100644 index 0000000..52e9873 --- /dev/null +++ b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleCommandFactory.kt @@ -0,0 +1,56 @@ +package `in`.kyle.mcspring.command + +import `in`.kyle.mcspring.command.BukkitCommandRegistration.CommandFactory +import lombok.RequiredArgsConstructor +import lombok.SneakyThrows +import org.bukkit.command.Command +import org.bukkit.command.CommandExecutor +import org.bukkit.command.CommandSender +import org.bukkit.command.PluginCommand +import org.bukkit.plugin.Plugin +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean +import org.springframework.stereotype.Component +import java.lang.reflect.Method +import java.util.stream.Collectors + +@Component +@ConditionalOnBean(Plugin::class) +internal open class SimpleCommandFactory : CommandFactory { + private val methodInjection: SimpleMethodInjection? = null + private val commandResolvers: Set? = null + private val plugin: Plugin? = null + + override fun makeCommand(method: Method, `object`: Any, name: String): Command { + val constructor = PluginCommand::class.java.getDeclaredConstructor(String::class.java, Plugin::class.java) + constructor.isAccessible = true + val command = constructor.newInstance(name, plugin) + val executor = makeExecutor(method, `object`) + command.setExecutor(executor) + return command + } + + fun makeExecutor(method: Method, `object`: Any?): CommandExecutor { + return CommandExecutor { commandSender: CommandSender, bukkitCommand: Command?, label: String?, args: Array? -> + try { +// val command = CommandResolver.Command(commandSender, args, label) +// val contextResolvers = commandResolvers!!.stream() +// .map { r: CommandResolver -> r.makeResolver(command) } +// .collect(Collectors.toList()) + TODO() +// val result: Any = methodInjection.invoke(method, +// `object`, +// contextResolvers, +// commandSender, +// args, +// label) +// if (result != null) { +// commandSender.sendMessage(result.toString()) +// } + } catch (exception: RuntimeException) { + throw RuntimeException("Could not invoke method " + method.name, + exception) + } + true + } + } +} diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleMethodInjection.java b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleMethodInjection.java deleted file mode 100644 index 88aa719..0000000 --- a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleMethodInjection.java +++ /dev/null @@ -1,82 +0,0 @@ -package in.kyle.mcspring.command; - -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Component; -import org.springframework.util.ClassUtils; - -import java.lang.reflect.Method; -import java.lang.reflect.Parameter; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import lombok.RequiredArgsConstructor; -import lombok.SneakyThrows; -import lombok.var; - -/** - * Used to inject method parameters - * Does not support annotated parameters - */ -@Lazy -@Component -@RequiredArgsConstructor -public class SimpleMethodInjection { - - private final List resolvers; - - @SneakyThrows - public Object invoke(Method method, Object object, List resolvers, Object... contextObjects) { - Object[] params = getParameters(method, resolvers, contextObjects); - method.setAccessible(true); - if (params.length != 0) { - return method.invoke(object, params); - } else { - return method.invoke(object); - } - } - - private List makeResolvers(Object... contextObjects) { - return Stream.of(contextObjects) - .filter(Objects::nonNull) - .map(o -> (Resolver) parameter -> ClassUtils.isAssignable(parameter.getType(), - o.getClass()) - ? Optional.of(o) - : Optional.empty()) - .collect(Collectors.toList()); - } - - public Object[] getParameters(Method method, List contextResolvers, Object... contextObjects) { - Parameter[] parameters = method.getParameters(); - Object[] params = new Object[parameters.length]; - - List methodResolvers = new ArrayList<>(); - methodResolvers.addAll(contextResolvers); - methodResolvers.addAll(makeResolvers(contextObjects)); - methodResolvers.addAll(resolvers); - for (int i = 0; i < parameters.length; i++) { - Parameter parameter = parameters[i]; - - for (int j = 0; j < methodResolvers.size(); j++) { - Resolver methodResolver = methodResolvers.get(j); - Optional resolved = methodResolver.resolve(parameter); - if (resolved.isPresent()) { - params[i] = resolved.get(); - var removed = methodResolvers.remove(j); - methodResolvers.add(removed); - break; - } - } - - if (params[i] == null) { - throw new RuntimeException( - "Unable to resolve parameter " + parameter.getType() + " for " + - method.getName()); - } - } - return params; - } -} diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleMethodInjection.kt b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleMethodInjection.kt new file mode 100644 index 0000000..4e4c810 --- /dev/null +++ b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleMethodInjection.kt @@ -0,0 +1,56 @@ +package `in`.kyle.mcspring.command + +import org.springframework.context.annotation.Lazy +import org.springframework.stereotype.Component +import kotlin.reflect.jvm.javaType +import kotlin.reflect.jvm.reflect + +/** + * Used to inject method parameters + * Does not support annotated parameters + */ +@Lazy +@Component +class SimpleMethodInjection( + private val resolvers: List +) { + + fun makeResolvers(vararg contextObjects: Any?): List { + return contextObjects.filterNotNull().map(this::makeResolverFor) + } + + private fun makeResolverFor(contextObject: Any): Resolver { + return object : Resolver { + override fun invoke(parameter: Class<*>): Any? { + return if (contextObject::class.java.isAssignableFrom(parameter)) { + contextObject + } else { + null + } + } + } + } + + fun getParameters(func: Function, contextResolvers: List): Array { + val parameters = func.reflect()!!.parameters.map { it.type.javaType as Class<*> } + return getParameters(parameters, contextResolvers) + } + + fun getParameters(parameters: List>, contextResolvers: List): Array { + val methodResolvers = contextResolvers.plus(resolvers).toMutableList() + + return parameters.map { parameter: Class<*> -> + val candidates = methodResolvers.map { it(parameter) }.toMutableList() + val firstIndex = candidates.indexOfFirst { it != null } + require(firstIndex != -1) { "Unable to resolve parameter $parameter for func($parameters)" } + methodResolvers.cycleElement(firstIndex) + candidates[firstIndex]!! + }.toTypedArray() + } + + private fun MutableList.cycleElement(index: Int): T { + val element = removeAt(index) + add(element) + return element + } +} diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleSpringResolver.java b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleSpringResolver.java deleted file mode 100644 index 8ad17f6..0000000 --- a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleSpringResolver.java +++ /dev/null @@ -1,26 +0,0 @@ -package in.kyle.mcspring.command; - -import org.springframework.beans.factory.NoSuchBeanDefinitionException; -import org.springframework.context.ApplicationContext; -import org.springframework.stereotype.Component; - -import java.lang.reflect.Parameter; -import java.util.Optional; - -import lombok.AllArgsConstructor; - -@Component -@AllArgsConstructor -class SimpleSpringResolver implements Resolver { - - private final ApplicationContext context; - - @Override - public Optional resolve(Parameter parameter) { - try { - return Optional.of(context.getBean(parameter.getType())); - } catch (NoSuchBeanDefinitionException e) { - return Optional.empty(); - } - } -} diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleSpringResolver.kt b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleSpringResolver.kt new file mode 100644 index 0000000..298cd05 --- /dev/null +++ b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleSpringResolver.kt @@ -0,0 +1,18 @@ +package `in`.kyle.mcspring.command + +import org.springframework.beans.factory.NoSuchBeanDefinitionException +import org.springframework.context.ApplicationContext +import org.springframework.stereotype.Component + +@Component +internal class SimpleSpringResolver( + private val context: ApplicationContext +) : Resolver { + override fun invoke(parameter: Class<*>): Any? { + return try { + context.getBean(parameter) + } catch (e: NoSuchBeanDefinitionException) { + null + } + } +} diff --git a/mcspring-api/mcspring-jar-loader/src/main/java/org/springframework/boot/loader/mcspring/McSpringLoader.java b/mcspring-api/mcspring-jar-loader/src/main/java/org/springframework/boot/loader/mcspring/McSpringLoader.java index 0c3a572..ec13147 100644 --- a/mcspring-api/mcspring-jar-loader/src/main/java/org/springframework/boot/loader/mcspring/McSpringLoader.java +++ b/mcspring-api/mcspring-jar-loader/src/main/java/org/springframework/boot/loader/mcspring/McSpringLoader.java @@ -23,11 +23,6 @@ public void launch(ClassLoader classLoader) { } } -// @SneakyThrows -// public void close() { -// getArchive().close(); -// } - @Override protected String getMainClass() { return ""; diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/command/TabCommandFactory.java b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/command/TabCommandFactory.java index 486ac06..8f8f77a 100644 --- a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/command/TabCommandFactory.java +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/command/TabCommandFactory.java @@ -1,65 +1,53 @@ package in.kyle.mcspring.command; -import in.kyle.mcspring.subcommands.PluginCommand; -import org.bukkit.command.Command; -import org.bukkit.command.TabCompleter; -import org.bukkit.plugin.Plugin; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.context.annotation.Primary; -import org.springframework.stereotype.Component; - -import java.lang.reflect.Method; -import java.util.Set; -import java.util.function.Consumer; - -import in.kyle.mcspring.subcommands.tab.TabDiscovery; -import lombok.SneakyThrows; -import lombok.var; - -@Primary -@Component -@ConditionalOnBean(Plugin.class) -class TabCommandFactory extends SimpleCommandFactory { - - private final TabDiscovery tabDiscovery; - - public TabCommandFactory(SimpleMethodInjection injection, - Set commandResolvers, - Plugin plugin, - TabDiscovery tabDiscovery) { - super(injection, commandResolvers, plugin); - this.tabDiscovery = tabDiscovery; - } - - @Override - public Command makeCommand(Method method, Object object, String name) { - var command = (org.bukkit.command.PluginCommand) super.makeCommand(method, object, name); - if (method.getParameterCount() == 1 && method.getParameters()[0].getType() - .isAssignableFrom(PluginCommand.class)) { - command.setTabCompleter(makeTabCompleter(method, object)); - } - return command; - } - - private Consumer methodToConsumer(Method method, - Object object) { - return pluginCommand -> { - method.setAccessible(true); - invoke(method, object, pluginCommand); - }; - } - - @SneakyThrows - private Object invoke(Method method, - Object object, - PluginCommand command) { - return method.invoke(object, command); - } - - private TabCompleter makeTabCompleter(Method method, Object object) { - return (sender, command, s, strings) -> { - var consumer = methodToConsumer(method, object); - return tabDiscovery.getCompletions(sender, String.join(" ", strings), consumer); - }; - } -} +//import in.kyle.mcspring.subcommands.tab.TabDiscovery; +//import lombok.SneakyThrows; +//import lombok.var; +// +//@Primary +//@Component +//@ConditionalOnBean(Plugin.class) +//class TabCommandFactory extends SimpleCommandFactory { +// +// private final TabDiscovery tabDiscovery; +// +// public TabCommandFactory(SimpleMethodInjection injection, +// Set commandResolvers, +// Plugin plugin, +// TabDiscovery tabDiscovery) { +// super(injection, commandResolvers, plugin); +// this.tabDiscovery = tabDiscovery; +// } +// +// @Override +// public Command makeCommand(Method method, Object object, String name) { +// var command = (org.bukkit.command.PluginCommand) super.makeCommand(method, object, name); +// if (method.getParameterCount() == 1 && method.getParameters()[0].getType() +// .isAssignableFrom(PluginCommand.class)) { +// command.setTabCompleter(makeTabCompleter(method, object)); +// } +// return command; +// } +// +// private Consumer methodToConsumer(Method method, +// Object object) { +// return pluginCommand -> { +// method.setAccessible(true); +// invoke(method, object, pluginCommand); +// }; +// } +// +// @SneakyThrows +// private Object invoke(Method method, +// Object object, +// PluginCommand command) { +// return method.invoke(object, command); +// } +// +// private TabCompleter makeTabCompleter(Method method, Object object) { +// return (sender, command, s, strings) -> { +// var consumer = methodToConsumer(method, object); +// return tabDiscovery.getCompletions(sender, String.join(" ", strings), consumer); +// }; +// } +//} diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/Executors.java b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/Executors.java deleted file mode 100644 index 8dffded..0000000 --- a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/Executors.java +++ /dev/null @@ -1,78 +0,0 @@ -package in.kyle.mcspring.subcommands; - -import java.io.Serializable; -import java.lang.invoke.SerializedLambda; -import java.lang.reflect.Method; -import java.util.stream.Stream; - -import lombok.SneakyThrows; - -public interface Executors { - - @SneakyThrows - default Method getMethod(int argCount) { - Method writeReplace = this.getClass().getDeclaredMethod("writeReplace"); - writeReplace.setAccessible(true); - SerializedLambda sl = (SerializedLambda) writeReplace.invoke(this); - String methodName = sl.getImplMethodName(); - Class clazz = Class.forName(sl.getImplClass().replace("/", ".")); - return Stream.of(clazz.getMethods(), clazz.getDeclaredMethods()) - .flatMap(Stream::of) - .filter(m -> m.getName().equals(methodName)) - .filter(m -> m.getParameters().length == argCount) - .findFirst() - .orElseThrow(() -> new RuntimeException("Method not found")); - } - - interface E1 extends Executors, Serializable { - void handle(A a1); - } - - interface E2 extends Executors, Serializable { - void handle(A a, B b); - } - - interface E3 extends Executors, Serializable { - void handle(A a, B b, C c); - } - - interface E4 extends Executors, Serializable { - void handle(A a, B b, C c, D d); - } - - interface E5 extends Executors, Serializable { - void handle(A a, B b, C c, D d, E e); - } - - interface E6 extends Executors, Serializable { - void handle(A a, B b, C c, D d, E e, F f); - } - - interface O0 extends Executors, Serializable { - Object handle(); - } - - interface O1 extends Executors, Serializable { - Object handle(A a1); - } - - interface O2 extends Executors, Serializable { - Object handle(A a, B b); - } - - interface O3 extends Executors, Serializable { - Object handle(A a, B b, C c); - } - - interface O4 extends Executors, Serializable { - Object handle(A a, B b, C c, D d); - } - - interface O5 extends Executors, Serializable { - Object handle(A a, B b, C c, D d, E e); - } - - interface O6 extends Executors, Serializable { - void handle(A a, B b, C c, D d, E e, F f); - } -} diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/PluginCommand.java b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/PluginCommand.java deleted file mode 100644 index 0462a3f..0000000 --- a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/PluginCommand.java +++ /dev/null @@ -1,172 +0,0 @@ -package in.kyle.mcspring.subcommands; - -import in.kyle.mcspring.command.SimpleMethodInjection; -import org.bukkit.command.CommandSender; - -import java.util.List; - -public class PluginCommand extends PluginCommandBase { - - public PluginCommand(SimpleMethodInjection injection, - CommandSender sender, - List parts, - List injections) { - super(injection, sender, parts, injections); - } - - public void on(String command, Executors.O1 e) { - callOn(command, e, 1); - } - - public void on(String command, Executors.O2 e) { - callOn(command, e, 2); - } - - public void on(String command, Executors.O3 e) { - callOn(command, e, 3); - } - - public void on(String command, Executors.O4 e) { - callOn(command, e, 4); - } - - public void on(String command, Executors.O5 e) { - callOn(command, e, 5); - } - - public void on(String command, Executors.O6 e) { - callOn(command, e, 6); - } - - public void on(String command, Executors.O0 executors) { - callOn(command, executors, 0); - } - - public void on(String command, Executors.E1 e) { - callOn(command, e, 1); - } - - public void on(String command, Executors.E2 e) { - callOn(command, e, 2); - } - - public void on(String command, Executors.E3 e) { - callOn(command, e, 3); - } - - public void on(String command, Executors.E4 e) { - callOn(command, e, 4); - } - - public void on(String command, Executors.E5 e) { - callOn(command, e, 5); - } - - public void on(String command, Executors.E6 e) { - callOn(command, e, 6); - } - - public void then(Executors.O0 e) { - then(() -> invoke(e, 0)); - } - - public void then(Executors.O1 e) { - then(() -> invoke(e, 1)); - } - - public void then(Executors.O2 e) { - then(() -> invoke(e, 2)); - } - - public void then(Executors.O3 e) { - then(() -> invoke(e, 3)); - } - - public void then(Executors.O4 e) { - then(() -> invoke(e, 4)); - } - - public void then(Executors.O5 e) { - then(() -> invoke(e, 5)); - } - - public void then(Executors.O6 e) { - then(() -> invoke(e, 6)); - } - - public void then(Executors.E1 e) { - then(() -> invoke(e, 1)); - } - - public void then(Executors.E2 e) { - then(() -> invoke(e, 2)); - } - - public void then(Executors.E3 e) { - then(() -> invoke(e, 3)); - } - - public void then(Executors.E4 e) { - then(() -> invoke(e, 4)); - } - - public void then(Executors.E5 e) { - then(() -> invoke(e, 5)); - } - - public void then(Executors.E6 e) { - then(() -> invoke(e, 6)); - } - - public void otherwise(Executors.O0 e) { - otherwise(() -> invoke(e, 0)); - } - - public void otherwise(Executors.O1 e) { - otherwise(() -> invoke(e, 1)); - } - - public void otherwise(Executors.O2 e) { - otherwise(() -> invoke(e, 2)); - } - - public void otherwise(Executors.O3 e) { - otherwise(() -> invoke(e, 3)); - } - - public void otherwise(Executors.O4 e) { - otherwise(() -> invoke(e, 4)); - } - - public void otherwise(Executors.O5 e) { - otherwise(() -> invoke(e, 5)); - } - - public void otherwise(Executors.O6 e) { - otherwise(() -> invoke(e, 6)); - } - - public void otherwise(Executors.E1 e) { - otherwise(() -> invoke(e, 1)); - } - - public void otherwise(Executors.E2 e) { - otherwise(() -> invoke(e, 2)); - } - - public void otherwise(Executors.E3 e) { - otherwise(() -> invoke(e, 3)); - } - - public void otherwise(Executors.E4 e) { - otherwise(() -> invoke(e, 4)); - } - - public void otherwise(Executors.E5 e) { - otherwise(() -> invoke(e, 5)); - } - - public void otherwise(Executors.E6 e) { - otherwise(() -> invoke(e, 6)); - } -} diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/PluginCommandBase.java b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/PluginCommandBase.java deleted file mode 100644 index e52452a..0000000 --- a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/PluginCommandBase.java +++ /dev/null @@ -1,261 +0,0 @@ -package in.kyle.mcspring.subcommands; - -import in.kyle.mcspring.command.SimpleMethodInjection; -import lombok.RequiredArgsConstructor; -import lombok.SneakyThrows; -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; - -import java.lang.reflect.Method; -import java.util.*; -import java.util.function.*; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -@RequiredArgsConstructor -public class PluginCommandBase { - - final SimpleMethodInjection injection; - final CommandSender sender; - final List parts; - final List injections; - State state = State.CLEAN; - - public void on(String command, Consumer executor) { - if (hasExecutablePart()) { - String part = parts.get(0); - if (command.equalsIgnoreCase(part)) { - parts.remove(0); - executor.accept(copy()); - state = State.EXECUTED; - } - } - } - - private void ifThen(Predicate ifPredicate, Executors executors, int argCount) { - if (state == State.CLEAN && ifPredicate.test(sender)) { - then(() -> invoke(executors, argCount)); - state = State.EXECUTED; - } - } - - public void ifThen(Predicate ifPredicate, Executors.E1 e1) { - ifThen(ifPredicate, e1, 1); - } - - public void withPlayerSender(String error) { - if (state == State.CLEAN && !(sender instanceof Player)) { - sendMessage(error); - state = State.EXECUTED; - } - } - - public void onInvalid(Function help) { - if (hasExecutablePart()) { - String message = help.apply(parts.get(0)); - sendMessage(message); - state = State.EXECUTED; - } - } - - public void otherwise(String message) { - otherwise(() -> message); - } - - public void otherwise(Supplier supplier) { - if (state == State.MISSING_ARG || state == State.CLEAN) { - String message = supplier.get(); - sendMessage(message); - state = State.EXECUTED; - } - } - - public void with(Function> processor, Function error) { - if (hasExecutablePart()) { - String part = parts.remove(0); - Optional apply = processor.apply(part); - if (apply.isPresent()) { - injections.add(apply.get()); - } else { - String message = error.apply(part); - sendMessage(message); - state = State.INVALID_ARG; - } - } else { - state = State.MISSING_ARG; - } - } - - public void withString() { - with(Optional::of, null); - } - - public void withSentence() { - if (hasExecutablePart()) { - String part = String.join(" ", parts); - injections.add(part); - parts.clear(); - } - } - - public void withOfflinePlayer(String notPlayer) { - withOfflinePlayer(s -> notPlayer); - } - - public void withOfflinePlayer(Function notPlayer) { - with(s -> Optional.ofNullable(Bukkit.getOfflinePlayer(s)), notPlayer); - } - - public void withPlayer(String notPlayer) { - withPlayer(s -> notPlayer); - } - - public void withPlayer(Function notPlayer) { - with(s -> Optional.ofNullable(Bukkit.getPlayerExact(s)), notPlayer); - } - - public void withWorld(String notWorld) { - withWorld(s -> notWorld); - } - - public void withWorld(Function notWorld) { - with(s -> Bukkit.getWorlds() - .stream() - .filter(w -> w.getName().equalsIgnoreCase(s)) - .findFirst(), notWorld); - } - - public void withMap(Map options, Function invalidArg) { - if (hasExecutablePart()) { - String part = parts.remove(0).toLowerCase(); - if (options.containsKey(part)) { - injections.add(options.get(part)); - } else { - String message = invalidArg.apply(part); - sendMessage(message); - state = State.INVALID_ARG; - } - } else { - state = State.MISSING_ARG; - } - } - - public void withAny(Collection options, Function invalidArg) { - BinaryOperator merge = (a, b) -> { - throw new RuntimeException("Duplicate option " + a); - }; - Map optionsMap = options.stream() - .collect(Collectors.toMap(Function.identity(), - Function.identity(), - merge, - LinkedHashMap::new)); - withMap(optionsMap, invalidArg); - } - - public void withAny(Function invalidArg, String... options) { - withAny(Arrays.asList(options), invalidArg); - } - - public void withOnlinePlayer(Function playerNotFound) { - with(s -> Optional.ofNullable(Bukkit.getPlayer(s)), playerNotFound); - } - - public void withInt(String notInteger) { - withInt(a -> notInteger); - } - - public void withInt(Function notInteger) { - with(this::tryParseInt, notInteger); - } - - public void withDouble(String notDouble) { - withDouble(a -> notDouble); - } - - public void withDouble(Function notDouble) { - with(this::tryParseDouble, notDouble); - } - - private Optional tryParseInt(String intString) { - try { - return Optional.of(Integer.parseInt(intString)); - } catch (NumberFormatException e) { - return Optional.empty(); - } - } - - private Optional tryParseDouble(String intString) { - try { - return Optional.of(Double.parseDouble(intString)); - } catch (NumberFormatException e) { - return Optional.empty(); - } - } - - protected void callOn(String command, Executors executors, int argSize) { - if(hasExecutablePart()) { - if(parts.get(0).equalsIgnoreCase(command)) { - parts.remove(0); - then(() -> invoke(executors, argSize)); - } - } else { - state = State.MISSING_ARG; - } - } - - public void otherwise(Runnable r) { - if (state == State.MISSING_ARG || state == State.CLEAN) { - r.run(); - state = State.EXECUTED; - } - } - - public void then(Runnable r) { - if (parts.size() == 0 && state == State.CLEAN) { - r.run(); - state = State.EXECUTED; - } - } - - void sendMessage(String message) { - sender.sendMessage(ChatColor.translateAlternateColorCodes('&', message)); - } - - PluginCommand copy() { - return new PluginCommand(injection, sender, parts, injections); - } - - @SneakyThrows - protected void invoke(Executors executors, int argCount) { - Method method = executors.getMethod(argCount); - injections.add(sender); - Object[] objects = injections.toArray(new Object[0]); - Object[] parameters = injection.getParameters(method, Collections.emptyList(), objects); - Method handleMethod = getHandleMethod(executors); - handleMethod.setAccessible(true); - Object output = handleMethod.invoke(executors, parameters); - if (output != null) { - sendMessage(output.toString()); - } - } - - boolean hasExecutablePart() { - return parts.size() > 0 && state == State.CLEAN; - } - - private Method getHandleMethod(Executors executors) { - return Stream.of(executors.getClass().getDeclaredMethods()) - .filter(m -> m.getName().equals("handle")) - .findFirst() - .orElseThrow(RuntimeException::new); - } - - public enum State { - CLEAN, - MISSING_ARG, - INVALID_ARG, - EXECUTED - } -} diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/PluginCommandResolver.java b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/PluginCommandResolver.java deleted file mode 100644 index 2ef1ef7..0000000 --- a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/PluginCommandResolver.java +++ /dev/null @@ -1,30 +0,0 @@ -package in.kyle.mcspring.subcommands; - -import org.springframework.stereotype.Component; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Optional; - -import in.kyle.mcspring.command.CommandResolver; -import in.kyle.mcspring.command.Resolver; -import in.kyle.mcspring.command.SimpleMethodInjection; -import lombok.AllArgsConstructor; - -@Component -@AllArgsConstructor -class PluginCommandResolver implements CommandResolver { - - private final SimpleMethodInjection injection; - - @Override - public Resolver makeResolver(Command command) { - return parameter -> parameter.getType().isAssignableFrom(PluginCommand.class) ? Optional.of( - makeCommand(command)) : Optional.empty(); - } - - private PluginCommand makeCommand(Command command) { - ArrayList args = new ArrayList<>(Arrays.asList(command.getArgs())); - return new PluginCommand(injection, command.getSender(), args, new ArrayList<>()); - } -} diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/PluginCommandResolver.kt b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/PluginCommandResolver.kt new file mode 100644 index 0000000..c855097 --- /dev/null +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/PluginCommandResolver.kt @@ -0,0 +1,29 @@ +package `in`.kyle.mcspring.subcommands + +import `in`.kyle.mcspring.command.CommandResolver +import `in`.kyle.mcspring.command.Resolver +import `in`.kyle.mcspring.command.SimpleMethodInjection +import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommand +import lombok.AllArgsConstructor +import org.springframework.stereotype.Component +import java.util.* + +@Component +@AllArgsConstructor +internal class PluginCommandResolver( + private val injection: SimpleMethodInjection +) : CommandResolver { + override fun makeResolver(command: CommandResolver.Command): Resolver { + return object : Resolver { + override fun invoke(parameter: Class<*>): Any? { + return if (parameter.isAssignableFrom(PluginCommand::class.java)) Optional.of( + makeCommand(command)) else Optional.empty() + } + } + } + + private fun makeCommand(command: CommandResolver.Command): PluginCommand { + val args = mutableListOf(*command.args) + return PluginCommand(injection, command.sender, args) + } +} diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/PluginCommandTabCompletable.java b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/PluginCommandTabCompletable.java index 0f3c076..5786ae7 100644 --- a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/PluginCommandTabCompletable.java +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/PluginCommandTabCompletable.java @@ -1,102 +1,104 @@ -package in.kyle.mcspring.subcommands; - -import org.bukkit.command.CommandSender; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; - -import in.kyle.mcspring.command.SimpleMethodInjection; -import lombok.Getter; -import lombok.var; - -public class PluginCommandTabCompletable extends PluginCommand { - - @Getter - private final List tabCompletionOptions = new ArrayList<>(); - @Getter - private PluginCommandTabCompletable child; - - public PluginCommandTabCompletable(SimpleMethodInjection injection, - CommandSender sender, - List parts) { - super(injection, sender, parts, Collections.emptyList()); - } - - public State getState() { - return state; - } - - public boolean hasChild() { - return child != null; - } - - @Override - public void on(String command, Consumer executor) { - tabCompletionOptions.add(command); - super.on(command, executor); - } - - @Override - public void onInvalid(Function help) { - state = State.INVALID_ARG; - } - - @Override - public void with(Function> processor, Function error) { - tabCompletionOptions.clear(); - if (hasExecutablePart()) { - parts.remove(0); - state = State.INVALID_ARG; - } else { - state = State.MISSING_ARG; - } - } - - @Override - public void withMap(Map options, Function invalidArg) { - if (hasExecutablePart()) { - Set validOptions = options.keySet(); - String part = parts.remove(0).toLowerCase(); - if (!validOptions.contains(part)) { - state = State.INVALID_ARG; - } - } else { - state = State.MISSING_ARG; - tabCompletionOptions.addAll(options.keySet()); - } - } - - @Override - protected void callOn(String command, Executors executors, int argSize) { - tabCompletionOptions.add(command); - super.callOn(command, executors, argSize); - } - - @Override - public void otherwise(Supplier supplier) { - } - - - @Override - public void then(Runnable r) { - } - - @Override - PluginCommand copy() { - var command = - new PluginCommandTabCompletable(injection, sender, parts); - if (child == null) { - child = command; - return command; - } else { - throw new IllegalStateException("Command cannot have 2 children"); - } - } -} +//package in.kyle.mcspring.subcommands; +// +//import org.bukkit.command.CommandSender; +// +//import java.util.ArrayList; +//import java.util.Collections; +//import java.util.List; +//import java.util.Map; +//import java.util.Optional; +//import java.util.Set; +//import java.util.function.Consumer; +//import java.util.function.Function; +//import java.util.function.Supplier; +// +//import in.kyle.mcspring.command.SimpleMethodInjection; +//import in.kyle.mcspring.subcommands.plugincommand.Executors; +//import in.kyle.mcspring.subcommands.plugincommand.PluginCommand; +//import lombok.Getter; +//import lombok.var; +// +//public class PluginCommandTabCompletable extends PluginCommand { +// +// @Getter +// private final List tabCompletionOptions = new ArrayList<>(); +// @Getter +// private PluginCommandTabCompletable child; +// +// public PluginCommandTabCompletable(SimpleMethodInjection injection, +// CommandSender sender, +// List parts) { +// super(injection, sender, parts, Collections.emptyList()); +// } +// +// public State getState() { +// return state; +// } +// +// public boolean hasChild() { +// return child != null; +// } +// +// @Override +// public void on(String command, Consumer executor) { +// tabCompletionOptions.add(command); +// super.on(command, executor); +// } +// +// @Override +// public void onInvalid(Function help) { +// state = State.INVALID_ARG; +// } +// +// @Override +// public void with(Function> processor, Function error) { +// tabCompletionOptions.clear(); +// if (hasExecutablePart()) { +// parts.remove(0); +// state = State.INVALID_ARG; +// } else { +// state = State.MISSING_ARG; +// } +// } +// +// @Override +// public void withMap(Map options, Function invalidArg) { +// if (hasExecutablePart()) { +// Set validOptions = options.keySet(); +// String part = parts.remove(0).toLowerCase(); +// if (!validOptions.contains(part)) { +// state = State.INVALID_ARG; +// } +// } else { +// state = State.MISSING_ARG; +// tabCompletionOptions.addAll(options.keySet()); +// } +// } +// +// @Override +// protected void callOn(String command, Executors executors, int argSize) { +// tabCompletionOptions.add(command); +// super.callOn(command, executors, argSize); +// } +// +// @Override +// public void otherwise(Supplier supplier) { +// } +// +// +// @Override +// public void then(Runnable r) { +// } +// +// @Override +// PluginCommand copy() { +// var command = +// new PluginCommandTabCompletable(injection, sender, parts); +// if (child == null) { +// child = command; +// return command; +// } else { +// throw new IllegalStateException("Command cannot have 2 children"); +// } +// } +//} diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommand.kt b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommand.kt new file mode 100644 index 0000000..f9a31f7 --- /dev/null +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommand.kt @@ -0,0 +1,41 @@ +package `in`.kyle.mcspring.subcommands.plugincommand + +import `in`.kyle.mcspring.command.SimpleMethodInjection +import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandBase.State +import `in`.kyle.mcspring.subcommands.plugincommand.javacompat.PluginCommandExecutorsJavaSupport +import `in`.kyle.mcspring.subcommands.plugincommand.javacompat.PluginCommandWithJavaSupport +import org.bukkit.ChatColor +import org.bukkit.command.CommandSender + +open class PluginCommand( + override val injection: SimpleMethodInjection, + override val sender: CommandSender, + override val parts: MutableList +) : PluginCommandBase, PluginCommandWith, PluginCommandRequires, PluginCommandExecutors, + PluginCommandExecutorsJavaSupport, PluginCommandWithJavaSupport { + + override val injections = mutableListOf() + override var state: State = State.CLEAN + + override fun sendMessage(message: String) { + if (message.isNotBlank()) { + sender.sendMessage(ChatColor.translateAlternateColorCodes('&', message)) + } + } + + override fun dirtiesState(predicate: Boolean, + requiredStates: Array, + resultingState: State, + action: () -> Unit) { + if (state in requiredStates && predicate) { + action() + state = resultingState + } + } + + override fun nextPart(): String? = parts.getOrNull(0).takeIf { state == State.CLEAN } + + override fun consumePart() { + parts.removeAt(0) + } +} diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandBase.kt b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandBase.kt new file mode 100644 index 0000000..dc3a819 --- /dev/null +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandBase.kt @@ -0,0 +1,32 @@ +package `in`.kyle.mcspring.subcommands.plugincommand + +import org.bukkit.command.CommandSender +import kotlin.reflect.KFunction + +interface PluginCommandBase { + + val injections: MutableList + val parts: MutableList + val sender: CommandSender + + var state: State + + fun nextPart(): String? + + fun consumePart() + + fun dirtiesState(predicate: Boolean = true, + requiredStates: Array = arrayOf(State.CLEAN), + resultingState: State = State.SUCCESS, + action: () -> Unit) + + fun sendMessage(message: String) + + fun run(function: KFunction) + + enum class State { + CLEAN, + MISSING_ARG, + SUCCESS + } +} diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandExecutors.kt b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandExecutors.kt new file mode 100644 index 0000000..35ad804 --- /dev/null +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandExecutors.kt @@ -0,0 +1,51 @@ +package `in`.kyle.mcspring.subcommands.plugincommand + +import `in`.kyle.mcspring.command.SimpleMethodInjection +import kotlin.reflect.KFunction +import kotlin.reflect.jvm.isAccessible + +interface PluginCommandExecutors : PluginCommandBase { + + val injection: SimpleMethodInjection + + fun otherwise(e: KFunction) { + dirtiesState(requiredStates = arrayOf(PluginCommandBase.State.CLEAN, PluginCommandBase.State.MISSING_ARG)) { run(e) } + } + + fun on(command: String, e: KFunction) { + if (nextPart().equals(command)) { + dirtiesState { run(e) } + } + } + + fun onInvalid(errorMessage: (String) -> String) { + if (nextPart() != null) { + dirtiesState { + sendMessage(errorMessage(parts[0])) + } + } + } + + fun then(e: KFunction) { + dirtiesState { run(e) } + } + + override fun run(function: KFunction) { + val contextResolvers = injection.makeResolvers(sender, makeNextExecutor(), *injections.toTypedArray()) + val parameters = injection.getParameters(function, contextResolvers) + function.isAccessible = true + val output = function.call(*parameters) + if (output !is Unit) { + sendMessage(output.toString()) + } + } + + fun makeNextExecutor(): PluginCommand? { + return if (parts.isNotEmpty()) { + PluginCommand(injection, sender, parts).apply { + injections.addAll(this.injections) + parts.removeAt(0) + } + } else null + } +} diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandRequires.kt b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandRequires.kt new file mode 100644 index 0000000..69c1976 --- /dev/null +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandRequires.kt @@ -0,0 +1,28 @@ +package `in`.kyle.mcspring.subcommands.plugincommand + +import org.bukkit.command.ConsoleCommandSender +import org.bukkit.entity.Player + +interface PluginCommandRequires : PluginCommandBase { + + fun requires(predicate: Boolean, errorMessage: () -> String) { + if (!predicate) { + dirtiesState { sendMessage(errorMessage()) } + } + } + + fun requiresPlayerSender(errorMessage: () -> String) { + requires(sender is Player, errorMessage) + } + + fun requireConsoleSender(errorMessage: () -> String) { + requires(sender is ConsoleCommandSender, errorMessage) + } + + fun requiresPermission(permission: String, errorMessage: () -> String) { + requires(sender.hasPermission(permission), errorMessage) + } + + fun requiresOp(errorMessage: () -> String) = requires(sender.isOp, errorMessage) + +} diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandWith.kt b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandWith.kt new file mode 100644 index 0000000..2ff3105 --- /dev/null +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandWith.kt @@ -0,0 +1,61 @@ +package `in`.kyle.mcspring.subcommands.plugincommand + +import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandBase.State +import org.bukkit.Bukkit + +interface PluginCommandWith : PluginCommandBase { + + fun withString() = with({ it }) + + fun withSentence() { + if (nextPart() != null) { + val out = injections.add(parts.joinToString()) + parts.clear() + } + } + + fun withInt(errorMessage: (String) -> String) = with({ it.toIntOrNull() }, errorMessage) + + fun withDouble(errorMessage: (String) -> String) = with({ it.toDoubleOrNull() }, errorMessage) + + fun withOfflinePlayer(errorMessage: (String) -> String) { + with({ Bukkit.getOfflinePlayer(it) }, errorMessage) + } + + fun withPlayer(errorMessage: (String) -> String) { + with({ Bukkit.getPlayer(it) }, errorMessage) + } + + fun withWorld(errorMessage: (String) -> String) { + with({ worldName: String -> + Bukkit.getWorlds().find { it.name === worldName } + }, errorMessage) + } + + fun withXYZInt(errorMessage: (String) -> String) { + repeat(3) { withInt(errorMessage) } + } + + fun withMap(options: Map, errorMessage: (String) -> String) { + with({ options[it] }, errorMessage) + } + + fun withAny(options: Collection, errorMessage: (String) -> String) { + withMap(options.associateBy { it }, errorMessage) + } + + fun with(processor: (String) -> Any?, errorMessage: (String) -> String = { "" }) { + val part = nextPart() + if (part != null) { + val parsed = processor(part) + if (parsed != null) { + consumePart() + injections.add(parsed) + } else { + dirtiesState { sendMessage(errorMessage(part)) } + } + } else { + dirtiesState(resultingState = State.MISSING_ARG) {} + } + } +} diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/javacompat/HighIQExecutors.kt b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/javacompat/HighIQExecutors.kt new file mode 100644 index 0000000..14bbb83 --- /dev/null +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/javacompat/HighIQExecutors.kt @@ -0,0 +1,74 @@ +package `in`.kyle.mcspring.subcommands.plugincommand.javacompat + +import java.io.Serializable +import java.lang.invoke.SerializedLambda +import java.lang.reflect.Method + +interface HighIQExecutors { + + @JvmDefault + fun getMethod(e: HighIQExecutors): Method { + val argCount = this::class.java.declaredMethods.find { it.name == "handle" }!!.parameterCount + val writeReplace = this::class.java.getDeclaredMethod("writeReplace") + writeReplace.isAccessible = true + val sl = writeReplace.invoke(e) as SerializedLambda + val methodName = sl.implMethodName + val clazz = Class.forName(sl.implClass.replace("/", ".")) + + return clazz.methods.plus(clazz.declaredMethods) + .filter { it.name == methodName }.first { it.parameters.size == argCount } + ?: throw RuntimeException("Method not found") + } + + interface E1 : HighIQExecutors, Serializable { + fun handle(a1: A) + } + + interface E2 : HighIQExecutors, Serializable { + fun handle(a: A, b: B) + } + + interface E3 : HighIQExecutors, Serializable { + fun handle(a: A, b: B, c: C) + } + + interface E4 : HighIQExecutors, Serializable { + fun handle(a: A, b: B, c: C, d: D) + } + + interface E5 : HighIQExecutors, Serializable { + fun handle(a: A, b: B, c: C, d: D, e: E) + } + + interface E6 : HighIQExecutors, Serializable { + fun handle(a: A, b: B, c: C, d: D, e: E, f: F) + } + + interface O0 : HighIQExecutors, Serializable { + fun handle(): Any? + } + + interface O1 : HighIQExecutors, Serializable { + fun handle(a1: A): Any? + } + + interface O2 : HighIQExecutors, Serializable { + fun handle(a: A, b: B): Any? + } + + interface O3 : HighIQExecutors, Serializable { + fun handle(a: A, b: B, c: C): Any? + } + + interface O4 : HighIQExecutors, Serializable { + fun handle(a: A, b: B, c: C, d: D): Any? + } + + interface O5 : HighIQExecutors, Serializable { + fun handle(a: A, b: B, c: C, d: D, e: E): Any? + } + + interface O6 : HighIQExecutors, Serializable { + fun handle(a: A, b: B, c: C, d: D, e: E, f: F): Any? + } +} diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/javacompat/PluginCommandExecutorsJavaSupport.kt b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/javacompat/PluginCommandExecutorsJavaSupport.kt new file mode 100644 index 0000000..caa4a8d --- /dev/null +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/javacompat/PluginCommandExecutorsJavaSupport.kt @@ -0,0 +1,194 @@ +package `in`.kyle.mcspring.subcommands.plugincommand.javacompat + +import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandBase.State +import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandExecutors +import `in`.kyle.mcspring.subcommands.plugincommand.javacompat.HighIQExecutors.* +import net.jodah.typetools.TypeResolver +import java.lang.invoke.SerializedLambda +import java.lang.reflect.Method +import kotlin.jvm.internal.FunctionReference +import kotlin.reflect.KFunction +import kotlin.reflect.jvm.javaMethod +import kotlin.reflect.jvm.kotlinFunction + +@Suppress("UNCHECKED_CAST") +interface PluginCommandExecutorsJavaSupport : PluginCommandExecutors { + + fun on(command: String, e: O1) { + this.on(command, e::handle as KFunction) + } + + fun on(command: String, e: O2) { + this.on(command, e::handle as KFunction) + } + + fun on(command: String, e: O3) { + this.on(command, e::handle as KFunction) + } + + fun on(command: String, e: O4) { + this.on(command, e::handle as KFunction) + } + + fun on(command: String, e: O5) { + this.on(command, e::handle as KFunction) + } + + fun on(command: String, e: O6) { + this.on(command, e::handle as KFunction) + } + + fun on(command: String, e: O0) { + this.on(command, e::handle as KFunction) + } + + fun on(command: String, e: E1) { + this.on(command, e::handle as KFunction) + } + + fun on(command: String, e: E2) { + this.on(command, e::handle as KFunction) + } + + fun on(command: String, e: E3) { + this.on(command, e::handle as KFunction) + } + + fun on(command: String, e: E4) { + this.on(command, e::handle as KFunction) + } + + fun on(command: String, e: E5) { + this.on(command, e::handle as KFunction) + } + + fun on(command: String, e: E6) { + this.on(command, e::handle as KFunction) + } + + fun then(e: O0) { + this.then(e, e::handle as KFunction) + } + + fun then(e: O1) { + this.then(e, e::handle as KFunction) + } + + fun then(e: O2) { + this.then(e, e::handle as KFunction) + } + + fun then(e: O3) { + this.then(e, e::handle as KFunction) + } + + fun then(e: O4) { + this.then(e, e::handle as KFunction) + } + + fun then(e: O5) { + this.then(e, e::handle as KFunction) + } + + fun then(e: O6) { + this.then(e, e::handle as KFunction) + } + + fun then(e: E1) { + this.then(e, e::handle as KFunction) + } + + fun then(e: E2) { + this.then(e, e::handle as KFunction) + } + + fun then(e: E3) { + this.then(e, e::handle as KFunction) + } + + fun then(e: E4) { + this.then(e, e::handle as KFunction) + } + + fun then(e: E5) { + this.then(e, e::handle as KFunction) + } + + fun then(e: E6) { + this.then(e, e::handle as KFunction) + } + + fun otherwise(e: O0) { + this.then(e, e::handle as KFunction) + } + + fun otherwise(e: O1) { + this.then(e, e::handle as KFunction) + } + + fun otherwise(e: O2) { + this.then(e, e::handle as KFunction) + } + + fun otherwise(e: O3) { + this.then(e, e::handle as KFunction) + } + + fun otherwise(e: O4) { + this.then(e, e::handle as KFunction) + } + + fun otherwise(e: O5) { + this.then(e, e::handle as KFunction) + } + + fun otherwise(e: O6) { + this.then(e, e::handle as KFunction) + } + + fun otherwise(e: E1) { + this.then(e, e::handle as KFunction) + } + + fun otherwise(e: E2) { + this.then(e, e::handle as KFunction) + } + + fun otherwise(e: E3) { + this.then(e, e::handle as KFunction) + } + + fun otherwise(e: E4) { + this.then(e, e::handle as KFunction) + } + + fun otherwise(e: E5) { + this.then(e, e::handle as KFunction) + } + + fun otherwise(e: E6) { + this.then(e, e::handle as KFunction) + } + + private fun then(e: HighIQExecutors, function: KFunction) { + val method = e.getMethod(e) + val types: List> = method.parameterTypes.toList() + val contextResolvers = injection.makeResolvers(sender, makeNextExecutor(), *injections.toTypedArray()) + dirtiesState { + val parameters = injection.getParameters(types, contextResolvers) + method.isAccessible = true + method.invoke(e, *parameters) + } + if (function.isAbstract) {// java method + val target = (function as FunctionReference).boundReceiver + val claz = function.javaMethod!!.declaringClass; + val args = TypeResolver.resolveRawArguments(function.javaMethod!!.declaringClass, target.javaClass) + println(args) + } + } + + fun otherwise(message: String) { + dirtiesState(requiredStates = arrayOf(State.CLEAN, State.MISSING_ARG)) { sendMessage(message) } + } +} + diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/javacompat/PluginCommandWithJavaSupport.kt b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/javacompat/PluginCommandWithJavaSupport.kt new file mode 100644 index 0000000..17a0ecd --- /dev/null +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/javacompat/PluginCommandWithJavaSupport.kt @@ -0,0 +1,21 @@ +package `in`.kyle.mcspring.subcommands.plugincommand.javacompat + +import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandWith + +interface PluginCommandWithJavaSupport : PluginCommandWith { + + fun withInt(errorMessage: String) = super.withInt { errorMessage } + + fun withDouble(errorMessage: String) = super.withDouble { errorMessage } + + fun withPlayer(errorMessage: String) = super.withPlayer { errorMessage } + + fun withWorld(errorMessage: String) = super.withWorld { errorMessage } + + fun withXYZInt(errorMessage: String) = super.withXYZInt { errorMessage } + + fun withMap(options: Map, errorMessage: String) = with({ options[it] }, { errorMessage }) + + fun withAny(options: Collection, errorMessage: String) = with({ options.associateBy { it } }, { errorMessage }) + +} diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/tab/TabDiscovery.java b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/tab/TabDiscovery.java index 014453a..e64ad56 100644 --- a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/tab/TabDiscovery.java +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/tab/TabDiscovery.java @@ -1,51 +1,35 @@ package in.kyle.mcspring.subcommands.tab; -import org.bukkit.command.CommandSender; -import org.springframework.stereotype.Component; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.function.Consumer; - -import in.kyle.mcspring.command.SimpleMethodInjection; -import in.kyle.mcspring.subcommands.PluginCommand; -import in.kyle.mcspring.subcommands.PluginCommandTabCompletable; -import lombok.RequiredArgsConstructor; -import lombok.var; - -@Component -@RequiredArgsConstructor -public class TabDiscovery { - - private final SimpleMethodInjection injection; - - public List getCompletions(CommandSender sender, - String commandString, - Consumer consumer) { - if (!commandString.isEmpty() && !commandString.endsWith(" ")) { - // TODO: 2020-02-25 Enable partial completions - return Collections.emptyList(); - } - var parts = new ArrayList<>(Arrays.asList(commandString.split(" "))); - if (parts.get(0).isEmpty()) { - parts.remove(0); - } - var command = new PluginCommandTabCompletable(injection, sender, parts); - consumer.accept(command); - - return getCompletions(command); - } - - private List getCompletions(PluginCommandTabCompletable command) { - if (command.hasChild()) { - return getCompletions(command.getChild()); - } else if (command.getState() == PluginCommand.State.MISSING_ARG || - command.getState() == PluginCommand.State.CLEAN) { - return command.getTabCompletionOptions(); - } else { - return Collections.emptyList(); - } - } -} +//@Component +//@RequiredArgsConstructor +//public class TabDiscovery { +// +// private final SimpleMethodInjection injection; +// +// public List getCompletions(CommandSender sender, +// String commandString, +// Consumer consumer) { +// if (!commandString.isEmpty() && !commandString.endsWith(" ")) { +// // TODO: 2020-02-25 Enable partial completions +// return Collections.emptyList(); +// } +// var parts = new ArrayList<>(Arrays.asList(commandString.split(" "))); +// if (parts.get(0).isEmpty()) { +// parts.remove(0); +// } +// var command = new PluginCommandTabCompletable(injection, sender, parts); +// consumer.accept(command); +// +// return getCompletions(command); +// } +// +// private List getCompletions(PluginCommandTabCompletable command) { +// if (command.hasChild()) { +// return getCompletions(command.getChild()); +// } else if (command.getState() == State.MISSING_ARG || command.getState() == State.CLEAN) { +// return command.getTabCompletionOptions(); +// } else { +// return Collections.emptyList(); +// } +// } +//} diff --git a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestConsole.java b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestConsole.java index 7c0d04c..c5addf6 100644 --- a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestConsole.java +++ b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestConsole.java @@ -9,6 +9,7 @@ import java.util.function.Consumer; import in.kyle.mcspring.command.SimpleMethodInjection; +import in.kyle.mcspring.subcommands.plugincommand.PluginCommand; import lombok.RequiredArgsConstructor; @Component @@ -22,7 +23,7 @@ public void run(CommandSender sender, String commandString, Consumer()); + PluginCommand command = new PluginCommand(injection, sender, parts); consumer.accept(command); } } diff --git a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestJavaPluginCommand.java b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestJavaPluginCommand.java new file mode 100644 index 0000000..2aae169 --- /dev/null +++ b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestJavaPluginCommand.java @@ -0,0 +1,53 @@ +package in.kyle.mcspring.subcommands; + +import org.bukkit.entity.Player; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.ArrayList; +import java.util.List; + +import in.kyle.mcspring.subcommands.plugincommand.PluginCommand; +import in.kyle.mcspring.test.MCSpringTest; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +@MCSpringTest +public class TestJavaPluginCommand { + + @Autowired + TestConsole console; + + Player sender; + List messages; + + @BeforeEach + void setup() { + messages = new ArrayList<>(); + sender = mock(Player.class); + doAnswer(invocationOnMock -> { + messages.add(invocationOnMock.getArgument(0)); + return null; + }).when(sender).sendMessage(anyString()); + } + + @Test + public void testSimple() { + class Test { + void parse(PluginCommand command) { + command.withString(); + command.then(this::handle); + } + + String handle(Player sender, String arg) { + return arg; + } + } + + console.run(sender, "test-arg", new Test()::parse); + assertThat(messages).containsExactly("test-arg"); + } + +} diff --git a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestPluginCommand.java b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestPluginCommand.java deleted file mode 100644 index 1f6ceb0..0000000 --- a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestPluginCommand.java +++ /dev/null @@ -1,208 +0,0 @@ -package in.kyle.mcspring.subcommands; - -import org.bukkit.command.CommandSender; -import org.bukkit.permissions.ServerOperator; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; - -import in.kyle.api.bukkit.entity.TestPlayer; -import in.kyle.mcspring.test.MCSpringTest; - -import static org.assertj.core.api.Assertions.assertThat; - -@MCSpringTest -class TestPluginCommand { - - @Autowired - TestConsole console; - @Autowired - TestPlayer sender; - List outputMessages; - - @BeforeEach - void setup() { - outputMessages = new ArrayList<>(); - sender.getMessages().subscribe(outputMessages::add); - } - - @Test - void testDirectExecutor() { - class Test { - - void exec1(PluginCommand command) { - command.on("test", this::handler1); - } - - void exec2(PluginCommand command) { - command.withString(); - command.on("test2", this::handler2); - } - - void handler1(CommandSender sender) { - sender.sendMessage("Hello World"); - } - - void handler2(CommandSender sender, String string) { - sender.sendMessage(string); - } - } - - Test test = new Test(); - console.run(sender, "test", test::exec1); - assertThat(outputMessages).containsExactly("Hello World"); - outputMessages.clear(); - console.run(sender, "hello test2", test::exec2); - assertThat(outputMessages).containsExactly("hello"); - } - - @Test - void testSenderArg() { - class Test { - void root(PluginCommand command) { - command.then(this::exec); - } - - void exec(CommandSender sender) { - sender.sendMessage("Hello World"); - } - } - Test test = new Test(); - console.run(sender, "", test::root); - assertThat(outputMessages).containsExactly("Hello World"); - } - - @Test - void testIf() { - class Test { - void root(PluginCommand command) { - command.ifThen(ServerOperator::isOp, this::exec); - } - - void exec(CommandSender sender) { - sender.sendMessage("Works"); - } - } - Test test = new Test(); - console.run(sender, "", test::root); - assertThat(outputMessages).isEmpty(); - - sender.setOp(true); - console.run(sender, "", test::root); - assertThat(outputMessages).containsExactly("Works"); - } - - @Test - void testCommandSingleSentenceArg() { - class Test { - void root(PluginCommand command) { - command.withSentence(); - command.then(this::exec); - } - - void exec(String sentence) { - sender.sendMessage(sentence); - } - } - Test test = new Test(); - console.run(sender, "Hello to you world", test::root); - assertThat(outputMessages).containsExactly("Hello to you world"); - } - - @Test - void testCommandIntArgs() { - AtomicBoolean ran = new AtomicBoolean(); - class Test { - void root(PluginCommand command) { - command.withInt("error"); - command.withInt("error"); - command.withInt("error"); - command.then(this::exec); - } - - void exec(int i1, int i2, int i3) { - assertThat(i1).isEqualTo(1); - assertThat(i2).isEqualTo(2); - assertThat(i3).isEqualTo(3); - ran.set(true); - } - } - Test test = new Test(); - console.run(sender, "1 2 3", test::root); - assertThat(outputMessages).isEmpty(); - assertThat(ran).isTrue(); - } - - @Test - void testCommandBranching() { - class Test { - void root(PluginCommand command) { - command.on("a", this::a); - } - - void a(PluginCommand command) { - command.on("b", this::b); - command.on("c", this::c); - } - - private void b(PluginCommand command) { - command.then(this::exec); - } - - private void c(PluginCommand command) { - } - - void exec(CommandSender sender) { - sender.sendMessage("Works"); - } - } - Test test = new Test(); - console.run(sender, "a b", test::root); - assertThat(outputMessages).containsExactly("Works"); - outputMessages.clear(); - console.run(sender, "a c", test::root); - assertThat(outputMessages).isEmpty(); - } - - @Test - void testOtherwise() { - class Test { - void root(PluginCommand command) { - command.on("a", this::a); - command.otherwise("no subcommand at root"); - } - - void a(PluginCommand command) { - command.withInt("error"); - command.otherwise("should run if int passed or missing arg"); - } - } - Test test = new Test(); - console.run(sender, "", test::root); - assertThat(outputMessages).containsExactly("no subcommand at root"); - outputMessages.clear(); - - console.run(sender, "a", test::root); - assertThat(outputMessages).containsExactly("should run if int passed or missing arg"); - outputMessages.clear(); - - console.run(sender, "a 2", test::root); - assertThat(outputMessages).containsExactly("should run if int passed or missing arg"); - } - - @Test - void testWithError() { - class Test { - void root(PluginCommand command) { - command.withInt(s -> s + " is not an int"); - } - } - Test test = new Test(); - console.run(sender, "swag", test::root); - assertThat(outputMessages).containsExactly("swag is not an int"); - } -} diff --git a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestPluginCommand.kt b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestPluginCommand.kt new file mode 100644 index 0000000..a04197b --- /dev/null +++ b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestPluginCommand.kt @@ -0,0 +1,179 @@ +package `in`.kyle.mcspring.subcommands + +import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommand +import `in`.kyle.mcspring.test.MCSpringTest +import org.assertj.core.api.Assertions.assertThat +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.mockito.Mockito.* +import org.springframework.beans.factory.annotation.Autowired +import java.util.concurrent.atomic.AtomicBoolean + +@MCSpringTest +internal class TestPluginCommand { + @Autowired + lateinit var console: TestConsole + + lateinit var sender: Player + lateinit var outputMessages: MutableList + + @BeforeEach + fun setup() { + outputMessages = mutableListOf() + sender = mock(Player::class.java) + doAnswer { outputMessages.add(it.getArgument(0)) }.`when`(sender).sendMessage(anyString()) + } + + @Test + fun testDirectExecutor() { + class Test { + fun exec1(command: PluginCommand) = command.on("subcommand1", this::handler1) + + fun exec2(command: PluginCommand) { + command.withString() + command.on("subcommand2", this::handler2) + } + + fun handler1() = "handler1" + + fun handler2(string: String) = "handler2: $string" + } + + val test = Test() + console.run(sender, "subcommand1", test::exec1) + assertThat(outputMessages).containsExactly("handler1") + outputMessages.clear() + + console.run(sender, "test-string subcommand2", test::exec2) + assertThat(outputMessages).containsExactly("handler2: test-string") + } + + @Test + fun testSenderArg() { + class Test { + fun root(command: PluginCommand) = command.then(this::exec) + + fun exec(sender: CommandSender) = "Hello World" + } + + console.run(sender, "", Test()::root) + assertThat(outputMessages).containsExactly("Hello World") + } + + // @Test + // void testIf() { + // class Test { + // void root(PluginCommand command) { + // command.re(ServerOperator::isOp, this::exec); + // } + // + // void exec(CommandSender sender) { + // sender.sendMessage("Works"); + // } + // } + // Test test = new Test(); + // console.run(sender, "", test::root); + // assertThat(outputMessages).isEmpty(); + // + // sender.setOp(true); + // console.run(sender, "", test::root); + // assertThat(outputMessages).containsExactly("Works"); + // } + @Test + fun testCommandSingleSentenceArg() { + class Test { + fun root(command: PluginCommand) { + command.withSentence() + command.then(this::exec) + } + + fun exec(sentence: String) = sentence + } + + console.run(sender, "Hello to you world", Test()::root) + assertThat(outputMessages).containsExactly("Hello to you world") + } + + @Test + fun testCommandIntArgs() { + class Test { + fun root(command: PluginCommand) { + command.withXYZInt("error") + command.then(this::exec) + } + + fun exec(x: Int, y: Int, z: Int): Boolean { + assertThat(x).isEqualTo(1) + assertThat(y).isEqualTo(2) + assertThat(z).isEqualTo(3) + return true + } + } + + console.run(sender, "1 2 3", Test()::root) + assertThat(outputMessages).containsExactly("true") + } + + @Test + fun testCommandBranching() { + class Test { + fun root(command: PluginCommand) = command.on("a", this::a) + + fun a(command: PluginCommand) { + command.on("b", this::b) + command.on("c", this::c) + } + + private fun b(command: PluginCommand) = command.then(this::exec) + + private fun c(command: PluginCommand) {} + + fun exec(sender: CommandSender) = "Works" + } + + console.run(sender, "a b", Test()::root) + assertThat(outputMessages).containsExactly("Works") + outputMessages.clear() + + console.run(sender, "a c", Test()::root) + assertThat(outputMessages).isEmpty() + } + + @Test + fun testOtherwise() { + class Test { + fun root(command: PluginCommand) { + command.on("a", this::a) + command.otherwise("no subcommand at root") + } + + fun a(command: PluginCommand) { + command.withInt("error") + command.otherwise("should run if int passed or missing arg") + } + } + + console.run(sender, "", Test()::root) + assertThat(outputMessages).containsExactly("no subcommand at root") + outputMessages.clear() + + console.run(sender, "a", Test()::root) + assertThat(outputMessages).containsExactly("should run if int passed or missing arg") + outputMessages.clear() + console.run(sender, "a 2", Test()::root) + assertThat(outputMessages).containsExactly("should run if int passed or missing arg") + } + + @Test + fun testWithError() { + class Test { + fun root(command: PluginCommand) = command.withInt { "$it is not an int" } + } + + console.run(sender, "swag", Test()::root) + assertThat(outputMessages).containsExactly("swag is not an int") + } +} diff --git a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestTabCompletion.java b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestTabCompletion.java index 768d411..25899be 100644 --- a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestTabCompletion.java +++ b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestTabCompletion.java @@ -1,166 +1,148 @@ package in.kyle.mcspring.subcommands; -import in.kyle.mcspring.test.MCSpringTest; -import org.bukkit.command.CommandSender; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Consumer; -import java.util.function.Function; - -import in.kyle.api.bukkit.entity.TestPlayer; -import in.kyle.mcspring.subcommands.tab.TabDiscovery; -import lombok.var; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.fail; - -@MCSpringTest -public class TestTabCompletion { - - @Autowired - TestPlayer sender; - @Autowired - TabDiscovery tabDiscovery; - List outputMessages; - - @BeforeEach - void setup() { - outputMessages = new ArrayList<>(); - sender.getMessages().subscribe(outputMessages::add); - } - - @Test - void testTabWithDirectExecution() { - class Test { - - void root(PluginCommand command) { - command.on("test", this::exec); - } - - void exec(String string) { - fail("Should not run"); - } - } - Test test = new Test(); - var completions = tabDiscovery.getCompletions(sender, "", test::root); - assertThat(completions).containsExactly("test"); - } - - @Test - void testNoTab() { - class Test { - void root(PluginCommand command) { - command.then(this::exec); - } - - void exec(CommandSender sender) { - fail("Should not run"); - } - } - - Test test = new Test(); - var completions = tabDiscovery.getCompletions(sender, "", test::root); - - assertThat(outputMessages).isEmpty(); - assertThat(completions).isEmpty(); - } - - @Test - void testSimpleSubs() { - class Test { - void root(PluginCommand command) { - Consumer dontRun = cmd -> fail("should not run"); - command.on("a", dontRun); - command.on("b", dontRun); - command.on("c", dontRun); - command.then(this::exec); - } - - void exec(CommandSender sender) { - fail("Should not run"); - } - } - - Test test = new Test(); - var completions = tabDiscovery.getCompletions(sender, "", test::root); - - assertThat(outputMessages).isEmpty(); - assertThat(completions).containsSequence("a", "b", "c"); - } - - @Test - void testAny() { - class Test { - void root(PluginCommand command) { - Function f = ignored -> { - fail("should not run"); - return ""; - }; - command.withAny(f, "a", "b", "c"); - command.withAny(f, "d", "e", "f"); - command.then(this::exec); - } - - void exec(CommandSender sender) { - fail("Should not run"); - } - } - - Test test = new Test(); - var completions = tabDiscovery.getCompletions(sender, "", test::root); - - assertThat(outputMessages).isEmpty(); - assertThat(completions).containsSequence("a", "b", "c", "d", "e", "f"); - - } - - @Test - void testOnTake1() { - class Test { - void root(PluginCommand command) { - Consumer doesNothing = cmd -> {}; - command.on("a", doesNothing); - command.on("b", doesNothing); - command.on("c", doesNothing); - command.then(this::exec); - } - - void exec(CommandSender sender) { - fail("Should not run"); - } - } - - Test test = new Test(); - var completions = tabDiscovery.getCompletions(sender, "", test::root); - assertThat(completions).containsSequence("a", "b", "c"); - - completions = tabDiscovery.getCompletions(sender, "a", test::root); - assertThat(completions).isEmpty(); - } - - @Test - void testInvalidStop() { - class Test { - void root(PluginCommand command) { - Consumer doesNothing = cmd -> fail("should not run"); - command.on("a", doesNothing); - command.on("b", doesNothing); - command.on("c", doesNothing); - command.onInvalid(s -> String.format("%s is not valid", s)); - command.then(this::exec); - } - - void exec(CommandSender sender) { - fail("Should not run"); - } - } - - Test test = new Test(); - var completions = tabDiscovery.getCompletions(sender, "g", test::root); - assertThat(completions).isEmpty(); - } -} +//@MCSpringTest +//public class TestTabCompletion { +// +// @Autowired +// TestPlayer sender; +// @Autowired +// TabDiscovery tabDiscovery; +// List outputMessages; +// +// @BeforeEach +// void setup() { +// outputMessages = new ArrayList<>(); +// sender.getMessages().subscribe(outputMessages::add); +// } +// +// @Test +// void testTabWithDirectExecution() { +// class Test { +// +// void root(PluginCommand command) { +// command.on("test", this::exec); +// } +// +// void exec(String string) { +// fail("Should not run"); +// } +// } +// Test test = new Test(); +// var completions = tabDiscovery.getCompletions(sender, "", test::root); +// assertThat(completions).containsExactly("test"); +// } +// +// @Test +// void testNoTab() { +// class Test { +// void root(PluginCommand command) { +// command.then(this::exec); +// } +// +// void exec(CommandSender sender) { +// fail("Should not run"); +// } +// } +// +// Test test = new Test(); +// var completions = tabDiscovery.getCompletions(sender, "", test::root); +// +// assertThat(outputMessages).isEmpty(); +// assertThat(completions).isEmpty(); +// } +// +// @Test +// void testSimpleSubs() { +// class Test { +// void root(PluginCommand command) { +// Consumer dontRun = cmd -> fail("should not run"); +// command.on("a", dontRun); +// command.on("b", dontRun); +// command.on("c", dontRun); +// command.then(this::exec); +// } +// +// void exec(CommandSender sender) { +// fail("Should not run"); +// } +// } +// +// Test test = new Test(); +// var completions = tabDiscovery.getCompletions(sender, "", test::root); +// +// assertThat(outputMessages).isEmpty(); +// assertThat(completions).containsSequence("a", "b", "c"); +// } +// +// @Test +// void testAny() { +// class Test { +// void root(PluginCommand command) { +// Function f = ignored -> { +// fail("should not run"); +// return ""; +// }; +// command.withAny(f, "a", "b", "c"); +// command.withAny(f, "d", "e", "f"); +// command.then(this::exec); +// } +// +// void exec(CommandSender sender) { +// fail("Should not run"); +// } +// } +// +// Test test = new Test(); +// var completions = tabDiscovery.getCompletions(sender, "", test::root); +// +// assertThat(outputMessages).isEmpty(); +// assertThat(completions).containsSequence("a", "b", "c", "d", "e", "f"); +// +// } +// +// @Test +// void testOnTake1() { +// class Test { +// void root(PluginCommand command) { +// Consumer doesNothing = cmd -> {}; +// command.on("a", doesNothing); +// command.on("b", doesNothing); +// command.on("c", doesNothing); +// command.then(this::exec); +// } +// +// void exec(CommandSender sender) { +// fail("Should not run"); +// } +// } +// +// Test test = new Test(); +// var completions = tabDiscovery.getCompletions(sender, "", test::root); +// assertThat(completions).containsSequence("a", "b", "c"); +// +// completions = tabDiscovery.getCompletions(sender, "a", test::root); +// assertThat(completions).isEmpty(); +// } +// +// @Test +// void testInvalidStop() { +// class Test { +// void root(PluginCommand command) { +// Consumer doesNothing = cmd -> fail("should not run"); +// command.on("a", doesNothing); +// command.on("b", doesNothing); +// command.on("c", doesNothing); +// command.onInvalid(s -> String.format("%s is not valid", s)); +// command.then(this::exec); +// } +// +// void exec(CommandSender sender) { +// fail("Should not run"); +// } +// } +// +// Test test = new Test(); +// var completions = tabDiscovery.getCompletions(sender, "g", test::root); +// assertThat(completions).isEmpty(); +// } +//} diff --git a/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/command/TestCommandRegistration.java b/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/command/TestCommandRegistration.java deleted file mode 100644 index 4fa3cbb..0000000 --- a/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/command/TestCommandRegistration.java +++ /dev/null @@ -1,68 +0,0 @@ -package in.kyle.mcspring.test.command; - -import org.bukkit.command.CommandSender; -import org.springframework.stereotype.Component; - -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -import in.kyle.mcspring.command.Command; -import in.kyle.mcspring.command.CommandRegistration; -import in.kyle.mcspring.command.CommandResolver; -import in.kyle.mcspring.command.SimpleMethodInjection; -import lombok.RequiredArgsConstructor; -import lombok.var; - -@Component -@RequiredArgsConstructor -class TestCommandRegistration implements CommandRegistration { - - private final SimpleMethodInjection injection; - private final Set commandResolvers; - - private final Map commandExecutors = new HashMap<>(); - - @Override - public void register(Command command, Method method, Object object) { - var executor = makeExecutor(method, object); - getAllNames(command).forEach(key -> commandExecutors.put(key, executor)); - } - - private List getAllNames(Command command) { - List commands = new ArrayList<>(Arrays.asList(command.aliases())); - commands.add(command.value()); - return commands; - } - - private Executor makeExecutor(Method method, Object object) { - return (sender, label, args) -> { - var temp = new CommandResolver.Command(sender, args, label); - var contextResolvers = commandResolvers.stream() - .map(r -> r.makeResolver(temp)) - .collect(Collectors.toList()); - Object result = injection.invoke(method, object, contextResolvers, sender, args, label); - if (result != null) { - sender.sendMessage(result.toString()); - } - }; - } - - public void run(String command, CommandSender sender, String label, String[] args) { - if (commandExecutors.containsKey(command)) { - commandExecutors.get(command).execute(sender, label, args); - } else { - throw new RuntimeException( - "Command " + command + " is not registered. Make sure to @Import it"); - } - } - - interface Executor { - void execute(CommandSender sender, String label, String[] args); - } -} diff --git a/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/command/TestCommandRegistration.kt b/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/command/TestCommandRegistration.kt new file mode 100644 index 0000000..b2e0c6c --- /dev/null +++ b/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/command/TestCommandRegistration.kt @@ -0,0 +1,71 @@ +package `in`.kyle.mcspring.test.command + +import `in`.kyle.mcspring.command.Command +import `in`.kyle.mcspring.command.CommandRegistration +import `in`.kyle.mcspring.command.CommandResolver +import `in`.kyle.mcspring.command.SimpleMethodInjection +import lombok.RequiredArgsConstructor +import org.bukkit.command.CommandSender +import org.springframework.stereotype.Component +import java.lang.reflect.Method +import java.util.* +import java.util.function.Consumer +import kotlin.reflect.KFunction +import kotlin.reflect.jvm.kotlinFunction + +@Component +@RequiredArgsConstructor +internal class TestCommandRegistration( + private val injection: SimpleMethodInjection, + private val commandResolvers: Set +) : CommandRegistration { + + private val commandExecutors: MutableMap = HashMap() + +// override fun register(command: Command, e: KFunction) { +// val executor = makeExecutor(e) +// getAllNames(command).forEach(Consumer { key: String -> commandExecutors[key] = executor }) +// } + + @Suppress("UNCHECKED_CAST") + override fun register(command: Command, method: Method, `object`: Any) { + val executor = makeExecutor(method.kotlinFunction as KFunction) + getAllNames(command).forEach(Consumer { key: String -> commandExecutors[key] = executor }) + } + + private fun getAllNames(command: Command): List { + val commands = command.aliases.toMutableList() + commands.add(command.value) + return commands + } + + private fun makeExecutor(e: KFunction): Executor { + return object : Executor { + override fun execute(sender: CommandSender, label: String, args: Array) { + val temp = CommandResolver.Command(sender, args, label) + + val thisResolvers = injection.makeResolvers(listOf(sender, args, label)) + val contextResolvers = commandResolvers.map { it.makeResolver(temp) } + + val parameters = injection.getParameters(e, contextResolvers.plus(thisResolvers)) + val result = e.call(*parameters) + if (result != null) { + sender.sendMessage(result.toString()) + } + } + } + } + + fun run(command: String, sender: CommandSender, label: String, args: Array) { + if (command in commandExecutors) { + commandExecutors[command]!!.execute(sender, label, args) + } else { + throw RuntimeException("Command $command is not registered. Make sure to @Import it") + } + } + + internal interface Executor { + fun execute(sender: CommandSender, label: String, args: Array) + } + +} diff --git a/mcspring-api/pom.xml b/mcspring-api/pom.xml index f238746..c010fd8 100644 --- a/mcspring-api/pom.xml +++ b/mcspring-api/pom.xml @@ -18,6 +18,10 @@ mcspring-test + + 1.3.72 + + 4.0.0 mcspring-api @@ -30,4 +34,52 @@ + + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + ${kotlin.version} + + + org.jetbrains.kotlin + kotlin-test + ${kotlin.version} + test + + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + compile + compile + + compile + + + + test-compile + test-compile + + test-compile + + + + + 1.8 + + -Xjvm-default=enable + + + + + + + diff --git a/mcspring-build/mcspring-maven-plugin/src/test/java/in/kyle/mcspring/autogenerator/TestGenerator.java b/mcspring-build/mcspring-maven-plugin/src/test/java/in/kyle/mcspring/autogenerator/TestGenerator.java index 77ca9e3..618f528 100644 --- a/mcspring-build/mcspring-maven-plugin/src/test/java/in/kyle/mcspring/autogenerator/TestGenerator.java +++ b/mcspring-build/mcspring-maven-plugin/src/test/java/in/kyle/mcspring/autogenerator/TestGenerator.java @@ -2,8 +2,6 @@ import org.apache.maven.plugin.testing.AbstractMojoTestCase; -import java.io.File; - public class TestGenerator extends AbstractMojoTestCase { @Override @@ -12,10 +10,10 @@ protected void setUp() throws Exception { } public void testMojoGoal() throws Exception { - File testPom = new File(getBasedir(), - "src/test/resources/unit/basic-test/basic-test-plugin-config.xml"); - GenerateFilesMojo mojo = (GenerateFilesMojo) lookupEmptyMojo("generate-files", testPom); - assertNotNull(mojo); - mojo.execute(); +// File testPom = new File(getBasedir(), +// "src/test/resources/unit/basic-test/basic-test-plugin-config.xml"); +// GenerateFilesMojo mojo = (GenerateFilesMojo) lookupEmptyMojo("generate-files", testPom); +// assertNotNull(mojo); +// mojo.execute(); } } diff --git a/mcspring-build/mcspring-plugin-manager/pom.xml b/mcspring-build/mcspring-plugin-manager/pom.xml index c01e245..65d0a82 100644 --- a/mcspring-build/mcspring-plugin-manager/pom.xml +++ b/mcspring-build/mcspring-plugin-manager/pom.xml @@ -13,6 +13,8 @@ 4.0.0 mcspring-plugin-manager + ${project.artifactId} + Adds basic debugging commands and features diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/Main.java b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/Main.java deleted file mode 100644 index 2b984df..0000000 --- a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/Main.java +++ /dev/null @@ -1,7 +0,0 @@ -package in.kyle.mcspring.manager; - -// TODO: 2020-03-07 Pull this information from the pom.xml in a build plugin -//@SpringPlugin(name = "mcspring-plugin-manager", -// description = "Provides simple debugging and plugin management functions") -interface Main { -} diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandAbout.java b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandAbout.java deleted file mode 100644 index 27b12ad..0000000 --- a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandAbout.java +++ /dev/null @@ -1,22 +0,0 @@ -package in.kyle.mcspring.manager.commands; - -import org.bukkit.Server; -import org.springframework.boot.info.BuildProperties; -import org.springframework.core.SpringVersion; -import org.springframework.stereotype.Component; - -import in.kyle.mcspring.command.Command; - -@Component -class CommandAbout { - @Command(value = "about", - description = "Provides information about current library versions being used") - String about(BuildProperties properties, Server server) { - return String.format( - "Plugin Name: %s\nPlugin Version: %s\nSpring Version: %s\nBukkit Version: %s", - properties.getName(), - properties.getVersion(), - SpringVersion.getVersion(), - server.getBukkitVersion()); - } -} diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandAbout.kt b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandAbout.kt new file mode 100644 index 0000000..16d62c2 --- /dev/null +++ b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandAbout.kt @@ -0,0 +1,20 @@ +package `in`.kyle.mcspring.manager.commands + +import `in`.kyle.mcspring.command.Command +import org.bukkit.Server +import org.springframework.boot.info.BuildProperties +import org.springframework.core.SpringVersion +import org.springframework.stereotype.Component + +@Component +internal class CommandAbout { + + @Command(value = "about", + description = "Provides information about current library versions in use") + fun about(properties: BuildProperties, server: Server): String = """ + Plugin Name: ${properties.name} + Plugin Version: ${properties.version} + Spring Version: ${SpringVersion.getVersion()} + Bukkit Version: ${server.bukkitVersion} + """.trimIndent() +} diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandClassLoader.java b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandClassLoader.java deleted file mode 100644 index 906a0f8..0000000 --- a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandClassLoader.java +++ /dev/null @@ -1,31 +0,0 @@ -package in.kyle.mcspring.manager.commands; - -import org.springframework.stereotype.Component; - -import in.kyle.mcspring.command.Command; -import in.kyle.mcspring.subcommands.PluginCommand; - -@Component -class CommandClassLoader { - - @Command(value = "classloader", - description = "Show ClassLoader information for a specific class", - usage = "/classloader ") - void classLoader(PluginCommand command) { - command.withString(); - command.then(this::executeClassLoader); - command.otherwise("Usage: /classloader "); - } - - String executeClassLoader(String clazz) { - try { - Class aClass = Class.forName(clazz); - String classLoader = aClass.getClassLoader().toString(); - String protectionDomain = - aClass.getProtectionDomain().getCodeSource().getLocation().toString(); - return String.format("ClassLoader: %s\nDomain: %s", classLoader, protectionDomain); - } catch (ClassNotFoundException e) { - return String.format("Class %s not found", clazz); - } - } -} diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandClassLoader.kt b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandClassLoader.kt new file mode 100644 index 0000000..76c9a48 --- /dev/null +++ b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandClassLoader.kt @@ -0,0 +1,26 @@ +package `in`.kyle.mcspring.manager.commands + +import `in`.kyle.mcspring.command.Command +import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommand +import org.springframework.stereotype.Component + +@Component +internal class CommandClassLoader { + + @Command(value = "classloader", description = "Show ClassLoader information for a specific class", usage = "/classloader ") + fun classLoader(command: PluginCommand) { + command.withString(); + command.then(::executeClassLoader); + command.otherwise("Usage: /classloader ") + } + + private fun executeClassLoader(clazz: String): String { + val aClass = Class.forName(clazz) + val classLoader = aClass.classLoader.toString() + val protectionDomain = aClass.protectionDomain.codeSource.location.toString() + return """ + ClassLoader: $classLoader + Domain: $protectionDomain + """.trimIndent() + } +} diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandGamemode.java b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandGamemode.java deleted file mode 100644 index 746a34b..0000000 --- a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandGamemode.java +++ /dev/null @@ -1,47 +0,0 @@ -package in.kyle.mcspring.manager.commands; - -import org.bukkit.GameMode; -import org.bukkit.entity.Player; -import org.springframework.stereotype.Component; - -import java.util.HashMap; -import java.util.Map; - -import in.kyle.mcspring.command.Command; -import in.kyle.mcspring.subcommands.PluginCommand; - -@Component -class CommandGamemode { - - @Command(value = "gamemode", - aliases = "gm", - description = "Set your game mode", - usage = "/gamemode ") - void gamemode(PluginCommand command) { - command.withPlayerSender("Only players can run this command."); - Map gamemodes = new HashMap<>(); - for (GameMode value : GameMode.values()) { - gamemodes.put(value.name().toLowerCase(), value); - gamemodes.put(String.valueOf(value.getValue()), value); - } - command.withMap(gamemodes, s -> String.format("%s is not a valid game mode", s)); - command.then(this::gamemodeExecutor); - - command.otherwise("Usage: /gamemode "); - } - - @Command(value = "gmc", description = "Set your game mode to creative") - String gmc(Player sender) { - return gamemodeExecutor(sender, GameMode.CREATIVE); - } - - @Command(value = "gms", description = "Set your game mode to survival") - String gms(Player sender) { - return gamemodeExecutor(sender, GameMode.SURVIVAL); - } - - String gamemodeExecutor(Player sender, GameMode gameMode) { - sender.setGameMode(gameMode); - return String.format("Game mode set to %s", gameMode.name().toLowerCase()); - } -} diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandGamemode.kt b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandGamemode.kt new file mode 100644 index 0000000..4f16278 --- /dev/null +++ b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandGamemode.kt @@ -0,0 +1,36 @@ +package `in`.kyle.mcspring.manager.commands + +import `in`.kyle.mcspring.command.Command +import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommand +import org.bukkit.GameMode +import org.bukkit.entity.Player +import org.springframework.stereotype.Component + +@Component +internal class CommandGamemode { + @Command(value = "gamemode", aliases = ["gm"], description = "Set your game mode", usage = "/gamemode ") + fun gamemode(command: PluginCommand) { + command.requiresPlayerSender { "Only players can run this command." } + + val gamemodes = mutableMapOf() + GameMode.values().forEach { + gamemodes[it.value.toString()] = it + gamemodes[it.name.toLowerCase()] = it + } + + command.withMap(gamemodes) { "$it is not a valid game mode" } + command.then(::gamemodeExecutor) + command.otherwise("Usage: /gamemode ") + } + + @Command(value = "gmc", description = "Set your game mode to creative") + fun gmc(sender: Player): String = gamemodeExecutor(sender, GameMode.CREATIVE) + + @Command(value = "gms", description = "Set your game mode to survival") + fun gms(sender: Player): String = gamemodeExecutor(sender, GameMode.SURVIVAL) + + private fun gamemodeExecutor(sender: Player, gameMode: GameMode): String { + sender.gameMode = gameMode + return "Game mode set to ${gameMode.name.toLowerCase()}" + } +} diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandHeal.java b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandHeal.java deleted file mode 100644 index 14c96b9..0000000 --- a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandHeal.java +++ /dev/null @@ -1,26 +0,0 @@ -package in.kyle.mcspring.manager.commands; - -import org.bukkit.entity.Player; -import org.springframework.stereotype.Component; - -import in.kyle.mcspring.command.Command; -import in.kyle.mcspring.subcommands.PluginCommand; - -@Component -class CommandHeal { - - @Command(value = "heal", - description = "Heal yourself or another player", - usage = "/heal ?") - void heal(PluginCommand command) { - command.withPlayerSender("Sender must be a player"); - command.withPlayer(s -> String.format("Player %s not found", s)); - command.then(this::executeHeal); - command.otherwise(this::executeHeal); - } - - String executeHeal(Player target) { - target.setHealth(target.getMaxHealth()); - return String.format("Healed %s", target.getName()); - } -} diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandHeal.kt b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandHeal.kt new file mode 100644 index 0000000..3249a1c --- /dev/null +++ b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandHeal.kt @@ -0,0 +1,22 @@ +package `in`.kyle.mcspring.manager.commands + +import `in`.kyle.mcspring.command.Command +import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommand +import org.bukkit.entity.Player +import org.springframework.stereotype.Component + +@Component +internal class CommandHeal { + @Command(value = "heal", description = "Heal yourself or another player", usage = "/heal ?") + fun heal(command: PluginCommand) { + command.requiresPlayerSender { "Sender must be a player" } + command.withPlayer { "Player $it not found" } + command.then(this::executeHeal) + command.otherwise(this::executeHeal) + } + + private fun executeHeal(target: Player): String { + target.health = target.maxHealth + return "Healed ${target.name}" + } +} diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandOp.java b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandOp.java deleted file mode 100644 index 8800360..0000000 --- a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandOp.java +++ /dev/null @@ -1,26 +0,0 @@ -package in.kyle.mcspring.manager.commands; - -import org.bukkit.command.CommandSender; -import org.springframework.stereotype.Component; - -import in.kyle.mcspring.command.Command; -import in.kyle.mcspring.subcommands.PluginCommand; - -@Component -class CommandOp { - - @Command(value = "op", - description = "Toggle yourself or another players OP status", - usage = "/op ?") - void op(PluginCommand command) { - command.withPlayer(s -> String.format("Player %s not found", s)); - command.then(this::toggleOp); - - command.otherwise(this::toggleOp); - } - - String toggleOp(CommandSender target) { - target.setOp(!target.isOp()); - return String.format("%s is %s op", target.getName(), target.isOp() ? "now" : "no longer"); - } -} diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandOp.kt b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandOp.kt new file mode 100644 index 0000000..252e867 --- /dev/null +++ b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandOp.kt @@ -0,0 +1,21 @@ +package `in`.kyle.mcspring.manager.commands + +import `in`.kyle.mcspring.command.Command +import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommand +import org.bukkit.command.CommandSender +import org.springframework.stereotype.Component + +@Component +internal class CommandOp { + @Command(value = "op", description = "Toggle yourself or another players OP status", usage = "/op ?") + fun op(command: PluginCommand) { + command.withPlayer { "Player $it not found" } + command.then(::toggleOp) + command.otherwise(::toggleOp) + } + + private fun toggleOp(target: CommandSender): String { + target.isOp = !target.isOp + return "${target.name} is ${if (target.isOp) "now" else "no longer"} op" + } +} diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandPlugin.java b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandPlugin.java deleted file mode 100644 index 8ff826a..0000000 --- a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandPlugin.java +++ /dev/null @@ -1,80 +0,0 @@ -package in.kyle.mcspring.manager.commands; - -import org.bukkit.plugin.Plugin; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.stereotype.Component; - -import java.nio.file.Path; -import java.util.function.Function; -import java.util.stream.Collectors; - -import in.kyle.mcspring.command.Command; -import in.kyle.mcspring.manager.controller.PluginController; -import in.kyle.mcspring.subcommands.PluginCommand; -import lombok.RequiredArgsConstructor; -import lombok.var; - -@Component -@RequiredArgsConstructor -@ConditionalOnBean(Plugin.class) -class CommandPlugin { - - private final PluginController pluginController; - - @Command(value = "plugin", - aliases = "pl", - description = "Load/unload/reload a specific plugin", - usage = "/plugin ") - void plugin(PluginCommand command) { - command.on("load", this::load); - command.on("unload", this::unload); - command.on("list", this::list); - command.otherwise("Usage: /plugin "); - } - - private void load(PluginCommand command) { - command.withMap(pluginController.getLoadablePlugins(), - s -> String.format("Plugin %s not found or is already loaded", s)); - command.then(this::executeLoad); - command.otherwise("Usage: /plugin load "); - } - - private void unload(PluginCommand command) { - var plugins = pluginController.getPlugins() - .stream() - .collect(Collectors.toMap(org.bukkit.plugin.Plugin::getName, Function.identity())); - command.withMap(plugins, s -> String.format("Plugin %s is not loaded", s)); - command.then(this::executeDisable); - command.otherwise("Usage: /plugin unload "); - } - - private void list(PluginCommand command) { - command.then(this::executeListPlugins); - } - - private String executeListPlugins() { - return pluginController.getAllPlugins() - .entrySet() - .stream() - .map(e -> String.format("%s%s", e.getValue() ? "&1" : "&4", e.getKey())) - .collect(Collectors.joining(" ")); - } - - private String executeLoad(Path jar) { - var pluginOptional = pluginController.load(jar); - if (pluginOptional.isPresent()) { - return String.format("Plugin %s enabled", pluginOptional.get().getName()); - } else { - return String.format("&4Could not load %s see log for details", jar); - } - } - - private String executeDisable(org.bukkit.plugin.Plugin plugin) { - boolean disabled = pluginController.unload(plugin); - if (disabled) { - return String.format("Plugin %s disabled", plugin); - } else { - return String.format("Could not disable %s, see log for details", plugin); - } - } -} diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandPlugin.kt b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandPlugin.kt new file mode 100644 index 0000000..2e58360 --- /dev/null +++ b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandPlugin.kt @@ -0,0 +1,63 @@ +package `in`.kyle.mcspring.manager.commands + +import `in`.kyle.mcspring.command.Command +import `in`.kyle.mcspring.manager.controller.PluginController +import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommand +import org.bukkit.plugin.Plugin +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean +import org.springframework.stereotype.Component +import java.nio.file.Path + +@Component +@ConditionalOnBean(Plugin::class) +internal class CommandPlugin( + private val pluginController: PluginController +) { + + @Command(value = "plugin", aliases = ["pl"], description = "Load/unload/reload a specific plugin", usage = "/plugin ") + fun plugin(command: PluginCommand) { + command.on("load", ::load) + command.on("unload", ::unload) + command.on("list", ::executeListPlugins) + command.otherwise("Usage: /plugin ") + } + + private fun load(command: PluginCommand) { + command.withMap(pluginController.loadablePlugins) { "Plugin $it not found or is already loaded" } + command.then(::executeLoad) + command.otherwise("Usage: /plugin load ") + } + + private fun unload(command: PluginCommand) { + val plugins = pluginController.plugins.associateBy({ it.name }, { it }) + command.withMap(plugins) { "Plugin $it is not loaded" } + command.then(::executeDisable) + command.otherwise("Usage: /plugin unload ") + } + + private fun executeListPlugins(): String { + val colors = mapOf(true to "&1", false to "&2") + return pluginController.allPlugins + .map { "${colors[it.value]}${it.key}" } + .joinToString(separator = " ") + } + + private fun executeLoad(jar: Path): String { + val plugin = pluginController.load(jar) + + return if (plugin != null) { + "Plugin ${plugin.name} enabled" + } else { + "&4Could not load $jar see log for details" + } + } + + private fun executeDisable(plugin: Plugin): String { + val disabled = pluginController.unload(plugin) + return if (disabled) { + "Plugin $plugin disabled" + } else { + "Could not disable $plugin, see log for details" + } + } +} diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandSpeed.java b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandSpeed.java deleted file mode 100644 index 1ddbe40..0000000 --- a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandSpeed.java +++ /dev/null @@ -1,27 +0,0 @@ -package in.kyle.mcspring.manager.commands; - -import org.bukkit.entity.Player; -import org.springframework.stereotype.Component; - -import in.kyle.mcspring.command.Command; -import in.kyle.mcspring.subcommands.PluginCommand; - -@Component -class CommandSpeed { - - @Command(value = "speed", - description = "Set your movement and fly speed", - usage = "/speed ") - void speed(PluginCommand command) { - command.withPlayerSender("Sender must be a player"); - command.withDouble("Speed value must be an integer"); - command.then(this::speedExecutor); - command.otherwise("Usage: /speed "); - } - - String speedExecutor(Player sender, double speed) { - sender.setFlySpeed((float) speed); - sender.setWalkSpeed((float) speed); - return String.format("Speed set to %f", speed); - } -} diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandSpeed.kt b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandSpeed.kt new file mode 100644 index 0000000..f48151f --- /dev/null +++ b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandSpeed.kt @@ -0,0 +1,24 @@ +package `in`.kyle.mcspring.manager.commands + +import `in`.kyle.mcspring.command.Command +import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommand +import org.bukkit.entity.Player +import org.springframework.stereotype.Component + +@Component +internal class CommandSpeed { + + @Command(value = "speed", description = "Set your movement and fly speed", usage = "/speed ") + fun speed(command: PluginCommand) { + command.requiresPlayerSender { "Sender must be a player" } + command.withDouble("Speed value must be an integer") + command.then(::speedExecutor) + command.otherwise("Usage: /speed ") + } + + private fun speedExecutor(sender: Player, speed: Double): String { + sender.flySpeed = speed.toFloat() + sender.walkSpeed = speed.toFloat() + return "Speed set to $speed" + } +} diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/BukkitPluginUnloader.java b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/BukkitPluginUnloader.java deleted file mode 100644 index f917e57..0000000 --- a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/BukkitPluginUnloader.java +++ /dev/null @@ -1,96 +0,0 @@ -package in.kyle.mcspring.manager.controller; - -import org.bukkit.command.Command; -import org.bukkit.command.CommandMap; -import org.bukkit.command.PluginCommand; -import org.bukkit.command.SimpleCommandMap; -import org.bukkit.plugin.Plugin; -import org.bukkit.plugin.PluginManager; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Component; - -import java.lang.reflect.Field; -import java.net.URLClassLoader; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import javax.annotation.PostConstruct; - -import lombok.RequiredArgsConstructor; -import lombok.SneakyThrows; -import lombok.var; - -@Lazy -@Component -@RequiredArgsConstructor -@ConditionalOnBean(Plugin.class) -@SuppressWarnings("unchecked") -class BukkitPluginUnloader { - - private final PluginManager pluginManager; - private final CommandMap commandMap; - private Map commands; - private List plugins; - private Map names; - - @PostConstruct - @SneakyThrows - void setup() { - plugins = getDeclaredField(pluginManager, "plugins"); - names = getDeclaredField(pluginManager, "lookupNames"); - Field knownCommands = SimpleCommandMap.class.getDeclaredField("knownCommands"); - knownCommands.setAccessible(true); - commands = (Map) knownCommands.get(commandMap); - } - - @SneakyThrows - boolean unload(Plugin plugin) { - pluginManager.disablePlugin(plugin); - - synchronized (pluginManager) { - plugins.remove(plugin); - names.remove(plugin.getName()); - unregisterCommands(plugin); - - closeClassLoader(plugin.getClass().getClassLoader()); - } - System.gc(); - - return true; - } - - private void unregisterCommands(Plugin plugin) { - var unregister = commands.entrySet() - .stream() - .filter(e -> e.getValue() instanceof PluginCommand) - .filter(e -> ((PluginCommand) e.getValue()).getPlugin() == plugin) - .peek(e -> e.getValue().unregister(commandMap)) - .collect(Collectors.toSet()); - unregister.forEach(e -> commands.remove(e.getKey())); - } - - @SneakyThrows - private void closeClassLoader(ClassLoader classLoader) { - if (classLoader instanceof URLClassLoader) { - setDeclaredField(classLoader, "plugin", null); - setDeclaredField(classLoader, "pluginInit", null); - ((URLClassLoader) classLoader).close(); - } - } - - @SneakyThrows - private T getDeclaredField(Object object, String fieldName) { - Field field = object.getClass().getDeclaredField(fieldName); - field.setAccessible(true); - return (T) field.get(object); - } - - @SneakyThrows - private void setDeclaredField(Object object, String fieldName, Object value) { - Field field = object.getClass().getDeclaredField(fieldName); - field.setAccessible(true); - field.set(object, value); - } -} diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/BukkitPluginUnloader.kt b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/BukkitPluginUnloader.kt new file mode 100644 index 0000000..b8300c5 --- /dev/null +++ b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/BukkitPluginUnloader.kt @@ -0,0 +1,78 @@ +package `in`.kyle.mcspring.manager.controller + +import lombok.SneakyThrows +import org.bukkit.command.Command +import org.bukkit.command.CommandMap +import org.bukkit.command.PluginCommand +import org.bukkit.command.SimpleCommandMap +import org.bukkit.plugin.Plugin +import org.bukkit.plugin.PluginManager +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean +import org.springframework.context.annotation.Lazy +import org.springframework.stereotype.Component +import java.net.URLClassLoader +import javax.annotation.PostConstruct + +@Lazy +@Component +@ConditionalOnBean(Plugin::class) +class BukkitPluginUnloader( + private val pluginManager: PluginManager, + private val commandMap: CommandMap +) { + + private val commands = mutableMapOf() + private val plugins = mutableListOf() + private val names = mutableMapOf() + + @PostConstruct + fun setup() { + plugins.addAll(getDeclaredField(pluginManager, "plugins")) + names.putAll(getDeclaredField(pluginManager, "lookupNames")) + val knownCommands = SimpleCommandMap::class.java.getDeclaredField("knownCommands") + knownCommands.isAccessible = true + commands.putAll(knownCommands.get(commandMap) as Map) + } + + fun unload(plugin: Plugin): Boolean { + pluginManager.disablePlugin(plugin) + synchronized(pluginManager) { + plugins.remove(plugin) + names.remove(plugin.name) + unregisterCommands(plugin) + closeClassLoader(plugin.javaClass.classLoader) + } + System.gc() + return true + } + + private fun unregisterCommands(plugin: Plugin) { + val unregister = commands.entries + .filter { (it.value as? PluginCommand)?.plugin === plugin } + .toSet() + unregister.forEach { it.value.unregister(commandMap); commands.remove(it.key) } + } + + @SneakyThrows + private fun closeClassLoader(classLoader: ClassLoader) { + if (classLoader is URLClassLoader) { + setDeclaredField(classLoader, "plugin", null) + setDeclaredField(classLoader, "pluginInit", null) + classLoader.close() + } + } + + @SneakyThrows + private fun getDeclaredField(obj: Any, fieldName: String): T { + val field = obj.javaClass.getDeclaredField(fieldName) + field.isAccessible = true + return field[obj] as T + } + + @SneakyThrows + private fun setDeclaredField(obj: Any, fieldName: String, value: Any?) { + val field = obj.javaClass.getDeclaredField(fieldName) + field.isAccessible = true + field[obj] = value + } +} diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/LogFileController.java b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/LogFileController.java deleted file mode 100644 index 99fe27a..0000000 --- a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/LogFileController.java +++ /dev/null @@ -1,33 +0,0 @@ -package in.kyle.mcspring.manager.controller; - -import org.bukkit.plugin.Plugin; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.stereotype.Controller; - -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; - -import javax.annotation.PostConstruct; - -import lombok.SneakyThrows; - -@Controller -@ConditionalOnBean(Plugin.class) -class LogFileController { - - private final Path logsFolder = Paths.get("logs"); - - @PostConstruct - @SneakyThrows - void setup() { - Files.list(logsFolder) - .filter(p -> p.toString().endsWith(".log.gz")) - .forEach(this::delete); - } - - @SneakyThrows - private void delete(Path f) { - Files.delete(f); - } -} diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/LogFileController.kt b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/LogFileController.kt new file mode 100644 index 0000000..589ecb3 --- /dev/null +++ b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/LogFileController.kt @@ -0,0 +1,22 @@ +package `in`.kyle.mcspring.manager.controller + +import org.bukkit.plugin.Plugin +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean +import org.springframework.stereotype.Controller +import java.nio.file.Files +import java.nio.file.Paths +import javax.annotation.PostConstruct + +@Controller +@ConditionalOnBean(Plugin::class) +internal class LogFileController { + + private val logsFolder = Paths.get("logs") + + @PostConstruct + fun setup() { + Files.list(logsFolder) + .filter { it.toString().endsWith(".log.gz") } + .forEach { Files.delete(it) } + } +} diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/PluginController.java b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/PluginController.java deleted file mode 100644 index 054ac93..0000000 --- a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/PluginController.java +++ /dev/null @@ -1,100 +0,0 @@ -package in.kyle.mcspring.manager.controller; - -import org.bukkit.plugin.InvalidDescriptionException; -import org.bukkit.plugin.Plugin; -import org.bukkit.plugin.PluginLoader; -import org.bukkit.plugin.PluginManager; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.stereotype.Controller; - -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.function.Function; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.stream.Collectors; - -import lombok.RequiredArgsConstructor; -import lombok.SneakyThrows; -import lombok.var; - -@Controller -@RequiredArgsConstructor -@ConditionalOnBean(Plugin.class) -public class PluginController { - - private final PluginManager pluginManager; - private final PluginLoader pluginLoader; - private final BukkitPluginUnloader unloader; - private final Logger logger; - - public Optional load(Path jar) { - try { - Plugin plugin = pluginManager.loadPlugin(jar.toFile()); - Optional optionalPlugin = Optional.ofNullable(plugin); - optionalPlugin.ifPresent(Plugin::onLoad); - return optionalPlugin; - } catch (Exception e) { - logger.log(Level.SEVERE, "Could not load " + jar.toAbsolutePath()); - logger.log(Level.SEVERE, e, () -> ""); - return Optional.empty(); - } - } - - public boolean unload(Plugin plugin) { - if (pluginManager.isPluginEnabled(plugin)) { - try { - return unloader.unload(plugin); - } catch (Exception e) { - logger.log(Level.SEVERE, "Could not unload " + plugin.getName()); - logger.log(Level.SEVERE, e, () -> ""); - } - } - return false; - } - - @SneakyThrows - public Map getLoadablePlugins() { - Path pluginsFolder = Paths.get("plugins"); - return Files.list(pluginsFolder) - .filter(p -> p.toString().endsWith(".jar")) - .filter(jar -> getPluginName(jar).isPresent()) - .collect(Collectors.toMap(j -> getPluginName(j).get(), Function.identity())); - } - - public Optional getPlugin(String name) { - return Optional.ofNullable(pluginManager.getPlugin(name)); - } - - public Set getPlugins() { - return new HashSet<>(Arrays.asList(pluginManager.getPlugins())); - } - - public Map getAllPlugins() { - Map allPlugins = new HashMap<>(); - getLoadablePlugins().forEach((k,v)->allPlugins.put(k, false)); - getPlugins().forEach(p -> allPlugins.put(p.getName(), p.isEnabled())); - return allPlugins; - } - - private boolean isEnabled(String pluginName) { - return getPlugin(pluginName).map(Plugin::isEnabled).orElse(false); - } - - private Optional getPluginName(Path jar) { - try { - var description = pluginLoader.getPluginDescription(jar.toFile()); - return Optional.of(description.getName()); - } catch (InvalidDescriptionException e) { - return Optional.empty(); - } - } - -} diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/PluginController.kt b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/PluginController.kt new file mode 100644 index 0000000..97e40b6 --- /dev/null +++ b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/PluginController.kt @@ -0,0 +1,86 @@ +package `in`.kyle.mcspring.manager.controller + +import org.bukkit.plugin.InvalidDescriptionException +import org.bukkit.plugin.Plugin +import org.bukkit.plugin.PluginLoader +import org.bukkit.plugin.PluginManager +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean +import org.springframework.stereotype.Controller +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.Paths +import java.util.logging.Level +import java.util.logging.Logger +import java.util.stream.Collectors + +@Controller +@ConditionalOnBean(Plugin::class) +class PluginController( + private val pluginManager: PluginManager, + private val pluginLoader: PluginLoader, + private val unloader: BukkitPluginUnloader, + private val logger: Logger +) { + + val loadablePlugins: Map + get() { + val pluginsFolder = Paths.get("plugins") + return Files.list(pluginsFolder) + .filter { it.toString().endsWith(".jar") } + .filter { getPluginName(it) != null } + .collect(Collectors.toMap({ getPluginName(it) }, { it })) + } + + val plugins: Set + get() = pluginManager.plugins.toSet() + + val allPlugins: Map + get() { + val allPlugins = mutableMapOf() + loadablePlugins.forEach { allPlugins[it.key] = false } + plugins.forEach { allPlugins[it.name] = it.isEnabled } + return allPlugins + } + + fun load(jar: Path): Plugin? { + return try { + val plugin: Plugin? = pluginManager.loadPlugin(jar.toFile()) + plugin?.onLoad() + plugin + } catch (e: Exception) { + logger.log(Level.SEVERE, "Could not load " + jar.toAbsolutePath()) + logger.log(Level.SEVERE, e) { "" } + null + } + } + + fun unload(plugin: Plugin): Boolean { + if (pluginManager.isPluginEnabled(plugin)) { + try { + return unloader.unload(plugin) + } catch (e: Exception) { + logger.log(Level.SEVERE, "Could not unload " + plugin.name) + logger.log(Level.SEVERE, e) { "" } + } + } + return false + } + + fun getPlugin(name: String?): Plugin? { + return pluginManager.getPlugin(name) + } + + fun isEnabled(pluginName: String): Boolean { + return getPlugin(pluginName)?.isEnabled ?: false + } + + private fun getPluginName(jar: Path): String? { + val description = try { + pluginLoader.getPluginDescription(jar.toFile()) + } catch (e: InvalidDescriptionException) { + null + } + + return description?.name + } +} diff --git a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandAbout.java b/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandAbout.java deleted file mode 100644 index 5b25654..0000000 --- a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandAbout.java +++ /dev/null @@ -1,28 +0,0 @@ -package in.kyle.mcspring.manager.commands; - -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; - -import java.util.List; - -import in.kyle.mcspring.test.MCSpringTest; -import in.kyle.mcspring.test.command.TestCommandExecutor; - -import static org.assertj.core.api.Assertions.assertThat; - -@MCSpringTest -class TestCommandAbout { - - @Autowired - TestCommandExecutor commandExecutor; - - @Test - void testAbout() { - List output = commandExecutor.run("about"); - assertThat(output).hasSize(4); - assertThat(output.get(0)).matches("Plugin Name: [^ ]+"); - assertThat(output.get(1)).matches("Plugin Version: [^ ]+"); - assertThat(output.get(2)).matches("Spring Version: [^ ]+"); - assertThat(output.get(3)).matches("Bukkit Version: [^ ]+"); - } -} diff --git a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandAbout.kt b/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandAbout.kt new file mode 100644 index 0000000..c158f0d --- /dev/null +++ b/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandAbout.kt @@ -0,0 +1,24 @@ +package `in`.kyle.mcspring.manager.commands + +import `in`.kyle.mcspring.test.MCSpringTest +import `in`.kyle.mcspring.test.command.TestCommandExecutor +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired + +@MCSpringTest +internal class TestCommandAbout { + + @Autowired + lateinit var commandExecutor: TestCommandExecutor + + @Test + fun testAbout() { + val output: List = commandExecutor.run("about") + assertThat(output).hasSize(4) + assertThat(output[0]).matches("Plugin Name: [^ ]+") + assertThat(output[1]).matches("Plugin Version: [^ ]+") + assertThat(output[2]).matches("Spring Version: [^ ]+") + assertThat(output[3]).matches("Bukkit Version: [^ ]+") + } +} diff --git a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandClassLoader.java b/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandClassLoader.java deleted file mode 100644 index 1ebec91..0000000 --- a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandClassLoader.java +++ /dev/null @@ -1,30 +0,0 @@ -package in.kyle.mcspring.manager.commands; - -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; - -import java.util.List; - -import in.kyle.mcspring.test.MCSpringTest; -import in.kyle.mcspring.test.command.TestCommandExecutor; - -import static org.assertj.core.api.Assertions.assertThat; - -@MCSpringTest -class TestCommandClassLoader { - - @Autowired - TestCommandExecutor commandExecutor; - - @Test - void testClassLoader() { - List output = commandExecutor.run("classloader " + getClass().getName()); - assertThat(output).hasSize(2); - assertThat(output.get(0)).isEqualTo( - "ClassLoader: " + getClass().getClassLoader().toString()); - assertThat(output.get(1)).isEqualTo("Domain: " + getClass().getProtectionDomain() - .getCodeSource() - .getLocation() - .toString()); - } -} diff --git a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandClassLoader.kt b/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandClassLoader.kt new file mode 100644 index 0000000..11b4a80 --- /dev/null +++ b/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandClassLoader.kt @@ -0,0 +1,22 @@ +package `in`.kyle.mcspring.manager.commands + +import `in`.kyle.mcspring.test.MCSpringTest +import `in`.kyle.mcspring.test.command.TestCommandExecutor +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired + +@MCSpringTest +internal class TestCommandClassLoader { + + @Autowired + lateinit var commandExecutor: TestCommandExecutor + + @Test + fun testClassLoader() { + val output: List = commandExecutor.run("classloader " + javaClass.name) + assertThat(output).hasSize(2) + assertThat(output[0]).isEqualTo("ClassLoader: ${javaClass.classLoader}") + assertThat(output[1]).isEqualTo("Domain: ${javaClass.protectionDomain.codeSource.location}") + } +} diff --git a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandGamemode.java b/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandGamemode.java deleted file mode 100644 index 356393d..0000000 --- a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandGamemode.java +++ /dev/null @@ -1,58 +0,0 @@ -package in.kyle.mcspring.manager.commands; - -import org.bukkit.GameMode; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; - -import java.util.List; - -import in.kyle.api.bukkit.entity.TestPlayer; -import in.kyle.mcspring.test.MCSpringTest; -import in.kyle.mcspring.test.command.TestCommandExecutor; - -import static org.assertj.core.api.Assertions.assertThat; - -@MCSpringTest -public class TestCommandGamemode { - - @Autowired - TestCommandExecutor executor; - - @Autowired - TestPlayer sender; - - @BeforeEach - void setup() { - sender.setGameMode(GameMode.SURVIVAL); - } - - @Test - void testGmc() { - List output = executor.run(sender, "gmc"); - assertThat(output).first().asString().isEqualTo("Game mode set to creative"); - assertThat(sender.getGameMode()).isEqualTo(GameMode.CREATIVE); - } - - @Test - void testGms() { - sender.setGameMode(GameMode.SPECTATOR); - List output = executor.run(sender, "gms"); - assertThat(output).first().asString().isEqualTo("Game mode set to survival"); - assertThat(sender.getGameMode()).isEqualTo(GameMode.SURVIVAL); - } - - @Test - void testGamemode() { - List output = executor.run(sender, "gm creative"); - assertThat(output).first().asString().isEqualTo("Game mode set to creative"); - assertThat(sender.getGameMode()).isEqualTo(GameMode.CREATIVE); - } - - @Test - void testGamemodeNumeric() { - List output = executor.run(sender, "gm 1"); - assertThat(output).first().asString().isEqualTo("Game mode set to creative"); - assertThat(sender.getGameMode()).isEqualTo(GameMode.CREATIVE); - } -} diff --git a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandGamemode.kt b/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandGamemode.kt new file mode 100644 index 0000000..1f52c75 --- /dev/null +++ b/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandGamemode.kt @@ -0,0 +1,53 @@ +package `in`.kyle.mcspring.manager.commands + +import `in`.kyle.api.bukkit.entity.TestPlayer +import `in`.kyle.mcspring.test.MCSpringTest +import `in`.kyle.mcspring.test.command.TestCommandExecutor +import org.assertj.core.api.Assertions.assertThat +import org.bukkit.GameMode +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired + +@MCSpringTest +class TestCommandGamemode { + + @Autowired + lateinit var executor: TestCommandExecutor + @Autowired + lateinit var sender: TestPlayer + + @BeforeEach + fun setup() { + sender.gameMode = GameMode.SURVIVAL + } + + @Test + fun testGmc() { + val output = executor.run(sender, "gmc") + assertThat(output).first().asString().isEqualTo("Game mode set to creative") + assertThat(sender.gameMode).isEqualTo(GameMode.CREATIVE) + } + + @Test + fun testGms() { + sender.gameMode = GameMode.SPECTATOR + val output = executor.run(sender, "gms") + assertThat(output).first().asString().isEqualTo("Game mode set to survival") + assertThat(sender.gameMode).isEqualTo(GameMode.SURVIVAL) + } + + @Test + fun testGamemode() { + val output = executor.run(sender, "gm creative") + assertThat(output).first().asString().isEqualTo("Game mode set to creative") + assertThat(sender.gameMode).isEqualTo(GameMode.CREATIVE) + } + + @Test + fun testGamemodeNumeric() { + val output = executor.run(sender, "gm 1") + assertThat(output).first().asString().isEqualTo("Game mode set to creative") + assertThat(sender.gameMode).isEqualTo(GameMode.CREATIVE) + } +} diff --git a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandHeal.java b/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandHeal.java deleted file mode 100644 index 81759cd..0000000 --- a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandHeal.java +++ /dev/null @@ -1,24 +0,0 @@ -package in.kyle.mcspring.manager.commands; - -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; - -import java.util.List; - -import in.kyle.mcspring.test.MCSpringTest; -import in.kyle.mcspring.test.command.TestCommandExecutor; - -import static org.assertj.core.api.Assertions.assertThat; - -@MCSpringTest -class TestCommandHeal { - - @Autowired - TestCommandExecutor commandExecutor; - - @Test - void testHeal() { - List output = commandExecutor.run("heal"); - assertThat(output).first().asString().matches("Healed [^ ]+"); - } -} diff --git a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandHeal.kt b/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandHeal.kt new file mode 100644 index 0000000..bcd9788 --- /dev/null +++ b/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandHeal.kt @@ -0,0 +1,20 @@ +package `in`.kyle.mcspring.manager.commands + +import `in`.kyle.mcspring.test.MCSpringTest +import `in`.kyle.mcspring.test.command.TestCommandExecutor +import org.assertj.core.api.Assertions +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired + +@MCSpringTest +internal class TestCommandHeal { + + @Autowired + lateinit var commandExecutor: TestCommandExecutor + + @Test + fun testHeal() { + val output = commandExecutor.run("heal") + Assertions.assertThat(output).first().asString().matches("Healed [^ ]+") + } +} diff --git a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandOp.java b/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandOp.java deleted file mode 100644 index 7c9492e..0000000 --- a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandOp.java +++ /dev/null @@ -1,35 +0,0 @@ -package in.kyle.mcspring.manager.commands; - -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; - -import java.util.List; - -import in.kyle.mcspring.test.MCSpringTest; -import in.kyle.mcspring.test.command.TestCommandExecutor; - -import static org.assertj.core.api.Assertions.assertThat; - -@MCSpringTest -class TestCommandOp { - - @Autowired - TestCommandExecutor executor; - - @Test - void testOpSelf() { - List messages = executor.run("op"); - assertThat(messages).hasSize(1); - assertThat(messages.get(0)).matches("[^ ]+ is now op"); - } - - @Test - void testOpOther() { - // TODO: 2020-03-13 Need better testing instrumentation - - // server.getOnlinePlayers().add(target); - // List messages = executor.run("op " + target.getName()); - // assertThat(messages).hasSize(1); - // assertThat(messages.get(0)).matches(target.getName() + " is now op"); - } -} diff --git a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandOp.kt b/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandOp.kt new file mode 100644 index 0000000..da11516 --- /dev/null +++ b/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandOp.kt @@ -0,0 +1,30 @@ +package `in`.kyle.mcspring.manager.commands + +import `in`.kyle.mcspring.test.MCSpringTest +import `in`.kyle.mcspring.test.command.TestCommandExecutor +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired + +@MCSpringTest +internal class TestCommandOp { + + @Autowired + lateinit var executor: TestCommandExecutor + + @Test + fun testOpSelf() { + val messages = executor.run("op") + assertThat(messages).hasSize(1) + assertThat(messages[0]).matches("[^ ]+ is now op") + } + + @Test + fun testOpOther() { + // TODO: 2020-03-13 Need better testing instrumentation + // server.getOnlinePlayers().add(target); + // List messages = executor.run("op " + target.getName()); + // assertThat(messages).hasSize(1); + // assertThat(messages.get(0)).matches(target.getName() + " is now op"); + } +} diff --git a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandSpeed.java b/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandSpeed.java deleted file mode 100644 index c0b0c56..0000000 --- a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandSpeed.java +++ /dev/null @@ -1,40 +0,0 @@ -package in.kyle.mcspring.manager.commands; - -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; - -import java.util.List; - -import in.kyle.api.bukkit.entity.TestPlayer; -import in.kyle.mcspring.test.MCSpringTest; -import in.kyle.mcspring.test.command.TestCommandExecutor; - -import static org.assertj.core.api.Assertions.assertThat; - -@MCSpringTest -class TestCommandSpeed { - - @Autowired - TestCommandExecutor executor; - - @Autowired - TestPlayer sender; - - @Test - void testSpeed() { - List messages = executor.run(sender, "speed 10"); - assertThat(messages).first().asString().matches("Speed set to [^ ]+"); - assertThat(sender.getWalkSpeed()).isEqualTo(10F); - assertThat(sender.getFlySpeed()).isEqualTo(10F); - } - - @Test - void testSpeedUsage() { - sender.setWalkSpeed(0); - sender.setFlySpeed(0); - List messages = executor.run(sender, "speed"); - assertThat(messages).first().asString().startsWith("Usage: "); - assertThat(sender.getWalkSpeed()).isEqualTo(0); - assertThat(sender.getFlySpeed()).isEqualTo(0); - } -} diff --git a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandSpeed.kt b/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandSpeed.kt new file mode 100644 index 0000000..e1bb6c8 --- /dev/null +++ b/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandSpeed.kt @@ -0,0 +1,35 @@ +package `in`.kyle.mcspring.manager.commands + +import `in`.kyle.api.bukkit.entity.TestPlayer +import `in`.kyle.mcspring.test.MCSpringTest +import `in`.kyle.mcspring.test.command.TestCommandExecutor +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired + +@MCSpringTest +internal class TestCommandSpeed { + + @Autowired + lateinit var executor: TestCommandExecutor + @Autowired + lateinit var sender: TestPlayer + + @Test + fun testSpeed() { + val messages = executor.run(sender, "speed 10") + assertThat(messages).first().asString().matches("Speed set to [^ ]+") + assertThat(sender.walkSpeed).isEqualTo(10f) + assertThat(sender.flySpeed).isEqualTo(10f) + } + + @Test + fun testSpeedUsage() { + sender.walkSpeed = 0f + sender.flySpeed = 0f + val messages = executor.run(sender, "speed") + assertThat(messages).first().asString().startsWith("Usage: ") + assertThat(sender.walkSpeed).isEqualTo(0f) + assertThat(sender.flySpeed).isEqualTo(0f) + } +} diff --git a/mcspring-build/mcspring-starter/pom.xml b/mcspring-build/mcspring-starter/pom.xml index fc6909f..1801b02 100644 --- a/mcspring-build/mcspring-starter/pom.xml +++ b/mcspring-build/mcspring-starter/pom.xml @@ -40,6 +40,14 @@ in.kyle.mcspring mcspring-maven-plugin 0.0.9 + + + process-classes + + generate-files + + + org.springframework.boot diff --git a/mcspring-build/pom.xml b/mcspring-build/pom.xml index 92b8041..cbead1a 100644 --- a/mcspring-build/pom.xml +++ b/mcspring-build/pom.xml @@ -17,6 +17,53 @@ mcspring-maven-plugin + + 1.3.72 + + + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + ${kotlin.version} + + + org.jetbrains.kotlin + kotlin-test + ${kotlin.version} + test + + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + compile + compile + + compile + + + + test-compile + test-compile + + test-compile + + + + + 1.8 + + + + + 4.0.0 mcspring-build diff --git a/mcspring-examples/simple-factions/src/main/java/org/example/factions/commands/FactionCommand.java b/mcspring-examples/simple-factions/src/main/java/org/example/factions/commands/FactionCommand.java index dcfe5a0..e2aec7f 100644 --- a/mcspring-examples/simple-factions/src/main/java/org/example/factions/commands/FactionCommand.java +++ b/mcspring-examples/simple-factions/src/main/java/org/example/factions/commands/FactionCommand.java @@ -10,7 +10,7 @@ import java.util.stream.Collectors; import in.kyle.mcspring.command.Command; -import in.kyle.mcspring.subcommands.PluginCommand; +import in.kyle.mcspring.subcommands.plugincommand.PluginCommand; import lombok.RequiredArgsConstructor; @Component @@ -25,15 +25,11 @@ void faction(PluginCommand command) { command.on("delete", this::delete); command.on("list", this::list); command.on("join", this::join); - command.on("mine", this::info); + command.on("mine", this::factionMine); command.onInvalid(s -> String.format("Invalid sub-command %s", s)); command.otherwise("Usage: /faction "); } - private void info(PluginCommand command) { - command.then(this::factionMine); - } - private void join(PluginCommand command) { command.withMap(factions.getFactions() .stream() From 5cae86624d58dd94d022fc2f24fa458b61a1a65c Mon Sep 17 00:00:00 2001 From: kylepls Date: Sun, 10 May 2020 13:05:32 -0600 Subject: [PATCH 14/54] Almost finished with command rewrite --- mcspring-api/mcspring-base/pom.xml | 7 +- .../command/BukkitCommandRegistration.java | 36 ---- .../command/BukkitCommandRegistration.kt | 31 +++ .../in/kyle/mcspring/command/Command.java | 23 --- .../java/in/kyle/mcspring/command/Command.kt | 12 ++ .../mcspring/command/CommandController.java | 39 ---- .../mcspring/command/CommandController.kt | 32 +++ .../mcspring/command/CommandRegistration.java | 7 - .../mcspring/command/CommandRegistration.kt | 7 + .../mcspring/command/CommandResolver.java | 17 -- .../kyle/mcspring/command/CommandResolver.kt | 9 + .../kyle/mcspring/command/CommandScanner.java | 37 ---- .../kyle/mcspring/command/CommandScanner.kt | 27 +++ .../mcspring/command/ParameterResolver.kt | 5 + .../java/in/kyle/mcspring/command/Resolver.kt | 5 - .../mcspring/command/SimpleCommandFactory.kt | 43 ++-- .../mcspring/command/SimpleMethodInjection.kt | 42 ++-- ...er.kt => SimpleSpringParameterResolver.kt} | 6 +- mcspring-api/mcspring-subcommands/pom.xml | 4 - .../mcspring/command/TabCommandFactory.java | 53 ----- .../subcommands/PluginCommandResolver.kt | 25 ++- .../PluginCommandTabCompletable.java | 104 ---------- .../plugincommand/PluginCommandBase.kt | 30 ++- .../plugincommand/PluginCommandExecutors.kt | 52 +++-- ...{PluginCommand.kt => PluginCommandImpl.kt} | 18 +- .../plugincommand/PluginCommandRequires.kt | 13 +- .../plugincommand/PluginCommandWith.kt | 48 ++--- .../plugincommand/api/PluginCommand.kt | 157 ++++++++++++++ .../javacompat/HighIQExecutors.kt | 14 -- .../PluginCommandExecutorsJavaSupport.kt | 191 ++++++------------ .../PluginCommandWithJavaSupport.kt | 14 +- .../subcommands/tab/TabCommandFactory.kt | 49 +++++ .../subcommands/tab/TabDiscovery.java | 35 ---- .../mcspring/subcommands/tab/TabDiscovery.kt | 46 +++++ .../mcspring/subcommands/TestConsole.java | 29 --- .../kyle/mcspring/subcommands/TestConsole.kt | 29 +++ .../subcommands/TestJavaPluginCommand.java | 14 +- .../mcspring/subcommands/TestPluginCommand.kt | 44 ++-- .../subcommands/TestTabCompletion.java | 148 -------------- .../mcspring/subcommands/TestTabCompletion.kt | 90 +++++++++ .../subcommands/tab/TestTabDiscovery.kt | 61 ++++++ .../test/command/TestCommandRegistration.kt | 48 ++--- .../manager/commands/CommandClassLoader.kt | 4 +- .../manager/commands/CommandGamemode.kt | 4 +- .../mcspring/manager/commands/CommandHeal.kt | 4 +- .../mcspring/manager/commands/CommandOp.kt | 4 +- .../manager/commands/CommandPlugin.kt | 8 +- .../mcspring/manager/commands/CommandSpeed.kt | 4 +- .../factions/commands/FactionCommand.java | 8 +- 49 files changed, 840 insertions(+), 897 deletions(-) delete mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/BukkitCommandRegistration.java create mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/BukkitCommandRegistration.kt delete mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/Command.java create mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/Command.kt delete mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandController.java create mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandController.kt delete mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandRegistration.java create mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandRegistration.kt delete mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandResolver.java create mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandResolver.kt delete mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandScanner.java create mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandScanner.kt create mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/ParameterResolver.kt delete mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/Resolver.kt rename mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/{SimpleSpringResolver.kt => SimpleSpringParameterResolver.kt} (77%) delete mode 100644 mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/command/TabCommandFactory.java delete mode 100644 mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/PluginCommandTabCompletable.java rename mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/{PluginCommand.kt => PluginCommandImpl.kt} (71%) create mode 100644 mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/api/PluginCommand.kt create mode 100644 mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/tab/TabCommandFactory.kt delete mode 100644 mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/tab/TabDiscovery.java create mode 100644 mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/tab/TabDiscovery.kt delete mode 100644 mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestConsole.java create mode 100644 mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestConsole.kt delete mode 100644 mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestTabCompletion.java create mode 100644 mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestTabCompletion.kt create mode 100644 mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/tab/TestTabDiscovery.kt diff --git a/mcspring-api/mcspring-base/pom.xml b/mcspring-api/mcspring-base/pom.xml index 74d3300..acdb418 100644 --- a/mcspring-api/mcspring-base/pom.xml +++ b/mcspring-api/mcspring-base/pom.xml @@ -33,11 +33,6 @@ mcspring-jar-loader ${project.version} - - net.jodah - typetools - 0.6.2 - org.springframework.boot spring-boot-loader @@ -70,7 +65,7 @@ org.jetbrains.kotlin kotlin-reflect - 1.3.61 + 1.3.72 compile diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/BukkitCommandRegistration.java b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/BukkitCommandRegistration.java deleted file mode 100644 index 9c33f9e..0000000 --- a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/BukkitCommandRegistration.java +++ /dev/null @@ -1,36 +0,0 @@ -package in.kyle.mcspring.command; - -import org.bukkit.plugin.Plugin; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.stereotype.Component; - -import java.lang.reflect.Method; -import java.util.Arrays; - -import lombok.RequiredArgsConstructor; -import lombok.var; - -@Component -@ConditionalOnBean(Plugin.class) -@RequiredArgsConstructor -class BukkitCommandRegistration implements CommandRegistration { - - private final CommandController controller; - private final CommandFactory commandFactory; - - @Override - public void register(Command command, Method method, Object object) { - String name = command.value(); - var bukkitCommand = commandFactory.makeCommand(method, object, name); - bukkitCommand.setAliases(Arrays.asList(command.aliases())); - bukkitCommand.setDescription(command.description()); - bukkitCommand.setUsage(command.usage()); - bukkitCommand.setPermission(command.permission()); - bukkitCommand.setPermissionMessage(command.permissionMessage()); - controller.registerCommand(bukkitCommand); - } - - interface CommandFactory { - org.bukkit.command.Command makeCommand(Method method, Object object, String name); - } -} diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/BukkitCommandRegistration.kt b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/BukkitCommandRegistration.kt new file mode 100644 index 0000000..d9bbec2 --- /dev/null +++ b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/BukkitCommandRegistration.kt @@ -0,0 +1,31 @@ +package `in`.kyle.mcspring.command + +import org.bukkit.plugin.Plugin +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean +import org.springframework.stereotype.Component +import java.lang.reflect.Method +import org.bukkit.command.Command as BukkitCommand + +@Component +@ConditionalOnBean(Plugin::class) +internal class BukkitCommandRegistration( + private val controller: CommandController, + private val commandFactory: CommandFactory +) : CommandRegistration { + + override fun register(command: Command, method: Method, obj: Any) { + val bukkitCommand = commandFactory.makeCommand(method, obj, command.value) + with(bukkitCommand) { + aliases = command.aliases.toList() + description = command.description + usage = command.usage + permission = command.permission + permissionMessage = command.permissionMessage + } + controller.registerCommand(bukkitCommand) + } + + internal interface CommandFactory { + fun makeCommand(method: Method, obj: Any, name: String): BukkitCommand + } +} diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/Command.java b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/Command.java deleted file mode 100644 index 8c45394..0000000 --- a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/Command.java +++ /dev/null @@ -1,23 +0,0 @@ -package in.kyle.mcspring.command; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface Command { - - String value(); - - String[] aliases() default {}; - - String description() default ""; - - String usage() default ""; - - String permission() default ""; - - String permissionMessage() default ""; -} diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/Command.kt b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/Command.kt new file mode 100644 index 0000000..2abe177 --- /dev/null +++ b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/Command.kt @@ -0,0 +1,12 @@ +package `in`.kyle.mcspring.command + +@kotlin.annotation.Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.FUNCTION) +annotation class Command( + val value: String, + val aliases: Array = [], + val description: String = "", + val usage: String = "", + val permission: String = "", + val permissionMessage: String = "" +) diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandController.java b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandController.java deleted file mode 100644 index d4edc40..0000000 --- a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandController.java +++ /dev/null @@ -1,39 +0,0 @@ -package in.kyle.mcspring.command; - -import org.bukkit.Bukkit; -import org.bukkit.command.Command; -import org.bukkit.command.CommandMap; -import org.bukkit.plugin.Plugin; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.context.annotation.Bean; -import org.springframework.stereotype.Controller; - -import java.lang.reflect.Field; - -import lombok.RequiredArgsConstructor; -import lombok.SneakyThrows; - -@Controller -@RequiredArgsConstructor -@ConditionalOnBean(Plugin.class) -class CommandController { - - private final CommandMap commandMap = getCommandMap(); - private final Plugin plugin; - - public void registerCommand(Command command) { - commandMap.register(command.getLabel(), plugin.getName(), command); - } - - @SneakyThrows - private CommandMap getCommandMap() { - Field bukkitCommandMap = Bukkit.getServer().getClass().getDeclaredField("commandMap"); - bukkitCommandMap.setAccessible(true); - return (CommandMap) bukkitCommandMap.get(Bukkit.getServer()); - } - - @Bean - CommandMap commandMap() { - return commandMap; - } -} diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandController.kt b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandController.kt new file mode 100644 index 0000000..e0b0321 --- /dev/null +++ b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandController.kt @@ -0,0 +1,32 @@ +package `in`.kyle.mcspring.command + +import org.bukkit.Bukkit +import org.bukkit.command.Command +import org.bukkit.command.CommandMap +import org.bukkit.plugin.Plugin +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean +import org.springframework.context.annotation.Bean +import org.springframework.stereotype.Controller + +@Controller +@ConditionalOnBean(Plugin::class) +internal class CommandController( + private val plugin: Plugin +) { + private val commandMap = getCommandMap() + + fun registerCommand(command: Command) { + commandMap.register(command.label, plugin.name, command) + } + + private fun getCommandMap(): CommandMap { + val bukkitCommandMap = Bukkit.getServer().javaClass.getDeclaredField("commandMap") + bukkitCommandMap.isAccessible = true + return bukkitCommandMap[Bukkit.getServer()] as CommandMap + } + + @Bean + fun commandMap(): CommandMap { + return commandMap + } +} diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandRegistration.java b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandRegistration.java deleted file mode 100644 index 0a8a4f4..0000000 --- a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandRegistration.java +++ /dev/null @@ -1,7 +0,0 @@ -package in.kyle.mcspring.command; - -import java.lang.reflect.Method; - -public interface CommandRegistration { - void register(Command command, Method method, Object object); -} diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandRegistration.kt b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandRegistration.kt new file mode 100644 index 0000000..6d27a2f --- /dev/null +++ b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandRegistration.kt @@ -0,0 +1,7 @@ +package `in`.kyle.mcspring.command + +import java.lang.reflect.Method + +interface CommandRegistration { + fun register(command: Command, method: Method, obj: Any) +} diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandResolver.java b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandResolver.java deleted file mode 100644 index 16d6309..0000000 --- a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandResolver.java +++ /dev/null @@ -1,17 +0,0 @@ -package in.kyle.mcspring.command; - -import org.bukkit.command.CommandSender; - -import lombok.Value; - -public interface CommandResolver { - - Resolver makeResolver(Command command); - - @Value - class Command { - CommandSender sender; - String[] args; - String label; - } -} diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandResolver.kt b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandResolver.kt new file mode 100644 index 0000000..763885a --- /dev/null +++ b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandResolver.kt @@ -0,0 +1,9 @@ +package `in`.kyle.mcspring.command + +import org.bukkit.command.CommandSender + +interface CommandResolver { + fun makeResolver(command: Command): ParameterResolver + + data class Command(val sender: CommandSender, val args: List, val label: String) +} diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandScanner.java b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandScanner.java deleted file mode 100644 index fdd9811..0000000 --- a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandScanner.java +++ /dev/null @@ -1,37 +0,0 @@ -package in.kyle.mcspring.command; - -import org.springframework.beans.BeansException; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.stereotype.Component; - -import java.lang.reflect.Method; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import in.kyle.mcspring.util.SpringScanner; -import lombok.AllArgsConstructor; -import lombok.var; - -@Component -@AllArgsConstructor -class CommandScanner implements ApplicationContextAware { - - private final SpringScanner scanner; - private final CommandRegistration commandRegistration; - private final Set registeredCommands = new HashSet<>(); - - @Override - public void setApplicationContext(ApplicationContext ctx) throws BeansException { - Map scan = scanner.scanMethods(Command.class); - for (var e : scan.entrySet()) { - if (!registeredCommands.contains(e.getKey())) { - Command command = e.getKey().getAnnotation(Command.class); - commandRegistration.register(command, e.getKey(), e.getValue()); - registeredCommands.add(e.getKey()); - } - } - } - -} diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandScanner.kt b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandScanner.kt new file mode 100644 index 0000000..faa1889 --- /dev/null +++ b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandScanner.kt @@ -0,0 +1,27 @@ +package `in`.kyle.mcspring.command + +import `in`.kyle.mcspring.util.SpringScanner +import org.springframework.context.ApplicationContext +import org.springframework.context.ApplicationContextAware +import org.springframework.stereotype.Component +import java.lang.reflect.Method +import java.util.* + +@Component +internal class CommandScanner( + private val scanner: SpringScanner, + private val commandRegistration: CommandRegistration +) : ApplicationContextAware { + + private val registeredCommands: MutableSet = HashSet() + + override fun setApplicationContext(ctx: ApplicationContext) { + val scan = scanner.scanMethods(Command::class.java) + scan.filterKeys { it !in registeredCommands } + .forEach { (key, value) -> + val command = key.getAnnotation(Command::class.java) + commandRegistration.register(command, key, value) + registeredCommands.add(key) + } + } +} diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/ParameterResolver.kt b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/ParameterResolver.kt new file mode 100644 index 0000000..44ac8a3 --- /dev/null +++ b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/ParameterResolver.kt @@ -0,0 +1,5 @@ +package `in`.kyle.mcspring.command + +interface ParameterResolver { + fun resolve(parameter: Class<*>): Any? +} diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/Resolver.kt b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/Resolver.kt deleted file mode 100644 index 7db96cf..0000000 --- a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/Resolver.kt +++ /dev/null @@ -1,5 +0,0 @@ -package `in`.kyle.mcspring.command - -interface Resolver { - operator fun invoke(parameter: Class<*>): Any? -} diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleCommandFactory.kt b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleCommandFactory.kt index 52e9873..416055f 100644 --- a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleCommandFactory.kt +++ b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleCommandFactory.kt @@ -1,8 +1,6 @@ package `in`.kyle.mcspring.command import `in`.kyle.mcspring.command.BukkitCommandRegistration.CommandFactory -import lombok.RequiredArgsConstructor -import lombok.SneakyThrows import org.bukkit.command.Command import org.bukkit.command.CommandExecutor import org.bukkit.command.CommandSender @@ -11,41 +9,36 @@ import org.bukkit.plugin.Plugin import org.springframework.boot.autoconfigure.condition.ConditionalOnBean import org.springframework.stereotype.Component import java.lang.reflect.Method -import java.util.stream.Collectors @Component @ConditionalOnBean(Plugin::class) -internal open class SimpleCommandFactory : CommandFactory { - private val methodInjection: SimpleMethodInjection? = null - private val commandResolvers: Set? = null - private val plugin: Plugin? = null +open class SimpleCommandFactory( + private val injection: SimpleMethodInjection, + private val commandResolvers: Set, + private val plugin: Plugin +) : CommandFactory { - override fun makeCommand(method: Method, `object`: Any, name: String): Command { + override fun makeCommand(method: Method, obj: Any, name: String): Command { val constructor = PluginCommand::class.java.getDeclaredConstructor(String::class.java, Plugin::class.java) constructor.isAccessible = true val command = constructor.newInstance(name, plugin) - val executor = makeExecutor(method, `object`) + val executor = makeExecutor(method, obj) command.setExecutor(executor) return command } - fun makeExecutor(method: Method, `object`: Any?): CommandExecutor { - return CommandExecutor { commandSender: CommandSender, bukkitCommand: Command?, label: String?, args: Array? -> + private fun makeExecutor(method: Method, obj: Any): CommandExecutor { + return CommandExecutor { commandSender: CommandSender, bukkitCommand: Command, label: String, args: Array -> try { -// val command = CommandResolver.Command(commandSender, args, label) -// val contextResolvers = commandResolvers!!.stream() -// .map { r: CommandResolver -> r.makeResolver(command) } -// .collect(Collectors.toList()) - TODO() -// val result: Any = methodInjection.invoke(method, -// `object`, -// contextResolvers, -// commandSender, -// args, -// label) -// if (result != null) { -// commandSender.sendMessage(result.toString()) -// } + val command = CommandResolver.Command(commandSender, args.toList(), label) + val contextParameterResolvers: List = commandResolvers.map { it.makeResolver(command) } + .plus(injection.makeResolvers(listOf(commandSender, args, label, bukkitCommand))) + val types: List> = method.parameterTypes.toList() + val parameters = injection.getParameters(types, contextParameterResolvers) + val result = method.invoke(obj, *parameters) + if (result !is Unit) { + commandSender.sendMessage(result.toString()) + } } catch (exception: RuntimeException) { throw RuntimeException("Could not invoke method " + method.name, exception) diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleMethodInjection.kt b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleMethodInjection.kt index 4e4c810..cb7432e 100644 --- a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleMethodInjection.kt +++ b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleMethodInjection.kt @@ -2,8 +2,9 @@ package `in`.kyle.mcspring.command import org.springframework.context.annotation.Lazy import org.springframework.stereotype.Component +import kotlin.reflect.KFunction +import kotlin.reflect.jvm.isAccessible import kotlin.reflect.jvm.javaType -import kotlin.reflect.jvm.reflect /** * Used to inject method parameters @@ -12,35 +13,42 @@ import kotlin.reflect.jvm.reflect @Lazy @Component class SimpleMethodInjection( - private val resolvers: List + private val parameterResolvers: List ) { - fun makeResolvers(vararg contextObjects: Any?): List { + fun makeResolvers(contextObjects: List): List { return contextObjects.filterNotNull().map(this::makeResolverFor) } - private fun makeResolverFor(contextObject: Any): Resolver { - return object : Resolver { - override fun invoke(parameter: Class<*>): Any? { - return if (contextObject::class.java.isAssignableFrom(parameter)) { - contextObject - } else { - null - } + private fun makeResolverFor(contextObject: Any): ParameterResolver { + return object : ParameterResolver { + override fun resolve(parameter: Class<*>): Any? { + return contextObject.takeIf { parameter.isAssignableFrom(contextObject.javaClass) } } } } - fun getParameters(func: Function, contextResolvers: List): Array { - val parameters = func.reflect()!!.parameters.map { it.type.javaType as Class<*> } - return getParameters(parameters, contextResolvers) + fun run(function: KFunction, contextObjects: List): Any { + val types = function.parameters.map { it.type.javaType as Class<*> } + return run(function, types, contextObjects) } - fun getParameters(parameters: List>, contextResolvers: List): Array { - val methodResolvers = contextResolvers.plus(resolvers).toMutableList() + fun run(function: KFunction, types: List>, contextObjects: List): Any { + val contextResolvers = makeResolvers(contextObjects) + val parameters = getParameters(types, contextResolvers) + try { + function.isAccessible = true + } catch (e: RuntimeException) { + // java compat required + } + return function.call(*parameters) + } + + fun getParameters(parameters: List>, contextParameterResolvers: List): Array { + val methodResolvers = contextParameterResolvers.plus(parameterResolvers).toMutableList() return parameters.map { parameter: Class<*> -> - val candidates = methodResolvers.map { it(parameter) }.toMutableList() + val candidates = methodResolvers.map { it.resolve(parameter) }.toMutableList() val firstIndex = candidates.indexOfFirst { it != null } require(firstIndex != -1) { "Unable to resolve parameter $parameter for func($parameters)" } methodResolvers.cycleElement(firstIndex) diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleSpringResolver.kt b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleSpringParameterResolver.kt similarity index 77% rename from mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleSpringResolver.kt rename to mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleSpringParameterResolver.kt index 298cd05..9596b5d 100644 --- a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleSpringResolver.kt +++ b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleSpringParameterResolver.kt @@ -5,10 +5,10 @@ import org.springframework.context.ApplicationContext import org.springframework.stereotype.Component @Component -internal class SimpleSpringResolver( +internal class SimpleSpringParameterResolver( private val context: ApplicationContext -) : Resolver { - override fun invoke(parameter: Class<*>): Any? { +) : ParameterResolver { + override fun resolve(parameter: Class<*>): Any? { return try { context.getBean(parameter) } catch (e: NoSuchBeanDefinitionException) { diff --git a/mcspring-api/mcspring-subcommands/pom.xml b/mcspring-api/mcspring-subcommands/pom.xml index 73591cc..6ec179f 100644 --- a/mcspring-api/mcspring-subcommands/pom.xml +++ b/mcspring-api/mcspring-subcommands/pom.xml @@ -22,10 +22,6 @@ ${spigot.version} provided - - org.projectlombok - lombok - in.kyle.mcspring mcspring-test diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/command/TabCommandFactory.java b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/command/TabCommandFactory.java deleted file mode 100644 index 8f8f77a..0000000 --- a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/command/TabCommandFactory.java +++ /dev/null @@ -1,53 +0,0 @@ -package in.kyle.mcspring.command; - -//import in.kyle.mcspring.subcommands.tab.TabDiscovery; -//import lombok.SneakyThrows; -//import lombok.var; -// -//@Primary -//@Component -//@ConditionalOnBean(Plugin.class) -//class TabCommandFactory extends SimpleCommandFactory { -// -// private final TabDiscovery tabDiscovery; -// -// public TabCommandFactory(SimpleMethodInjection injection, -// Set commandResolvers, -// Plugin plugin, -// TabDiscovery tabDiscovery) { -// super(injection, commandResolvers, plugin); -// this.tabDiscovery = tabDiscovery; -// } -// -// @Override -// public Command makeCommand(Method method, Object object, String name) { -// var command = (org.bukkit.command.PluginCommand) super.makeCommand(method, object, name); -// if (method.getParameterCount() == 1 && method.getParameters()[0].getType() -// .isAssignableFrom(PluginCommand.class)) { -// command.setTabCompleter(makeTabCompleter(method, object)); -// } -// return command; -// } -// -// private Consumer methodToConsumer(Method method, -// Object object) { -// return pluginCommand -> { -// method.setAccessible(true); -// invoke(method, object, pluginCommand); -// }; -// } -// -// @SneakyThrows -// private Object invoke(Method method, -// Object object, -// PluginCommand command) { -// return method.invoke(object, command); -// } -// -// private TabCompleter makeTabCompleter(Method method, Object object) { -// return (sender, command, s, strings) -> { -// var consumer = methodToConsumer(method, object); -// return tabDiscovery.getCompletions(sender, String.join(" ", strings), consumer); -// }; -// } -//} diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/PluginCommandResolver.kt b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/PluginCommandResolver.kt index c855097..1da8f1f 100644 --- a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/PluginCommandResolver.kt +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/PluginCommandResolver.kt @@ -1,29 +1,28 @@ package `in`.kyle.mcspring.subcommands import `in`.kyle.mcspring.command.CommandResolver -import `in`.kyle.mcspring.command.Resolver +import `in`.kyle.mcspring.command.ParameterResolver import `in`.kyle.mcspring.command.SimpleMethodInjection -import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommand -import lombok.AllArgsConstructor +import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandImpl +import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand import org.springframework.stereotype.Component -import java.util.* @Component -@AllArgsConstructor internal class PluginCommandResolver( private val injection: SimpleMethodInjection ) : CommandResolver { - override fun makeResolver(command: CommandResolver.Command): Resolver { - return object : Resolver { - override fun invoke(parameter: Class<*>): Any? { - return if (parameter.isAssignableFrom(PluginCommand::class.java)) Optional.of( - makeCommand(command)) else Optional.empty() + + override fun makeResolver(command: CommandResolver.Command): ParameterResolver { + return object : ParameterResolver { + override fun resolve(parameter: Class<*>): Any? { + return if (parameter.isAssignableFrom(PluginCommand::class.java)) { + return makeCommand(command) + } else null } } } - private fun makeCommand(command: CommandResolver.Command): PluginCommand { - val args = mutableListOf(*command.args) - return PluginCommand(injection, command.sender, args) + private fun makeCommand(command: CommandResolver.Command): PluginCommandImpl { + return PluginCommandImpl(injection, command.sender, command.args.toMutableList(), true) } } diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/PluginCommandTabCompletable.java b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/PluginCommandTabCompletable.java deleted file mode 100644 index 5786ae7..0000000 --- a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/PluginCommandTabCompletable.java +++ /dev/null @@ -1,104 +0,0 @@ -//package in.kyle.mcspring.subcommands; -// -//import org.bukkit.command.CommandSender; -// -//import java.util.ArrayList; -//import java.util.Collections; -//import java.util.List; -//import java.util.Map; -//import java.util.Optional; -//import java.util.Set; -//import java.util.function.Consumer; -//import java.util.function.Function; -//import java.util.function.Supplier; -// -//import in.kyle.mcspring.command.SimpleMethodInjection; -//import in.kyle.mcspring.subcommands.plugincommand.Executors; -//import in.kyle.mcspring.subcommands.plugincommand.PluginCommand; -//import lombok.Getter; -//import lombok.var; -// -//public class PluginCommandTabCompletable extends PluginCommand { -// -// @Getter -// private final List tabCompletionOptions = new ArrayList<>(); -// @Getter -// private PluginCommandTabCompletable child; -// -// public PluginCommandTabCompletable(SimpleMethodInjection injection, -// CommandSender sender, -// List parts) { -// super(injection, sender, parts, Collections.emptyList()); -// } -// -// public State getState() { -// return state; -// } -// -// public boolean hasChild() { -// return child != null; -// } -// -// @Override -// public void on(String command, Consumer executor) { -// tabCompletionOptions.add(command); -// super.on(command, executor); -// } -// -// @Override -// public void onInvalid(Function help) { -// state = State.INVALID_ARG; -// } -// -// @Override -// public void with(Function> processor, Function error) { -// tabCompletionOptions.clear(); -// if (hasExecutablePart()) { -// parts.remove(0); -// state = State.INVALID_ARG; -// } else { -// state = State.MISSING_ARG; -// } -// } -// -// @Override -// public void withMap(Map options, Function invalidArg) { -// if (hasExecutablePart()) { -// Set validOptions = options.keySet(); -// String part = parts.remove(0).toLowerCase(); -// if (!validOptions.contains(part)) { -// state = State.INVALID_ARG; -// } -// } else { -// state = State.MISSING_ARG; -// tabCompletionOptions.addAll(options.keySet()); -// } -// } -// -// @Override -// protected void callOn(String command, Executors executors, int argSize) { -// tabCompletionOptions.add(command); -// super.callOn(command, executors, argSize); -// } -// -// @Override -// public void otherwise(Supplier supplier) { -// } -// -// -// @Override -// public void then(Runnable r) { -// } -// -// @Override -// PluginCommand copy() { -// var command = -// new PluginCommandTabCompletable(injection, sender, parts); -// if (child == null) { -// child = command; -// return command; -// } else { -// throw new IllegalStateException("Command cannot have 2 children"); -// } -// } -//} diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandBase.kt b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandBase.kt index dc3a819..b671f92 100644 --- a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandBase.kt +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandBase.kt @@ -1,20 +1,20 @@ package `in`.kyle.mcspring.subcommands.plugincommand import org.bukkit.command.CommandSender -import kotlin.reflect.KFunction interface PluginCommandBase { val injections: MutableList val parts: MutableList val sender: CommandSender + val completions: MutableList + val runExecutors: Boolean + var child: PluginCommandBase? var state: State fun nextPart(): String? - fun consumePart() - fun dirtiesState(predicate: Boolean = true, requiredStates: Array = arrayOf(State.CLEAN), resultingState: State = State.SUCCESS, @@ -22,7 +22,29 @@ interface PluginCommandBase { fun sendMessage(message: String) - fun run(function: KFunction) + fun addCompletion(completion: String, type: String) { + var stage = if (completions.size != 0) completions.last() else addCompletionStage(type, type) + if (stage.type != type) { + stage = addCompletionStage(type, type) + } + stage.completions.add(completion) + } + + fun addCompletionStage(name: String, type: String): CompletionStage { + val stage = CompletionStage(name, type) + completions.add(stage) + return stage + } + + fun execute(e: () -> Unit) { + if (runExecutors) { + e() + } + } + + data class CompletionStage(val name: String, val type: String) { + val completions: MutableList = mutableListOf() + } enum class State { CLEAN, diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandExecutors.kt b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandExecutors.kt index 35ad804..e0a99dc 100644 --- a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandExecutors.kt +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandExecutors.kt @@ -1,48 +1,60 @@ package `in`.kyle.mcspring.subcommands.plugincommand import `in`.kyle.mcspring.command.SimpleMethodInjection +import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandBase.State +import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand import kotlin.reflect.KFunction -import kotlin.reflect.jvm.isAccessible +import kotlin.reflect.full.createType +import kotlin.reflect.full.isSubtypeOf -interface PluginCommandExecutors : PluginCommandBase { +interface PluginCommandExecutors : PluginCommandBase, PluginCommand { val injection: SimpleMethodInjection - fun otherwise(e: KFunction) { - dirtiesState(requiredStates = arrayOf(PluginCommandBase.State.CLEAN, PluginCommandBase.State.MISSING_ARG)) { run(e) } + override fun otherwise(e: KFunction) { + dirtiesState(requiredStates = arrayOf(State.CLEAN, State.MISSING_ARG)) { + execute { runWithContext(e) } + } } - fun on(command: String, e: KFunction) { + override fun on(command: String, e: KFunction) { + addCompletion(command, "on") if (nextPart().equals(command)) { - dirtiesState { run(e) } + dirtiesState { + val receivesPluginCommand = e.parameters.any { + it.type.isSubtypeOf(PluginCommand::class.createType()) + } + if (receivesPluginCommand) { + runWithContext(e, sendOutput = false) + } else { + execute { runWithContext(e) } + } + } } } - fun onInvalid(errorMessage: (String) -> String) { + override fun onInvalid(errorMessage: (String) -> String) { if (nextPart() != null) { dirtiesState { - sendMessage(errorMessage(parts[0])) + execute { sendMessage(errorMessage(parts[0])) } } } } - fun then(e: KFunction) { - dirtiesState { run(e) } - } + override fun then(e: KFunction) = dirtiesState { execute { runWithContext(e) } } - override fun run(function: KFunction) { - val contextResolvers = injection.makeResolvers(sender, makeNextExecutor(), *injections.toTypedArray()) - val parameters = injection.getParameters(function, contextResolvers) - function.isAccessible = true - val output = function.call(*parameters) - if (output !is Unit) { - sendMessage(output.toString()) + private fun runWithContext(e: KFunction, sendOutput: Boolean = true) { + val nextExecutor = makeNextExecutor() + child = nextExecutor + val out = injection.run(e, injections.plus(nextExecutor)) + if (out !is Unit && sendOutput) { + sendMessage(out.toString()) } } - fun makeNextExecutor(): PluginCommand? { + private fun makeNextExecutor(): PluginCommandImpl? { return if (parts.isNotEmpty()) { - PluginCommand(injection, sender, parts).apply { + PluginCommandImpl(injection, sender, parts, runExecutors).apply { injections.addAll(this.injections) parts.removeAt(0) } diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommand.kt b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandImpl.kt similarity index 71% rename from mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommand.kt rename to mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandImpl.kt index f9a31f7..7b7dea8 100644 --- a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommand.kt +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandImpl.kt @@ -1,21 +1,26 @@ package `in`.kyle.mcspring.subcommands.plugincommand import `in`.kyle.mcspring.command.SimpleMethodInjection +import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandBase.CompletionStage import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandBase.State import `in`.kyle.mcspring.subcommands.plugincommand.javacompat.PluginCommandExecutorsJavaSupport import `in`.kyle.mcspring.subcommands.plugincommand.javacompat.PluginCommandWithJavaSupport import org.bukkit.ChatColor import org.bukkit.command.CommandSender +import java.lang.RuntimeException -open class PluginCommand( +open class PluginCommandImpl( override val injection: SimpleMethodInjection, override val sender: CommandSender, - override val parts: MutableList + override val parts: MutableList, + override val runExecutors: Boolean ) : PluginCommandBase, PluginCommandWith, PluginCommandRequires, PluginCommandExecutors, PluginCommandExecutorsJavaSupport, PluginCommandWithJavaSupport { override val injections = mutableListOf() override var state: State = State.CLEAN + override var child: PluginCommandBase? = null + override val completions: MutableList = mutableListOf() override fun sendMessage(message: String) { if (message.isNotBlank()) { @@ -33,9 +38,10 @@ open class PluginCommand( } } - override fun nextPart(): String? = parts.getOrNull(0).takeIf { state == State.CLEAN } - - override fun consumePart() { - parts.removeAt(0) + override fun nextPart(): String? { + val part = parts.getOrNull(0).takeIf { state == State.CLEAN } + assert(part?.isNotBlank() ?: true) + return part } } + diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandRequires.kt b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandRequires.kt index 69c1976..3f0b8b9 100644 --- a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandRequires.kt +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandRequires.kt @@ -1,28 +1,29 @@ package `in`.kyle.mcspring.subcommands.plugincommand +import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand import org.bukkit.command.ConsoleCommandSender import org.bukkit.entity.Player -interface PluginCommandRequires : PluginCommandBase { +interface PluginCommandRequires : PluginCommandBase, PluginCommand { - fun requires(predicate: Boolean, errorMessage: () -> String) { + override fun requires(predicate: Boolean, errorMessage: () -> String) { if (!predicate) { dirtiesState { sendMessage(errorMessage()) } } } - fun requiresPlayerSender(errorMessage: () -> String) { + override fun requiresPlayerSender(errorMessage: () -> String) { requires(sender is Player, errorMessage) } - fun requireConsoleSender(errorMessage: () -> String) { + override fun requiresConsoleSender(errorMessage: () -> String) { requires(sender is ConsoleCommandSender, errorMessage) } - fun requiresPermission(permission: String, errorMessage: () -> String) { + override fun requiresPermission(permission: String, errorMessage: () -> String) { requires(sender.hasPermission(permission), errorMessage) } - fun requiresOp(errorMessage: () -> String) = requires(sender.isOp, errorMessage) + override fun requiresOp(errorMessage: () -> String) = requires(sender.isOp, errorMessage) } diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandWith.kt b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandWith.kt index 2ff3105..8460841 100644 --- a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandWith.kt +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandWith.kt @@ -1,55 +1,57 @@ package `in`.kyle.mcspring.subcommands.plugincommand import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandBase.State +import `in`.kyle.mcspring.subcommands.plugincommand.api.Err1 +import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand import org.bukkit.Bukkit -interface PluginCommandWith : PluginCommandBase { +interface PluginCommandWith : PluginCommandBase, PluginCommand { - fun withString() = with({ it }) + override fun withString() = with({ it }, stageName = "string") - fun withSentence() { + override fun withSentence() { + addCompletionStage("sentence", "with") if (nextPart() != null) { - val out = injections.add(parts.joinToString()) + injections.add(parts.joinToString()) parts.clear() } } - fun withInt(errorMessage: (String) -> String) = with({ it.toIntOrNull() }, errorMessage) + override fun withInt(errorMessage: Err1) = with({ it.toIntOrNull() }, errorMessage, "int") - fun withDouble(errorMessage: (String) -> String) = with({ it.toDoubleOrNull() }, errorMessage) + override fun withDouble(errorMessage: Err1) = with({ it.toDoubleOrNull() }, + errorMessage, "double") - fun withOfflinePlayer(errorMessage: (String) -> String) { - with({ Bukkit.getOfflinePlayer(it) }, errorMessage) + override fun withOfflinePlayer(errorMessage: Err1) { + with({ Bukkit.getOfflinePlayer(it) }, errorMessage, "offline player") } - fun withPlayer(errorMessage: (String) -> String) { - with({ Bukkit.getPlayer(it) }, errorMessage) + override fun withPlayer(errorMessage: Err1) { + withMap(Bukkit.getOnlinePlayers().associateBy { it.name }, errorMessage, "player") } - fun withWorld(errorMessage: (String) -> String) { - with({ worldName: String -> - Bukkit.getWorlds().find { it.name === worldName } - }, errorMessage) + override fun withWorld(errorMessage: Err1) { + withMap(Bukkit.getWorlds().associateBy { it.name }, errorMessage, "world") } - fun withXYZInt(errorMessage: (String) -> String) { - repeat(3) { withInt(errorMessage) } - } + override fun withXYZInt(errorMessage: Err1) = repeat(3) { withInt(errorMessage) } - fun withMap(options: Map, errorMessage: (String) -> String) { - with({ options[it] }, errorMessage) + override fun withMap(options: Map, errorMessage: Err1, stageName: String) { + with({ options[it] }, errorMessage, stageName) + options.keys.forEach { addCompletion(it, "with") } } - fun withAny(options: Collection, errorMessage: (String) -> String) { - withMap(options.associateBy { it }, errorMessage) + override fun withAny(options: Collection, errorMessage: Err1, stageName: String) { + withMap(options.associateBy { it }, errorMessage, stageName) } - fun with(processor: (String) -> Any?, errorMessage: (String) -> String = { "" }) { + override fun with(processor: (String) -> Any?, errorMessage: Err1, stageName: String) { + addCompletionStage(stageName, "with") val part = nextPart() if (part != null) { val parsed = processor(part) if (parsed != null) { - consumePart() + parts.removeAt(0) injections.add(parsed) } else { dirtiesState { sendMessage(errorMessage(part)) } diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/api/PluginCommand.kt b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/api/PluginCommand.kt new file mode 100644 index 0000000..270d60a --- /dev/null +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/api/PluginCommand.kt @@ -0,0 +1,157 @@ +package `in`.kyle.mcspring.subcommands.plugincommand.api + +import `in`.kyle.mcspring.subcommands.plugincommand.javacompat.HighIQExecutors +import org.bukkit.command.CommandSender +import kotlin.reflect.KFunction + +typealias Err = () -> String +typealias Err1 = (String) -> String + +interface PluginCommand { + + val sender: CommandSender + + fun otherwise(e: KFunction) + + fun otherwise(e: HighIQExecutors.O0) + + fun otherwise(e: HighIQExecutors.O1) + + fun otherwise(e: HighIQExecutors.O2) + + fun otherwise(e: HighIQExecutors.O3) + + fun otherwise(e: HighIQExecutors.O4) + + fun otherwise(e: HighIQExecutors.O5) + + fun otherwise(e: HighIQExecutors.O6) + + fun otherwise(message: String) + + fun otherwise(e: HighIQExecutors.E1) + + fun otherwise(e: HighIQExecutors.E2) + + fun otherwise(e: HighIQExecutors.E3) + + fun otherwise(e: HighIQExecutors.E4) + + fun otherwise(e: HighIQExecutors.E5) + + fun otherwise(e: HighIQExecutors.E6) + + fun on(command: String, e: KFunction) + + fun on(command: String, e: HighIQExecutors.O0) + + fun on(command: String, e: HighIQExecutors.O1) + + fun on(command: String, e: HighIQExecutors.O2) + + fun on(command: String, e: HighIQExecutors.O3) + + fun on(command: String, e: HighIQExecutors.O4) + + fun on(command: String, e: HighIQExecutors.O5) + + fun on(command: String, e: HighIQExecutors.O6) + + fun on(command: String, e: HighIQExecutors.E1) + + fun on(command: String, e: HighIQExecutors.E2) + + fun on(command: String, e: HighIQExecutors.E3) + + fun on(command: String, e: HighIQExecutors.E4) + + fun on(command: String, e: HighIQExecutors.E5) + + fun on(command: String, e: HighIQExecutors.E6) + + fun onInvalid(errorMessage: Err1) + + fun then(e: KFunction) + + fun then(e: HighIQExecutors.O0) + + fun then(e: HighIQExecutors.O1) + + fun then(e: HighIQExecutors.O2) + + fun then(e: HighIQExecutors.O3) + + fun then(e: HighIQExecutors.O4) + + fun then(e: HighIQExecutors.O5) + + fun then(e: HighIQExecutors.O6) + + fun then(e: HighIQExecutors.E1) + + fun then(e: HighIQExecutors.E2) + + fun then(e: HighIQExecutors.E3) + + fun then(e: HighIQExecutors.E4) + + fun then(e: HighIQExecutors.E5) + + fun then(e: HighIQExecutors.E6) + + fun requires(predicate: Boolean, errorMessage: Err) + + fun requiresPlayerSender(errorMessage: Err) + + fun requiresConsoleSender(errorMessage: Err) + + fun requiresPermission(permission: String, errorMessage: Err) + + fun requiresOp(errorMessage: Err) + + fun with(processor: (String) -> Any?, errorMessage: Err1 = { "" }, stageName: String = "UNKNOWN") + + fun with(processor: (String) -> Any?, errorMessage: Err1 = { "" }) + = with(processor, errorMessage, "UNKNOWN") + + fun withString() + + fun withSentence() + + fun withInt(errorMessage: Err1) + + fun withInt(errorMessage: String) + + fun withDouble(errorMessage: Err1) + + fun withDouble(errorMessage: String) + + fun withOfflinePlayer(errorMessage: Err1) + + fun withPlayer(errorMessage: Err1) + + fun withPlayer(errorMessage: String) + + fun withWorld(errorMessage: Err1) + + fun withWorld(errorMessage: String) + + fun withXYZInt(errorMessage: Err1) + + fun withXYZInt(errorMessage: String) + + fun withMap(options: Map, errorMessage: Err1, stageName: String = "UNKNOWN") + + fun withMap(options: Map, errorMessage: Err1) + = withMap(options, errorMessage, "UNKNOWN") + + fun withMap(options: Map, errorMessage: String) + + fun withAny(options: Collection, errorMessage: Err1, stageName: String = "UNKNOWN") + + fun withAny(options: Collection, errorMessage: Err1) + = withAny(options, errorMessage, "UNKNOWN") + + fun withAny(options: Collection, errorMessage: String) + +} diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/javacompat/HighIQExecutors.kt b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/javacompat/HighIQExecutors.kt index 14bbb83..53a9745 100644 --- a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/javacompat/HighIQExecutors.kt +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/javacompat/HighIQExecutors.kt @@ -6,20 +6,6 @@ import java.lang.reflect.Method interface HighIQExecutors { - @JvmDefault - fun getMethod(e: HighIQExecutors): Method { - val argCount = this::class.java.declaredMethods.find { it.name == "handle" }!!.parameterCount - val writeReplace = this::class.java.getDeclaredMethod("writeReplace") - writeReplace.isAccessible = true - val sl = writeReplace.invoke(e) as SerializedLambda - val methodName = sl.implMethodName - val clazz = Class.forName(sl.implClass.replace("/", ".")) - - return clazz.methods.plus(clazz.declaredMethods) - .filter { it.name == methodName }.first { it.parameters.size == argCount } - ?: throw RuntimeException("Method not found") - } - interface E1 : HighIQExecutors, Serializable { fun handle(a1: A) } diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/javacompat/PluginCommandExecutorsJavaSupport.kt b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/javacompat/PluginCommandExecutorsJavaSupport.kt index caa4a8d..782311f 100644 --- a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/javacompat/PluginCommandExecutorsJavaSupport.kt +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/javacompat/PluginCommandExecutorsJavaSupport.kt @@ -3,192 +3,115 @@ package `in`.kyle.mcspring.subcommands.plugincommand.javacompat import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandBase.State import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandExecutors import `in`.kyle.mcspring.subcommands.plugincommand.javacompat.HighIQExecutors.* -import net.jodah.typetools.TypeResolver import java.lang.invoke.SerializedLambda import java.lang.reflect.Method -import kotlin.jvm.internal.FunctionReference import kotlin.reflect.KFunction -import kotlin.reflect.jvm.javaMethod -import kotlin.reflect.jvm.kotlinFunction @Suppress("UNCHECKED_CAST") interface PluginCommandExecutorsJavaSupport : PluginCommandExecutors { - fun on(command: String, e: O1) { - this.on(command, e::handle as KFunction) - } + override fun on(command: String, e: O0) = this.on(command, e::handle as KFunction) - fun on(command: String, e: O2) { - this.on(command, e::handle as KFunction) - } + override fun on(command: String, e: O1) = this.on(command, e::handle as KFunction) - fun on(command: String, e: O3) { - this.on(command, e::handle as KFunction) - } + override fun on(command: String, e: O2) = this.on(command, e::handle as KFunction) - fun on(command: String, e: O4) { - this.on(command, e::handle as KFunction) - } + override fun on(command: String, e: O3) = this.on(command, e::handle as KFunction) - fun on(command: String, e: O5) { - this.on(command, e::handle as KFunction) - } + override fun on(command: String, e: O4) = this.on(command, e::handle as KFunction) - fun on(command: String, e: O6) { - this.on(command, e::handle as KFunction) - } + override fun on(command: String, e: O5) = this.on(command, e::handle as KFunction) - fun on(command: String, e: O0) { - this.on(command, e::handle as KFunction) - } + override fun on(command: String, e: O6) = this.on(command, e::handle as KFunction) - fun on(command: String, e: E1) { - this.on(command, e::handle as KFunction) - } + override fun on(command: String, e: E1) = this.on(command, e::handle as KFunction) - fun on(command: String, e: E2) { - this.on(command, e::handle as KFunction) - } + override fun on(command: String, e: E2) = this.on(command, e::handle as KFunction) - fun on(command: String, e: E3) { - this.on(command, e::handle as KFunction) - } + override fun on(command: String, e: E3) = this.on(command, e::handle as KFunction) - fun on(command: String, e: E4) { - this.on(command, e::handle as KFunction) - } + override fun on(command: String, e: E4) = this.on(command, e::handle as KFunction) - fun on(command: String, e: E5) { - this.on(command, e::handle as KFunction) - } + override fun on(command: String, e: E5) = this.on(command, e::handle as KFunction) - fun on(command: String, e: E6) { - this.on(command, e::handle as KFunction) - } + override fun on(command: String, e: E6) = this.on(command, e::handle as KFunction) - fun then(e: O0) { - this.then(e, e::handle as KFunction) - } + override fun then(e: O0) = this.then(e, e::handle as KFunction) - fun then(e: O1) { - this.then(e, e::handle as KFunction) - } + override fun then(e: O1) = this.then(e, e::handle as KFunction) - fun then(e: O2) { - this.then(e, e::handle as KFunction) - } + override fun then(e: O2) = this.then(e, e::handle as KFunction) - fun then(e: O3) { - this.then(e, e::handle as KFunction) - } + override fun then(e: O3) = this.then(e, e::handle as KFunction) - fun then(e: O4) { - this.then(e, e::handle as KFunction) - } + override fun then(e: O4) = this.then(e, e::handle as KFunction) - fun then(e: O5) { - this.then(e, e::handle as KFunction) - } + override fun then(e: O5) = this.then(e, e::handle as KFunction) - fun then(e: O6) { - this.then(e, e::handle as KFunction) - } + override fun then(e: O6) = this.then(e, e::handle as KFunction) - fun then(e: E1) { - this.then(e, e::handle as KFunction) - } + override fun then(e: E1) = this.then(e, e::handle as KFunction) - fun then(e: E2) { - this.then(e, e::handle as KFunction) - } + override fun then(e: E2) = this.then(e, e::handle as KFunction) - fun then(e: E3) { - this.then(e, e::handle as KFunction) - } + override fun then(e: E3) = this.then(e, e::handle as KFunction) - fun then(e: E4) { - this.then(e, e::handle as KFunction) - } + override fun then(e: E4) = this.then(e, e::handle as KFunction) - fun then(e: E5) { - this.then(e, e::handle as KFunction) - } + override fun then(e: E5) = this.then(e, e::handle as KFunction) - fun then(e: E6) { - this.then(e, e::handle as KFunction) - } + override fun then(e: E6) = this.then(e, e::handle as KFunction) - fun otherwise(e: O0) { - this.then(e, e::handle as KFunction) - } + override fun otherwise(e: O0) = this.then(e, e::handle as KFunction) - fun otherwise(e: O1) { - this.then(e, e::handle as KFunction) - } + override fun otherwise(e: O1) = this.then(e, e::handle as KFunction) - fun otherwise(e: O2) { - this.then(e, e::handle as KFunction) - } + override fun otherwise(e: O2) = this.then(e, e::handle as KFunction) - fun otherwise(e: O3) { - this.then(e, e::handle as KFunction) - } + override fun otherwise(e: O3) = this.then(e, e::handle as KFunction) - fun otherwise(e: O4) { - this.then(e, e::handle as KFunction) - } + override fun otherwise(e: O4) = this.then(e, e::handle as KFunction) - fun otherwise(e: O5) { - this.then(e, e::handle as KFunction) - } + override fun otherwise(e: O5) = this.then(e, e::handle as KFunction) - fun otherwise(e: O6) { - this.then(e, e::handle as KFunction) - } + override fun otherwise(e: O6) = this.then(e, e::handle as KFunction) - fun otherwise(e: E1) { - this.then(e, e::handle as KFunction) - } + override fun otherwise(e: E1) = this.then(e, e::handle as KFunction) - fun otherwise(e: E2) { - this.then(e, e::handle as KFunction) - } + override fun otherwise(e: E2) = this.then(e, e::handle as KFunction) - fun otherwise(e: E3) { - this.then(e, e::handle as KFunction) - } + override fun otherwise(e: E3) = this.then(e, e::handle as KFunction) - fun otherwise(e: E4) { - this.then(e, e::handle as KFunction) - } + override fun otherwise(e: E4) = this.then(e, e::handle as KFunction) - fun otherwise(e: E5) { - this.then(e, e::handle as KFunction) - } + override fun otherwise(e: E5) = this.then(e, e::handle as KFunction) + + override fun otherwise(e: E6) = this.then(e, e::handle as KFunction) - fun otherwise(e: E6) { - this.then(e, e::handle as KFunction) + override fun otherwise(message: String) { + dirtiesState(requiredStates = arrayOf(State.CLEAN, State.MISSING_ARG)) { sendMessage(message) } } private fun then(e: HighIQExecutors, function: KFunction) { - val method = e.getMethod(e) - val types: List> = method.parameterTypes.toList() - val contextResolvers = injection.makeResolvers(sender, makeNextExecutor(), *injections.toTypedArray()) dirtiesState { - val parameters = injection.getParameters(types, contextResolvers) - method.isAccessible = true - method.invoke(e, *parameters) - } - if (function.isAbstract) {// java method - val target = (function as FunctionReference).boundReceiver - val claz = function.javaMethod!!.declaringClass; - val args = TypeResolver.resolveRawArguments(function.javaMethod!!.declaringClass, target.javaClass) - println(args) + val types: List> = getMethod(e).parameterTypes.toList() + injection.run(function, types) } } - fun otherwise(message: String) { - dirtiesState(requiredStates = arrayOf(State.CLEAN, State.MISSING_ARG)) { sendMessage(message) } + companion object { + private fun getMethod(e: HighIQExecutors): Method { + val argCount = e::class.java.declaredMethods.find { it.name == "handle" }!!.parameterCount + val writeReplace = e::class.java.getDeclaredMethod("writeReplace") + writeReplace.isAccessible = true + val sl = writeReplace.invoke(e) as SerializedLambda + val methodName = sl.implMethodName + val clazz = Class.forName(sl.implClass.replace("/", ".")) + + return clazz.methods.plus(clazz.declaredMethods) + .filter { it.name == methodName }.first { it.parameters.size == argCount } + ?: throw RuntimeException("Method not found") + } } } diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/javacompat/PluginCommandWithJavaSupport.kt b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/javacompat/PluginCommandWithJavaSupport.kt index 17a0ecd..54544a3 100644 --- a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/javacompat/PluginCommandWithJavaSupport.kt +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/javacompat/PluginCommandWithJavaSupport.kt @@ -4,18 +4,18 @@ import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandWith interface PluginCommandWithJavaSupport : PluginCommandWith { - fun withInt(errorMessage: String) = super.withInt { errorMessage } + override fun withInt(errorMessage: String) = super.withInt { errorMessage } - fun withDouble(errorMessage: String) = super.withDouble { errorMessage } + override fun withDouble(errorMessage: String) = super.withDouble { errorMessage } - fun withPlayer(errorMessage: String) = super.withPlayer { errorMessage } + override fun withPlayer(errorMessage: String) = super.withPlayer { errorMessage } - fun withWorld(errorMessage: String) = super.withWorld { errorMessage } + override fun withWorld(errorMessage: String) = super.withWorld { errorMessage } - fun withXYZInt(errorMessage: String) = super.withXYZInt { errorMessage } + override fun withXYZInt(errorMessage: String) = super.withXYZInt { errorMessage } - fun withMap(options: Map, errorMessage: String) = with({ options[it] }, { errorMessage }) + override fun withMap(options: Map, errorMessage: String) = withMap(options) { errorMessage } - fun withAny(options: Collection, errorMessage: String) = with({ options.associateBy { it } }, { errorMessage }) + override fun withAny(options: Collection, errorMessage: String) = withAny(options) { errorMessage } } diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/tab/TabCommandFactory.kt b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/tab/TabCommandFactory.kt new file mode 100644 index 0000000..db276b4 --- /dev/null +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/tab/TabCommandFactory.kt @@ -0,0 +1,49 @@ +package `in`.kyle.mcspring.subcommands.tab + +import `in`.kyle.mcspring.command.CommandResolver +import `in`.kyle.mcspring.command.SimpleCommandFactory +import `in`.kyle.mcspring.command.SimpleMethodInjection +import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand +import org.bukkit.command.CommandSender +import org.bukkit.command.TabCompleter +import org.bukkit.plugin.Plugin +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean +import org.springframework.context.annotation.Primary +import org.springframework.stereotype.Component +import java.lang.reflect.Method +import org.bukkit.command.Command as BukkitCommand +import org.bukkit.command.PluginCommand as BukkitPluginCommand + +@Primary +@Component +@ConditionalOnBean(Plugin::class) +internal class TabCommandFactory( + injection: SimpleMethodInjection, + commandResolvers: Set, + val plugin: Plugin, + private val tabDiscovery: TabDiscovery +) : SimpleCommandFactory(injection, commandResolvers, plugin) { + + override fun makeCommand(method: Method, obj: Any, name: String): BukkitCommand { + val command = super.makeCommand(method, obj, name) as BukkitPluginCommand + if (method.parameterCount == 1 + && method.parameters[0].type.isAssignableFrom(PluginCommand::class.java)) { + command.tabCompleter = makeTabCompleter(method, obj) + } + return command + } + + private fun methodToConsumer(method: Method, obj: Any): (PluginCommand) -> Unit { + return { + method.isAccessible = true + method.invoke(obj, it) + } + } + + private fun makeTabCompleter(method: Method, obj: Any): TabCompleter { + return TabCompleter { sender: CommandSender, _: BukkitCommand, _: String, strings: Array -> + val consumer = methodToConsumer(method, obj) + tabDiscovery.getCompletions(sender, strings.joinToString(" "), consumer) + } + } +} diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/tab/TabDiscovery.java b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/tab/TabDiscovery.java deleted file mode 100644 index e64ad56..0000000 --- a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/tab/TabDiscovery.java +++ /dev/null @@ -1,35 +0,0 @@ -package in.kyle.mcspring.subcommands.tab; - -//@Component -//@RequiredArgsConstructor -//public class TabDiscovery { -// -// private final SimpleMethodInjection injection; -// -// public List getCompletions(CommandSender sender, -// String commandString, -// Consumer consumer) { -// if (!commandString.isEmpty() && !commandString.endsWith(" ")) { -// // TODO: 2020-02-25 Enable partial completions -// return Collections.emptyList(); -// } -// var parts = new ArrayList<>(Arrays.asList(commandString.split(" "))); -// if (parts.get(0).isEmpty()) { -// parts.remove(0); -// } -// var command = new PluginCommandTabCompletable(injection, sender, parts); -// consumer.accept(command); -// -// return getCompletions(command); -// } -// -// private List getCompletions(PluginCommandTabCompletable command) { -// if (command.hasChild()) { -// return getCompletions(command.getChild()); -// } else if (command.getState() == State.MISSING_ARG || command.getState() == State.CLEAN) { -// return command.getTabCompletionOptions(); -// } else { -// return Collections.emptyList(); -// } -// } -//} diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/tab/TabDiscovery.kt b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/tab/TabDiscovery.kt new file mode 100644 index 0000000..8d1f69d --- /dev/null +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/tab/TabDiscovery.kt @@ -0,0 +1,46 @@ +package `in`.kyle.mcspring.subcommands.tab + +import `in`.kyle.mcspring.command.SimpleMethodInjection +import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandBase.State +import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandImpl +import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand +import org.bukkit.command.CommandSender +import org.springframework.stereotype.Component + +@Component +class TabDiscovery( + private val injection: SimpleMethodInjection +) { + fun getCompletions(sender: CommandSender, + commandString: String, + consumer: (PluginCommand) -> Unit): List { + val (parts, prefix) = getCommandParts(commandString) + + val command = PluginCommandImpl(injection, sender, parts, false) + consumer(command) + + val completionCandidates = getCompletions(command) + return completionCandidates.filter { it.startsWith(prefix) } + } + + private fun getCommandParts(command: String): Pair, String> { + val parts = command.split(" ").filter { it.isNotBlank() }.toMutableList() + + val prefix = if (!command.endsWith(" ") && command.isNotBlank()) { + parts.removeAt(parts.size - 1) + } else { + "" + } + return Pair(parts, prefix) + } + + private fun getCompletions(command: PluginCommandImpl): List { + return if (command.child != null) { + getCompletions(command.child as PluginCommandImpl) + } else if (command.state == State.MISSING_ARG || command.state == State.CLEAN) { + command.completions.flatMap { it.completions } + } else { + emptyList() + } + } +} diff --git a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestConsole.java b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestConsole.java deleted file mode 100644 index c5addf6..0000000 --- a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestConsole.java +++ /dev/null @@ -1,29 +0,0 @@ -package in.kyle.mcspring.subcommands; - -import org.bukkit.command.CommandSender; -import org.springframework.stereotype.Component; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.function.Consumer; - -import in.kyle.mcspring.command.SimpleMethodInjection; -import in.kyle.mcspring.subcommands.plugincommand.PluginCommand; -import lombok.RequiredArgsConstructor; - -@Component -@RequiredArgsConstructor -public class TestConsole { - - private final SimpleMethodInjection injection; - - public void run(CommandSender sender, String commandString, Consumer consumer) { - List parts = new ArrayList<>(Arrays.asList(commandString.split(" "))); - if (parts.get(0).isEmpty()) { - parts.remove(0); - } - PluginCommand command = new PluginCommand(injection, sender, parts); - consumer.accept(command); - } -} diff --git a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestConsole.kt b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestConsole.kt new file mode 100644 index 0000000..46087ca --- /dev/null +++ b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestConsole.kt @@ -0,0 +1,29 @@ +package `in`.kyle.mcspring.subcommands + +import `in`.kyle.mcspring.command.SimpleMethodInjection +import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandImpl +import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player +import org.mockito.Mockito.mock +import java.util.function.Consumer + +object TestConsole { + + private val injection: SimpleMethodInjection = SimpleMethodInjection(emptyList()) + + fun run(commandString: String, + consumer: (PluginCommand) -> Unit, + runExecutors: Boolean = true, + sender: CommandSender = mock(Player::class.java)): PluginCommandImpl { + val parts = if (!commandString.isBlank()) commandString.split(" ") else emptyList() + val command = PluginCommandImpl(injection, sender, parts.toMutableList(), runExecutors) + consumer(command) + return command + } + + fun run(commandString: String, + consumer: Consumer, + runExecutors: Boolean = true, + sender: CommandSender = mock(Player::class.java)) = run(commandString, { consumer.accept(it) }, runExecutors, sender) +} diff --git a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestJavaPluginCommand.java b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestJavaPluginCommand.java index 2aae169..d9366d8 100644 --- a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestJavaPluginCommand.java +++ b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestJavaPluginCommand.java @@ -8,7 +8,7 @@ import java.util.ArrayList; import java.util.List; -import in.kyle.mcspring.subcommands.plugincommand.PluginCommand; +import in.kyle.mcspring.subcommands.plugincommand.api.PluginCommand; import in.kyle.mcspring.test.MCSpringTest; import static org.assertj.core.api.Assertions.*; @@ -38,16 +38,16 @@ public void testSimple() { class Test { void parse(PluginCommand command) { command.withString(); - command.then(this::handle); + command.then(this::parse2); } - String handle(Player sender, String arg) { - return arg; + String parse2(PluginCommand command, String parsedPart) { + assertThat(command).isNotNull(); + return String.format("part is %s", parsedPart); } } - console.run(sender, "test-arg", new Test()::parse); - assertThat(messages).containsExactly("test-arg"); + console.run("test-arg another-arg", new Test()::parse, true, mock(Player.class)); + assertThat(messages).containsExactly("part is test-arg true"); } - } diff --git a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestPluginCommand.kt b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestPluginCommand.kt index a04197b..5c5f5c5 100644 --- a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestPluginCommand.kt +++ b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestPluginCommand.kt @@ -1,21 +1,15 @@ package `in`.kyle.mcspring.subcommands -import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommand -import `in`.kyle.mcspring.test.MCSpringTest +import `in`.kyle.mcspring.subcommands.TestConsole.run +import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand import org.assertj.core.api.Assertions.assertThat import org.bukkit.command.CommandSender import org.bukkit.entity.Player -import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.mockito.Mockito.* -import org.springframework.beans.factory.annotation.Autowired -import java.util.concurrent.atomic.AtomicBoolean -@MCSpringTest internal class TestPluginCommand { - @Autowired - lateinit var console: TestConsole lateinit var sender: Player lateinit var outputMessages: MutableList @@ -31,23 +25,20 @@ internal class TestPluginCommand { fun testDirectExecutor() { class Test { fun exec1(command: PluginCommand) = command.on("subcommand1", this::handler1) - fun exec2(command: PluginCommand) { command.withString() command.on("subcommand2", this::handler2) } fun handler1() = "handler1" - fun handler2(string: String) = "handler2: $string" } - val test = Test() - console.run(sender, "subcommand1", test::exec1) + run("subcommand1", Test()::exec1) assertThat(outputMessages).containsExactly("handler1") outputMessages.clear() - console.run(sender, "test-string subcommand2", test::exec2) + run("test-string subcommand2", Test()::exec2, sender = sender) assertThat(outputMessages).containsExactly("handler2: test-string") } @@ -59,7 +50,7 @@ internal class TestPluginCommand { fun exec(sender: CommandSender) = "Hello World" } - console.run(sender, "", Test()::root) + run("", Test()::root, sender = sender) assertThat(outputMessages).containsExactly("Hello World") } @@ -75,13 +66,14 @@ internal class TestPluginCommand { // } // } // Test test = new Test(); - // console.run(sender, "", test::root); + // run(sender, "", test::root); // assertThat(outputMessages).isEmpty(); // // sender.setOp(true); - // console.run(sender, "", test::root); + // run(sender, "", test::root); // assertThat(outputMessages).containsExactly("Works"); // } + @Test fun testCommandSingleSentenceArg() { class Test { @@ -93,7 +85,7 @@ internal class TestPluginCommand { fun exec(sentence: String) = sentence } - console.run(sender, "Hello to you world", Test()::root) + run("Hello to you world", Test()::root, sender = sender) assertThat(outputMessages).containsExactly("Hello to you world") } @@ -101,7 +93,7 @@ internal class TestPluginCommand { fun testCommandIntArgs() { class Test { fun root(command: PluginCommand) { - command.withXYZInt("error") + command.withXYZInt { "error" } command.then(this::exec) } @@ -113,7 +105,7 @@ internal class TestPluginCommand { } } - console.run(sender, "1 2 3", Test()::root) + run("1 2 3", Test()::root, sender = sender) assertThat(outputMessages).containsExactly("true") } @@ -134,11 +126,11 @@ internal class TestPluginCommand { fun exec(sender: CommandSender) = "Works" } - console.run(sender, "a b", Test()::root) + run("a b", Test()::root, sender = sender) assertThat(outputMessages).containsExactly("Works") outputMessages.clear() - console.run(sender, "a c", Test()::root) + run("a c", Test()::root, sender = sender) assertThat(outputMessages).isEmpty() } @@ -151,19 +143,19 @@ internal class TestPluginCommand { } fun a(command: PluginCommand) { - command.withInt("error") + command.withInt{ "error" } command.otherwise("should run if int passed or missing arg") } } - console.run(sender, "", Test()::root) + run("", Test()::root, sender = sender) assertThat(outputMessages).containsExactly("no subcommand at root") outputMessages.clear() - console.run(sender, "a", Test()::root) + run("a", Test()::root, sender = sender) assertThat(outputMessages).containsExactly("should run if int passed or missing arg") outputMessages.clear() - console.run(sender, "a 2", Test()::root) + run("a 2", Test()::root, sender = sender) assertThat(outputMessages).containsExactly("should run if int passed or missing arg") } @@ -173,7 +165,7 @@ internal class TestPluginCommand { fun root(command: PluginCommand) = command.withInt { "$it is not an int" } } - console.run(sender, "swag", Test()::root) + run("swag", Test()::root, sender = sender) assertThat(outputMessages).containsExactly("swag is not an int") } } diff --git a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestTabCompletion.java b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestTabCompletion.java deleted file mode 100644 index 25899be..0000000 --- a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestTabCompletion.java +++ /dev/null @@ -1,148 +0,0 @@ -package in.kyle.mcspring.subcommands; - -//@MCSpringTest -//public class TestTabCompletion { -// -// @Autowired -// TestPlayer sender; -// @Autowired -// TabDiscovery tabDiscovery; -// List outputMessages; -// -// @BeforeEach -// void setup() { -// outputMessages = new ArrayList<>(); -// sender.getMessages().subscribe(outputMessages::add); -// } -// -// @Test -// void testTabWithDirectExecution() { -// class Test { -// -// void root(PluginCommand command) { -// command.on("test", this::exec); -// } -// -// void exec(String string) { -// fail("Should not run"); -// } -// } -// Test test = new Test(); -// var completions = tabDiscovery.getCompletions(sender, "", test::root); -// assertThat(completions).containsExactly("test"); -// } -// -// @Test -// void testNoTab() { -// class Test { -// void root(PluginCommand command) { -// command.then(this::exec); -// } -// -// void exec(CommandSender sender) { -// fail("Should not run"); -// } -// } -// -// Test test = new Test(); -// var completions = tabDiscovery.getCompletions(sender, "", test::root); -// -// assertThat(outputMessages).isEmpty(); -// assertThat(completions).isEmpty(); -// } -// -// @Test -// void testSimpleSubs() { -// class Test { -// void root(PluginCommand command) { -// Consumer dontRun = cmd -> fail("should not run"); -// command.on("a", dontRun); -// command.on("b", dontRun); -// command.on("c", dontRun); -// command.then(this::exec); -// } -// -// void exec(CommandSender sender) { -// fail("Should not run"); -// } -// } -// -// Test test = new Test(); -// var completions = tabDiscovery.getCompletions(sender, "", test::root); -// -// assertThat(outputMessages).isEmpty(); -// assertThat(completions).containsSequence("a", "b", "c"); -// } -// -// @Test -// void testAny() { -// class Test { -// void root(PluginCommand command) { -// Function f = ignored -> { -// fail("should not run"); -// return ""; -// }; -// command.withAny(f, "a", "b", "c"); -// command.withAny(f, "d", "e", "f"); -// command.then(this::exec); -// } -// -// void exec(CommandSender sender) { -// fail("Should not run"); -// } -// } -// -// Test test = new Test(); -// var completions = tabDiscovery.getCompletions(sender, "", test::root); -// -// assertThat(outputMessages).isEmpty(); -// assertThat(completions).containsSequence("a", "b", "c", "d", "e", "f"); -// -// } -// -// @Test -// void testOnTake1() { -// class Test { -// void root(PluginCommand command) { -// Consumer doesNothing = cmd -> {}; -// command.on("a", doesNothing); -// command.on("b", doesNothing); -// command.on("c", doesNothing); -// command.then(this::exec); -// } -// -// void exec(CommandSender sender) { -// fail("Should not run"); -// } -// } -// -// Test test = new Test(); -// var completions = tabDiscovery.getCompletions(sender, "", test::root); -// assertThat(completions).containsSequence("a", "b", "c"); -// -// completions = tabDiscovery.getCompletions(sender, "a", test::root); -// assertThat(completions).isEmpty(); -// } -// -// @Test -// void testInvalidStop() { -// class Test { -// void root(PluginCommand command) { -// Consumer doesNothing = cmd -> fail("should not run"); -// command.on("a", doesNothing); -// command.on("b", doesNothing); -// command.on("c", doesNothing); -// command.onInvalid(s -> String.format("%s is not valid", s)); -// command.then(this::exec); -// } -// -// void exec(CommandSender sender) { -// fail("Should not run"); -// } -// } -// -// Test test = new Test(); -// var completions = tabDiscovery.getCompletions(sender, "g", test::root); -// assertThat(completions).isEmpty(); -// } -//} diff --git a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestTabCompletion.kt b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestTabCompletion.kt new file mode 100644 index 0000000..a7c1149 --- /dev/null +++ b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestTabCompletion.kt @@ -0,0 +1,90 @@ +package `in`.kyle.mcspring.subcommands + +import `in`.kyle.mcspring.subcommands.TestConsole.run +import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.fail +import org.junit.jupiter.api.Test + +internal class TestTabCompletion { + + @Test + fun testWithString() { + class Test { + fun base(command: PluginCommand) { + command.withString() + command.withString() + } + } + + val command = run("hello world", Test()::base, runExecutors = false) + + assertThat(command.completions).hasSize(2) + assertThat(command.completions.first().completions).isEmpty() + assertThat(command.completions.last().completions).isEmpty() + } + + @Test + fun testThenNoExecute() { + class Test { + fun base(command: PluginCommand) { + command.then(this::fail) + } + + fun fail(): Unit = fail("Should not run") + } + + run("", Test()::base, runExecutors = false) + } + + @Test + fun testOnRecurse() { + class Test { + fun base(command: PluginCommand) { + command.on("subcommand-1", this::sub1) + command.on("other-1", this::fail) + command.on("other-2", this::fail) + } + + fun sub1(command: PluginCommand) { + command.on("subcommand-2", this::fail) + } + + fun fail(): Unit = fail("Should not run") + } + + val command = run("subcommand-1 subcommand-2", Test()::base, runExecutors = false) + assertThat(command.completions).hasSize(1) + assertThat(command.completions[0].completions).hasSize(3) + assertThat(command.child).isNotNull() + assertThat(command.child!!.completions).hasSize(1) + } + + @Test + fun testMultiOn() { + class Test { + fun base(command: PluginCommand) { + command.on("a", this::fail) + command.on("b", this::fail) + command.on("c", this::fail) + } + + fun fail(): Unit = fail() + } + + val command = run("", Test()::base, runExecutors = false) + assertThat(command.completions).hasSize(1) + assertThat(command.completions[0].completions).containsExactly("a", "b", "c") + } + + @Test + fun testWithAny() { + class Test { + fun base(command: PluginCommand) = command.withAny(listOf("a", "b", "c")) { "" } + } + + val command = run("", Test()::base, runExecutors = false) + assertThat(command.completions).hasSize(1) + assertThat(command.completions[0].completions).containsExactly("a", "b", "c") + } +} diff --git a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/tab/TestTabDiscovery.kt b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/tab/TestTabDiscovery.kt new file mode 100644 index 0000000..dd77f89 --- /dev/null +++ b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/tab/TestTabDiscovery.kt @@ -0,0 +1,61 @@ +package `in`.kyle.mcspring.subcommands.tab + +import `in`.kyle.mcspring.command.SimpleMethodInjection +import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand +import `in`.kyle.mcspring.test.MCSpringTest +import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.fail +import org.bukkit.entity.Player +import org.junit.jupiter.api.Test +import org.mockito.Mockito.mock + +class TestTabDiscovery { + + private val tabDiscovery = TabDiscovery(SimpleMethodInjection(emptyList())) + + class TestCommandSinglePart { + fun base(command: PluginCommand) { + command.withAny(listOf("sub1", "sub2", "other3"), "err") + } + } + + class TestCommandMultiPart { + fun base(command: PluginCommand) { + command.on("sub1", this::error) + command.on("sub2", this::error) + command.on("other3", this::other3) + } + + private fun other3(command: PluginCommand) { + command.withAny(listOf("other1", "other2"), "fail") + } + + private fun error(): Unit = fail("fail"); + } + + @Test + fun testFullCompletions() { + getCompletions("", TestCommandSinglePart()::base).also { + assertThat(it).containsExactly("sub1", "sub2", "other3") + } + } + + @Test + fun testPartialCompletions() { + getCompletions("sub", TestCommandSinglePart()::base).also { + assertThat(it).containsExactly("sub1", "sub2") + } + } + + @Test + fun testCompletionsMultiPart() { + getCompletions("other3 ", TestCommandMultiPart()::base).also { + assertThat(it).containsExactly("other1", "other2") + } + } + + private fun getCompletions(commandString: String, consumer: (PluginCommand) -> Unit): List { + return tabDiscovery.getCompletions(mock(Player::class.java), commandString, consumer) + } +} diff --git a/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/command/TestCommandRegistration.kt b/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/command/TestCommandRegistration.kt index b2e0c6c..cd98f7e 100644 --- a/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/command/TestCommandRegistration.kt +++ b/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/command/TestCommandRegistration.kt @@ -4,31 +4,27 @@ import `in`.kyle.mcspring.command.Command import `in`.kyle.mcspring.command.CommandRegistration import `in`.kyle.mcspring.command.CommandResolver import `in`.kyle.mcspring.command.SimpleMethodInjection -import lombok.RequiredArgsConstructor import org.bukkit.command.CommandSender import org.springframework.stereotype.Component import java.lang.reflect.Method import java.util.* import java.util.function.Consumer import kotlin.reflect.KFunction +import kotlin.reflect.jvm.javaMethod import kotlin.reflect.jvm.kotlinFunction +typealias CommandSig = (CommandSender, String, Array) -> Unit + @Component -@RequiredArgsConstructor internal class TestCommandRegistration( private val injection: SimpleMethodInjection, private val commandResolvers: Set ) : CommandRegistration { - private val commandExecutors: MutableMap = HashMap() - -// override fun register(command: Command, e: KFunction) { -// val executor = makeExecutor(e) -// getAllNames(command).forEach(Consumer { key: String -> commandExecutors[key] = executor }) -// } + private val commandExecutors: MutableMap = HashMap() - @Suppress("UNCHECKED_CAST") - override fun register(command: Command, method: Method, `object`: Any) { + override fun register(command: Command, method: Method, obj: Any) { + @Suppress("UNCHECKED_CAST") val executor = makeExecutor(method.kotlinFunction as KFunction) getAllNames(command).forEach(Consumer { key: String -> commandExecutors[key] = executor }) } @@ -39,33 +35,27 @@ internal class TestCommandRegistration( return commands } - private fun makeExecutor(e: KFunction): Executor { - return object : Executor { - override fun execute(sender: CommandSender, label: String, args: Array) { - val temp = CommandResolver.Command(sender, args, label) - - val thisResolvers = injection.makeResolvers(listOf(sender, args, label)) - val contextResolvers = commandResolvers.map { it.makeResolver(temp) } - - val parameters = injection.getParameters(e, contextResolvers.plus(thisResolvers)) - val result = e.call(*parameters) - if (result != null) { - sender.sendMessage(result.toString()) - } + private fun makeExecutor(e: KFunction): CommandSig { + return { sender: CommandSender, label: String, args: Array -> + val temp = CommandResolver.Command(sender, args.toList(), label) + val thisResolvers = injection.makeResolvers(listOf(sender, args, label)) + val contextResolvers = commandResolvers.map { it.makeResolver(temp) } + + val parameters = injection.getParameters(e.javaMethod!!.parameterTypes.toList(), + contextResolvers.plus + (thisResolvers)) + val result = e.call(*parameters) + if (result !is Unit) { + sender.sendMessage(result.toString()) } } } fun run(command: String, sender: CommandSender, label: String, args: Array) { if (command in commandExecutors) { - commandExecutors[command]!!.execute(sender, label, args) + commandExecutors[command]!!(sender, label, args) } else { throw RuntimeException("Command $command is not registered. Make sure to @Import it") } } - - internal interface Executor { - fun execute(sender: CommandSender, label: String, args: Array) - } - } diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandClassLoader.kt b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandClassLoader.kt index 76c9a48..4ce54a3 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandClassLoader.kt +++ b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandClassLoader.kt @@ -1,14 +1,14 @@ package `in`.kyle.mcspring.manager.commands import `in`.kyle.mcspring.command.Command -import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommand +import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandImpl import org.springframework.stereotype.Component @Component internal class CommandClassLoader { @Command(value = "classloader", description = "Show ClassLoader information for a specific class", usage = "/classloader ") - fun classLoader(command: PluginCommand) { + fun classLoader(command: PluginCommandImpl) { command.withString(); command.then(::executeClassLoader); command.otherwise("Usage: /classloader ") diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandGamemode.kt b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandGamemode.kt index 4f16278..74d10b6 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandGamemode.kt +++ b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandGamemode.kt @@ -1,7 +1,7 @@ package `in`.kyle.mcspring.manager.commands import `in`.kyle.mcspring.command.Command -import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommand +import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandImpl import org.bukkit.GameMode import org.bukkit.entity.Player import org.springframework.stereotype.Component @@ -9,7 +9,7 @@ import org.springframework.stereotype.Component @Component internal class CommandGamemode { @Command(value = "gamemode", aliases = ["gm"], description = "Set your game mode", usage = "/gamemode ") - fun gamemode(command: PluginCommand) { + fun gamemode(command: PluginCommandImpl) { command.requiresPlayerSender { "Only players can run this command." } val gamemodes = mutableMapOf() diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandHeal.kt b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandHeal.kt index 3249a1c..39b08a6 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandHeal.kt +++ b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandHeal.kt @@ -1,14 +1,14 @@ package `in`.kyle.mcspring.manager.commands import `in`.kyle.mcspring.command.Command -import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommand +import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandImpl import org.bukkit.entity.Player import org.springframework.stereotype.Component @Component internal class CommandHeal { @Command(value = "heal", description = "Heal yourself or another player", usage = "/heal ?") - fun heal(command: PluginCommand) { + fun heal(command: PluginCommandImpl) { command.requiresPlayerSender { "Sender must be a player" } command.withPlayer { "Player $it not found" } command.then(this::executeHeal) diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandOp.kt b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandOp.kt index 252e867..e69c044 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandOp.kt +++ b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandOp.kt @@ -1,14 +1,14 @@ package `in`.kyle.mcspring.manager.commands import `in`.kyle.mcspring.command.Command -import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommand +import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandImpl import org.bukkit.command.CommandSender import org.springframework.stereotype.Component @Component internal class CommandOp { @Command(value = "op", description = "Toggle yourself or another players OP status", usage = "/op ?") - fun op(command: PluginCommand) { + fun op(command: PluginCommandImpl) { command.withPlayer { "Player $it not found" } command.then(::toggleOp) command.otherwise(::toggleOp) diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandPlugin.kt b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandPlugin.kt index 2e58360..390039a 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandPlugin.kt +++ b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandPlugin.kt @@ -2,7 +2,7 @@ package `in`.kyle.mcspring.manager.commands import `in`.kyle.mcspring.command.Command import `in`.kyle.mcspring.manager.controller.PluginController -import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommand +import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandImpl import org.bukkit.plugin.Plugin import org.springframework.boot.autoconfigure.condition.ConditionalOnBean import org.springframework.stereotype.Component @@ -15,20 +15,20 @@ internal class CommandPlugin( ) { @Command(value = "plugin", aliases = ["pl"], description = "Load/unload/reload a specific plugin", usage = "/plugin ") - fun plugin(command: PluginCommand) { + fun plugin(command: PluginCommandImpl) { command.on("load", ::load) command.on("unload", ::unload) command.on("list", ::executeListPlugins) command.otherwise("Usage: /plugin ") } - private fun load(command: PluginCommand) { + private fun load(command: PluginCommandImpl) { command.withMap(pluginController.loadablePlugins) { "Plugin $it not found or is already loaded" } command.then(::executeLoad) command.otherwise("Usage: /plugin load ") } - private fun unload(command: PluginCommand) { + private fun unload(command: PluginCommandImpl) { val plugins = pluginController.plugins.associateBy({ it.name }, { it }) command.withMap(plugins) { "Plugin $it is not loaded" } command.then(::executeDisable) diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandSpeed.kt b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandSpeed.kt index f48151f..77a1a84 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandSpeed.kt +++ b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandSpeed.kt @@ -1,7 +1,7 @@ package `in`.kyle.mcspring.manager.commands import `in`.kyle.mcspring.command.Command -import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommand +import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandImpl import org.bukkit.entity.Player import org.springframework.stereotype.Component @@ -9,7 +9,7 @@ import org.springframework.stereotype.Component internal class CommandSpeed { @Command(value = "speed", description = "Set your movement and fly speed", usage = "/speed ") - fun speed(command: PluginCommand) { + fun speed(command: PluginCommandImpl) { command.requiresPlayerSender { "Sender must be a player" } command.withDouble("Speed value must be an integer") command.then(::speedExecutor) diff --git a/mcspring-examples/simple-factions/src/main/java/org/example/factions/commands/FactionCommand.java b/mcspring-examples/simple-factions/src/main/java/org/example/factions/commands/FactionCommand.java index e2aec7f..e0f0958 100644 --- a/mcspring-examples/simple-factions/src/main/java/org/example/factions/commands/FactionCommand.java +++ b/mcspring-examples/simple-factions/src/main/java/org/example/factions/commands/FactionCommand.java @@ -10,7 +10,7 @@ import java.util.stream.Collectors; import in.kyle.mcspring.command.Command; -import in.kyle.mcspring.subcommands.plugincommand.PluginCommand; +import in.kyle.mcspring.subcommands.plugincommand.api.PluginCommand; import lombok.RequiredArgsConstructor; @Component @@ -23,7 +23,7 @@ class FactionCommand { void faction(PluginCommand command) { command.on("create", this::create); command.on("delete", this::delete); - command.on("list", this::list); + command.on("list", this::factionList); command.on("join", this::join); command.on("mine", this::factionMine); command.onInvalid(s -> String.format("Invalid sub-command %s", s)); @@ -53,10 +53,6 @@ private void delete(PluginCommand command) { command.otherwise("Usage: /faction delete "); } - private void list(PluginCommand command) { - command.then(this::factionList); - } - private String factionJoin(Player sender, Faction faction) { if (!factions.isFactionMember(sender)) { faction.getMembers().put(sender.getUniqueId(), Faction.Rank.MEMBER); From d4c0a8a7d37b37601bbf3132cc9dd30a30d68cac Mon Sep 17 00:00:00 2001 From: kylepls Date: Sun, 10 May 2020 17:08:00 -0600 Subject: [PATCH 15/54] Continue conversion --- mcspring-api/mcspring-base/pom.xml | 4 - .../java/in/kyle/mcspring/SpringPlugin.java | 83 --------------- .../java/in/kyle/mcspring/SpringPlugin.kt | 68 ++++++++++++ .../in/kyle/mcspring/SpringSpigotSupport.java | 77 -------------- .../in/kyle/mcspring/SpringSpigotSupport.kt | 75 +++++++++++++ .../mcspring/annotation/PluginDepend.java | 13 --- .../kyle/mcspring/annotation/PluginDepend.kt | 5 + .../mcspring/command/SimpleMethodInjection.kt | 10 +- .../mcspring/event/EventHandlerSupport.java | 45 -------- .../mcspring/event/EventHandlerSupport.kt | 30 ++++++ .../in/kyle/mcspring/event/EventService.java | 44 -------- .../in/kyle/mcspring/event/EventService.kt | 34 ++++++ .../scheduler/ScheduledAnnotationSupport.java | 100 ------------------ .../scheduler/ScheduledAnnotationSupport.kt | 56 ++++++++++ .../mcspring/scheduler/SchedulerService.java | 56 ---------- .../mcspring/scheduler/SchedulerService.kt | 52 +++++++++ .../in/kyle/mcspring/util/SpringScanner.java | 43 -------- .../in/kyle/mcspring/util/SpringScanner.kt | 33 ++++++ .../plugincommand/PluginCommandExecutors.kt | 18 ++-- .../plugincommand/PluginCommandWith.kt | 3 +- .../plugincommand/api/PluginCommand.kt | 14 +-- .../PluginCommandExecutorsJavaSupport.kt | 8 +- .../subcommands/TestJavaPluginCommand.java | 21 ++-- .../mcspring/subcommands/TestPluginCommand.kt | 38 ++++--- .../subcommands/tab/TestTabDiscovery.kt | 2 +- .../src/test/resources/application.properties | 2 - mcspring-api/mcspring-test/pom.xml | 14 +-- .../mcspring/test/SpringSpigotSupport.java | 20 +--- .../test/command/TestCommandExecutor.java | 49 --------- .../test/command/TestCommandExecutor.kt | 40 +++++++ .../test/command/TestCommandRegistration.kt | 2 +- .../mcspring-plugin-manager/pom.xml | 1 + .../manager/commands/CommandGamemode.kt | 1 + .../mcspring/manager/commands/CommandHeal.kt | 1 + .../controller/BukkitPluginUnloader.kt | 5 +- .../manager/controller/PluginController.kt | 2 +- .../manager/commands/TestCommandGamemode.kt | 76 +++++++------ .../manager/commands/TestCommandSpeed.kt | 47 ++++---- 38 files changed, 535 insertions(+), 657 deletions(-) delete mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/SpringPlugin.java create mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/SpringPlugin.kt delete mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/SpringSpigotSupport.java create mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/SpringSpigotSupport.kt delete mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/annotation/PluginDepend.java create mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/annotation/PluginDepend.kt delete mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/event/EventHandlerSupport.java create mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/event/EventHandlerSupport.kt delete mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/event/EventService.java create mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/event/EventService.kt delete mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/scheduler/ScheduledAnnotationSupport.java create mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/scheduler/ScheduledAnnotationSupport.kt delete mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/scheduler/SchedulerService.java create mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/scheduler/SchedulerService.kt delete mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/util/SpringScanner.java create mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/util/SpringScanner.kt delete mode 100644 mcspring-api/mcspring-subcommands/src/test/resources/application.properties delete mode 100644 mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/command/TestCommandExecutor.java create mode 100644 mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/command/TestCommandExecutor.kt diff --git a/mcspring-api/mcspring-base/pom.xml b/mcspring-api/mcspring-base/pom.xml index acdb418..0a84510 100644 --- a/mcspring-api/mcspring-base/pom.xml +++ b/mcspring-api/mcspring-base/pom.xml @@ -20,10 +20,6 @@ - - org.projectlombok - lombok - org.spigotmc spigot-api diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/SpringPlugin.java b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/SpringPlugin.java deleted file mode 100644 index 7988d2d..0000000 --- a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/SpringPlugin.java +++ /dev/null @@ -1,83 +0,0 @@ -package in.kyle.mcspring; - -import org.apache.logging.log4j.Level; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.core.Logger; -import org.bukkit.plugin.Plugin; -import org.springframework.boot.Banner; -import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.core.io.DefaultResourceLoader; -import org.springframework.core.io.ResourceLoader; - -import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.Map; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -public class SpringPlugin { - - private static final LinkedHashMap SETUP_PLUGINS = new LinkedHashMap<>(); - - private final Plugin plugin; - @Getter - private ConfigurableApplicationContext context; - - public static void setup(Plugin plugin, Class config) { - setupLogger(); - SpringPlugin springPlugin = new SpringPlugin(plugin); - springPlugin.initSpring(config); - SETUP_PLUGINS.put(plugin, springPlugin); - } - - public static void teardown(Plugin plugin) { - SpringPlugin springPlugin = SETUP_PLUGINS.remove(plugin); - if (springPlugin != null) { - springPlugin.onDisable(plugin); - } - } - - public final void onDisable(Plugin plugin) { - if (context != null) { - context.close(); - context = null; - SETUP_PLUGINS.remove(plugin); - } - } - - private void initSpring(Class config) { - SpringApplicationBuilder builder = new SpringApplicationBuilder(); - ClassLoader classLoader = plugin.getClass().getClassLoader(); - ResourceLoader loader = new DefaultResourceLoader(classLoader); - Class[] sources = new Class[]{config, SpringSpigotSupport.class}; - if (!SETUP_PLUGINS.isEmpty()) { - SpringPlugin parent = findParentCandidate(); - builder.parent(parent.getContext()); - sources = Arrays.copyOfRange(sources, 0, 1); - } - context = builder.sources(sources) - .resourceLoader(loader) - .bannerMode(Banner.Mode.OFF) - .properties("spigot.plugin=" + plugin.getName()) - .logStartupInfo(true) - .run(); - } - - private static void setupLogger() { - if (SETUP_PLUGINS.isEmpty()) { - Logger rootLogger = (Logger) LogManager.getRootLogger(); - rootLogger.setLevel(Level.ALL); - } - } - - private SpringPlugin findParentCandidate() { - return SETUP_PLUGINS.entrySet() - .stream() - .reduce((a, b) -> b) - .map(Map.Entry::getValue) - .orElse(null); - } -} diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/SpringPlugin.kt b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/SpringPlugin.kt new file mode 100644 index 0000000..6bb16f0 --- /dev/null +++ b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/SpringPlugin.kt @@ -0,0 +1,68 @@ +package `in`.kyle.mcspring + +import org.apache.logging.log4j.Level +import org.apache.logging.log4j.LogManager +import org.apache.logging.log4j.core.Logger +import org.bukkit.plugin.Plugin +import org.springframework.boot.Banner +import org.springframework.boot.builder.SpringApplicationBuilder +import org.springframework.context.ConfigurableApplicationContext +import org.springframework.core.io.DefaultResourceLoader +import java.util.* + +class SpringPlugin( + private val plugin: Plugin +) { + + private var context: ConfigurableApplicationContext? = null + + fun onDisable(plugin: Plugin) { + if (context != null) { + context!!.close() + context = null + SETUP_PLUGINS.remove(plugin) + } + } + + private fun initSpring(config: Class<*>) { + val builder = SpringApplicationBuilder() + var sources = arrayOf(config, SpringSpigotSupport::class.java) + + if (SETUP_PLUGINS.isNotEmpty()) { + val parent = SETUP_PLUGINS.entries.reduce { _, b -> b }.value + builder.parent(parent.context) + sources = sources.copyOfRange(0, 1) + } + + context = builder.sources(*sources) + .resourceLoader(DefaultResourceLoader(plugin.javaClass.classLoader)) + .bannerMode(Banner.Mode.OFF) + .properties("spigot.plugin=" + plugin.name) + .logStartupInfo(true) + .run() + } + + companion object { + private val SETUP_PLUGINS = LinkedHashMap() + + @JvmStatic + fun setup(plugin: Plugin, config: Class<*>) { + setupLogger() + val springPlugin = SpringPlugin(plugin) + springPlugin.initSpring(config) + SETUP_PLUGINS[plugin] = springPlugin + } + + @JvmStatic + fun teardown(plugin: Plugin) { + SETUP_PLUGINS.remove(plugin)?.onDisable(plugin) + } + + private fun setupLogger() { + if (SETUP_PLUGINS.isEmpty()) { + val rootLogger = LogManager.getRootLogger() as Logger + rootLogger.level = Level.ALL + } + } + } +} diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/SpringSpigotSupport.java b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/SpringSpigotSupport.java deleted file mode 100644 index 4f8cf8e..0000000 --- a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/SpringSpigotSupport.java +++ /dev/null @@ -1,77 +0,0 @@ -package in.kyle.mcspring; - -import org.bukkit.Bukkit; -import org.bukkit.Server; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.plugin.Plugin; -import org.bukkit.plugin.PluginDescriptionFile; -import org.bukkit.plugin.PluginLoader; -import org.bukkit.plugin.PluginManager; -import org.bukkit.plugin.messaging.Messenger; -import org.bukkit.scheduler.BukkitScheduler; -import org.bukkit.scoreboard.ScoreboardManager; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.EnableAspectJAutoProxy; -import org.springframework.scheduling.annotation.EnableScheduling; - -import java.util.logging.Logger; - -@Configuration -@ComponentScan(basePackageClasses = SpringPlugin.class) -@EnableScheduling -@EnableAspectJAutoProxy -class SpringSpigotSupport { - - @Bean - Plugin plugin(@Value("${spigot.plugin}") String pluginName) { - return Bukkit.getPluginManager().getPlugin(pluginName); - } - - @Bean(destroyMethod = "") - Server server(Plugin plugin) { - return plugin.getServer(); - } - - @Bean - Logger logger(Plugin plugin) { - return plugin.getLogger(); - } - - @Bean - PluginManager pluginManager(Server server) { - return server.getPluginManager(); - } - - @Bean - ScoreboardManager scoreboardManager(Server server) { - return server.getScoreboardManager(); - } - - @Bean - Messenger messenger(Server server) { - return server.getMessenger(); - } - - @Bean - FileConfiguration configuration(Plugin plugin) { - return plugin.getConfig(); - } - - @Bean - PluginDescriptionFile description(Plugin plugin) { - return plugin.getDescription(); - } - - @Bean - BukkitScheduler scheduler(Server server) { - return server.getScheduler(); - } - - @Bean - PluginLoader pluginLoader(Plugin plugin) { - return plugin.getPluginLoader(); - } -} diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/SpringSpigotSupport.kt b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/SpringSpigotSupport.kt new file mode 100644 index 0000000..5dc84f7 --- /dev/null +++ b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/SpringSpigotSupport.kt @@ -0,0 +1,75 @@ +package `in`.kyle.mcspring + +import org.bukkit.Bukkit +import org.bukkit.Server +import org.bukkit.configuration.file.FileConfiguration +import org.bukkit.plugin.Plugin +import org.bukkit.plugin.PluginDescriptionFile +import org.bukkit.plugin.PluginLoader +import org.bukkit.plugin.PluginManager +import org.bukkit.plugin.messaging.Messenger +import org.bukkit.scheduler.BukkitScheduler +import org.bukkit.scoreboard.ScoreboardManager +import org.springframework.beans.factory.annotation.Value +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.ComponentScan +import org.springframework.context.annotation.Configuration +import org.springframework.context.annotation.EnableAspectJAutoProxy +import org.springframework.scheduling.annotation.EnableScheduling +import java.util.logging.Logger + +@Configuration +@ComponentScan(basePackageClasses = [SpringPlugin::class]) +@EnableScheduling +@EnableAspectJAutoProxy +internal open class SpringSpigotSupport { + @Bean + open fun plugin(@Value("\${spigot.plugin}") pluginName: String): Plugin { + return Bukkit.getPluginManager().getPlugin(pluginName)!! + } + + @Bean(destroyMethod = "") + open fun server(plugin: Plugin): Server { + return plugin.server + } + + @Bean + open fun logger(plugin: Plugin): Logger { + return plugin.logger + } + + @Bean + open fun pluginManager(server: Server): PluginManager { + return server.pluginManager + } + + @Bean + open fun scoreboardManager(server: Server): ScoreboardManager? { + return server.scoreboardManager + } + + @Bean + open fun messenger(server: Server): Messenger { + return server.messenger + } + + @Bean + open fun configuration(plugin: Plugin): FileConfiguration { + return plugin.config + } + + @Bean + open fun description(plugin: Plugin): PluginDescriptionFile { + return plugin.description + } + + @Bean + open fun scheduler(server: Server): BukkitScheduler { + return server.scheduler + } + + @Bean + open fun pluginLoader(plugin: Plugin): PluginLoader { + return plugin.pluginLoader + } +} diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/annotation/PluginDepend.java b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/annotation/PluginDepend.java deleted file mode 100644 index ee2cb46..0000000 --- a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/annotation/PluginDepend.java +++ /dev/null @@ -1,13 +0,0 @@ -package in.kyle.mcspring.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -public @interface PluginDepend { - String[] plugins(); - boolean soft() default false; -} diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/annotation/PluginDepend.kt b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/annotation/PluginDepend.kt new file mode 100644 index 0000000..7cd94c7 --- /dev/null +++ b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/annotation/PluginDepend.kt @@ -0,0 +1,5 @@ +package `in`.kyle.mcspring.annotation + +@kotlin.annotation.Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) +annotation class PluginDepend(val plugins: Array, val soft: Boolean = false) diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleMethodInjection.kt b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleMethodInjection.kt index cb7432e..7a00a1b 100644 --- a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleMethodInjection.kt +++ b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleMethodInjection.kt @@ -3,6 +3,7 @@ package `in`.kyle.mcspring.command import org.springframework.context.annotation.Lazy import org.springframework.stereotype.Component import kotlin.reflect.KFunction +import kotlin.reflect.full.isSubclassOf import kotlin.reflect.jvm.isAccessible import kotlin.reflect.jvm.javaType @@ -23,17 +24,12 @@ class SimpleMethodInjection( private fun makeResolverFor(contextObject: Any): ParameterResolver { return object : ParameterResolver { override fun resolve(parameter: Class<*>): Any? { - return contextObject.takeIf { parameter.isAssignableFrom(contextObject.javaClass) } + return contextObject.takeIf { contextObject::class.isSubclassOf(parameter.kotlin) } } } } - fun run(function: KFunction, contextObjects: List): Any { - val types = function.parameters.map { it.type.javaType as Class<*> } - return run(function, types, contextObjects) - } - - fun run(function: KFunction, types: List>, contextObjects: List): Any { + fun callWithInjection(function: KFunction, types: List>, contextObjects: List): Any { val contextResolvers = makeResolvers(contextObjects) val parameters = getParameters(types, contextResolvers) try { diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/event/EventHandlerSupport.java b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/event/EventHandlerSupport.java deleted file mode 100644 index 4213e56..0000000 --- a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/event/EventHandlerSupport.java +++ /dev/null @@ -1,45 +0,0 @@ -package in.kyle.mcspring.event; - -import org.bukkit.event.Event; -import org.bukkit.event.EventHandler; -import org.bukkit.plugin.EventExecutor; -import org.bukkit.plugin.Plugin; -import org.springframework.aop.support.AopUtils; -import org.springframework.beans.BeansException; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.stereotype.Component; - -import java.lang.reflect.Method; - -import in.kyle.mcspring.util.SpringScanner; -import lombok.AllArgsConstructor; -import lombok.SneakyThrows; - -@Component -@AllArgsConstructor -@ConditionalOnBean(Plugin.class) -class EventHandlerSupport implements ApplicationContextAware { - - private final EventService eventService; - private final SpringScanner scanner; - - @Override - public void setApplicationContext(ApplicationContext ctx) throws BeansException { - scanner.scanMethods(EventHandler.class).forEach(this::register); - } - - private void register(Method method, Object container) { - eventService.registerEvent(method, createEventExecutor(container, method)); - } - - private EventExecutor createEventExecutor(Object listenerBean, Method method) { - return (listener, event) -> triggerEvent(method, listenerBean, event); - } - - @SneakyThrows - private void triggerEvent(Method method, Object listenerBean, Event event) { - AopUtils.invokeJoinpointUsingReflection(listenerBean, method, new Object[]{event}); - } -} diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/event/EventHandlerSupport.kt b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/event/EventHandlerSupport.kt new file mode 100644 index 0000000..8992282 --- /dev/null +++ b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/event/EventHandlerSupport.kt @@ -0,0 +1,30 @@ +package `in`.kyle.mcspring.event + +import `in`.kyle.mcspring.util.SpringScanner +import org.bukkit.event.Event +import org.bukkit.event.EventHandler +import org.bukkit.event.Listener +import org.bukkit.plugin.EventExecutor +import org.bukkit.plugin.Plugin +import org.springframework.aop.support.AopUtils +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean +import org.springframework.context.ApplicationContext +import org.springframework.context.ApplicationContextAware +import org.springframework.stereotype.Component + +@Component +@ConditionalOnBean(Plugin::class) +internal class EventHandlerSupport( + private val eventService: EventService, + private val scanner: SpringScanner +) : ApplicationContextAware { + + override fun setApplicationContext(ctx: ApplicationContext) { + scanner.scanMethods(EventHandler::class.java).forEach { + val executor = EventExecutor { _: Listener, event: Event -> + AopUtils.invokeJoinpointUsingReflection(it.value, it.key, arrayOf(event)) + } + eventService.registerEvent(it.key, executor) + } + } +} diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/event/EventService.java b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/event/EventService.java deleted file mode 100644 index 6b26004..0000000 --- a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/event/EventService.java +++ /dev/null @@ -1,44 +0,0 @@ -package in.kyle.mcspring.event; - -import org.bukkit.Server; -import org.bukkit.event.Event; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.plugin.EventExecutor; -import org.bukkit.plugin.Plugin; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Service; - -import java.lang.reflect.Method; - -import lombok.AllArgsConstructor; -import lombok.val; - -@Lazy -@Service -@AllArgsConstructor -@ConditionalOnBean(Plugin.class) -class EventService { - - private final Server server; - private final Plugin plugin; - - void registerEvent(Method method, EventExecutor executor) { - val handler = method.getAnnotation(EventHandler.class); - val eventType = (Class) method.getParameters()[0].getType(); - - server.getPluginManager() - .registerEvent(eventType, - makeListener(), - handler.priority(), - executor, - plugin, - handler.ignoreCancelled()); - } - - private Listener makeListener() { - return new Listener() { - }; - } -} diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/event/EventService.kt b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/event/EventService.kt new file mode 100644 index 0000000..40cd2ec --- /dev/null +++ b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/event/EventService.kt @@ -0,0 +1,34 @@ +package `in`.kyle.mcspring.event + +import org.bukkit.Server +import org.bukkit.event.Event +import org.bukkit.event.EventHandler +import org.bukkit.event.Listener +import org.bukkit.plugin.EventExecutor +import org.bukkit.plugin.Plugin +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean +import org.springframework.context.annotation.Lazy +import org.springframework.stereotype.Service +import java.lang.reflect.Method + +@Lazy +@Service +@ConditionalOnBean(Plugin::class) +internal class EventService( + private val server: Server, + private val plugin: Plugin +) { + fun registerEvent(method: Method, executor: EventExecutor) { + val handler = method.getAnnotation(EventHandler::class.java) + + @Suppress("UNCHECKED_CAST") + val eventType = method.parameters[0].type as Class + server.pluginManager + .registerEvent(eventType, + object : Listener {}, + handler.priority, + executor, + plugin, + handler.ignoreCancelled) + } +} diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/scheduler/ScheduledAnnotationSupport.java b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/scheduler/ScheduledAnnotationSupport.java deleted file mode 100644 index d224a2a..0000000 --- a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/scheduler/ScheduledAnnotationSupport.java +++ /dev/null @@ -1,100 +0,0 @@ -package in.kyle.mcspring.scheduler; - -import org.bukkit.plugin.Plugin; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.context.annotation.Lazy; -import org.springframework.scheduling.Trigger; -import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; -import org.springframework.stereotype.Component; -import org.springframework.util.concurrent.ListenableFuture; - -import java.util.Date; -import java.util.concurrent.Callable; -import java.util.concurrent.Future; -import java.util.concurrent.ScheduledFuture; - -import lombok.AllArgsConstructor; -import lombok.EqualsAndHashCode; - -@Lazy -@Component -@AllArgsConstructor -@ConditionalOnBean(Plugin.class) -class ScheduledAnnotationSupport extends ThreadPoolTaskScheduler { - - private final SchedulerService scheduler; - - @Override - public ScheduledFuture schedule(Runnable task, Trigger trigger) { - return super.schedule(wrapSync(task), trigger); - } - - @Override - public ScheduledFuture schedule(Runnable task, Date startTime) { - return super.schedule(wrapSync(task), startTime); - } - - @Override - public ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period) { - return super.scheduleAtFixedRate(wrapSync(task), startTime, period); - } - - @Override - public ScheduledFuture scheduleAtFixedRate(Runnable task, long period) { - return super.scheduleAtFixedRate(wrapSync(task), period); - } - - @Override - public ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay) { - return super.scheduleWithFixedDelay(wrapSync(task), startTime, delay); - } - - @Override - public ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay) { - return super.scheduleWithFixedDelay(wrapSync(task), delay); - } - - @Override - public void execute(Runnable task) { - super.execute(task); - } - - @Override - public Future submit(Runnable task) { - return super.submit(task); - } - - @Override - public Future submit(Callable task) { - return super.submit(task); - } - - @Override - public ListenableFuture submitListenable(Runnable task) { - return super.submitListenable(task); - } - - @Override - public ListenableFuture submitListenable(Callable task) { - return super.submitListenable(task); - } - - private Runnable wrapSync(Runnable task) { - return new WrappedRunnable(scheduler, task); - } - - @AllArgsConstructor - @EqualsAndHashCode(onlyExplicitlyIncluded = true) - static class WrappedRunnable implements Runnable { - - private final SchedulerService scheduler; - - @EqualsAndHashCode.Include - private final Runnable runnable; - - @Override - public void run() { - scheduler.syncTask(runnable); - } - } -} diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/scheduler/ScheduledAnnotationSupport.kt b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/scheduler/ScheduledAnnotationSupport.kt new file mode 100644 index 0000000..782c77a --- /dev/null +++ b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/scheduler/ScheduledAnnotationSupport.kt @@ -0,0 +1,56 @@ +package `in`.kyle.mcspring.scheduler + +import org.bukkit.plugin.Plugin +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean +import org.springframework.context.annotation.Lazy +import org.springframework.scheduling.Trigger +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler +import org.springframework.stereotype.Component +import org.springframework.util.concurrent.ListenableFuture +import java.util.* +import java.util.concurrent.Callable +import java.util.concurrent.Future +import java.util.concurrent.ScheduledFuture + +@Lazy +@Component +@ConditionalOnBean(Plugin::class) +internal class ScheduledAnnotationSupport( + private val scheduler: SchedulerService +) : ThreadPoolTaskScheduler() { + + override fun schedule(task: Runnable, trigger: Trigger): ScheduledFuture<*> { + @Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS") + return super.schedule(wrapSync(task), trigger) + } + + override fun schedule(task: Runnable, startTime: Date): ScheduledFuture<*> { + return super.schedule(wrapSync(task), startTime) + } + + override fun scheduleAtFixedRate(task: Runnable, startTime: Date, period: Long): ScheduledFuture<*> { + return super.scheduleAtFixedRate(wrapSync(task), startTime, period) + } + + override fun scheduleAtFixedRate(task: Runnable, period: Long): ScheduledFuture<*> { + return super.scheduleAtFixedRate(wrapSync(task), period) + } + + override fun scheduleWithFixedDelay(task: Runnable, startTime: Date, delay: Long): ScheduledFuture<*> { + return super.scheduleWithFixedDelay(wrapSync(task), startTime, delay) + } + + override fun scheduleWithFixedDelay(task: Runnable, delay: Long): ScheduledFuture<*> { + return super.scheduleWithFixedDelay(wrapSync(task), delay) + } + + private fun wrapSync(task: Runnable): Runnable { + return WrappedRunnable(scheduler, task) + } + + data class WrappedRunnable(val scheduler: SchedulerService, val runnable: Runnable) : Runnable { + override fun run() { + scheduler.syncTask(runnable) + } + } +} diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/scheduler/SchedulerService.java b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/scheduler/SchedulerService.java deleted file mode 100644 index 41b38ec..0000000 --- a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/scheduler/SchedulerService.java +++ /dev/null @@ -1,56 +0,0 @@ -package in.kyle.mcspring.scheduler; - -import org.bukkit.plugin.Plugin; -import org.bukkit.scheduler.BukkitScheduler; -import org.bukkit.scheduler.BukkitTask; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Service; - -import lombok.RequiredArgsConstructor; - -@Lazy -@Service -@RequiredArgsConstructor -@ConditionalOnBean(Plugin.class) -public class SchedulerService { - - private final BukkitScheduler scheduler; - private final Plugin plugin; - - public BukkitTask asyncTask(Runnable task) { - return scheduler.runTaskAsynchronously(plugin, task); - } - - public BukkitTask syncTask(Runnable task) { - return scheduler.runTask(plugin, task); - } - - public BukkitTask asyncDelayedTask(Runnable task, long delayTicks) { - return scheduler.runTaskLaterAsynchronously(plugin, task, delayTicks); - } - - public BukkitTask syncDelayedTask(Runnable task, long delayTicks) { - return scheduler.runTaskLater(plugin, task, delayTicks); - } - - public BukkitTask asyncRepeatingTask(Runnable task, long delayTicks, long periodTicks) { - return scheduler.runTaskTimerAsynchronously(plugin, task, delayTicks, periodTicks); - } - - public BukkitTask syncRepeatingTask(Runnable task, long delayTicks, long periodTicks) { - return scheduler.runTaskTimer(plugin, task, delayTicks, periodTicks); - } - - public void cancelTask(BukkitTask task) { - scheduler.cancelTask(task.getTaskId()); - } - - public boolean isCurrentlyRunning(BukkitTask task) { - return scheduler.isCurrentlyRunning(task.getTaskId()); - } - - public boolean isQueued(BukkitTask task) { - return scheduler.isQueued(task.getTaskId()); - } -} diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/scheduler/SchedulerService.kt b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/scheduler/SchedulerService.kt new file mode 100644 index 0000000..0aa083b --- /dev/null +++ b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/scheduler/SchedulerService.kt @@ -0,0 +1,52 @@ +package `in`.kyle.mcspring.scheduler + +import org.bukkit.plugin.Plugin +import org.bukkit.scheduler.BukkitScheduler +import org.bukkit.scheduler.BukkitTask +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean +import org.springframework.context.annotation.Lazy +import org.springframework.stereotype.Service + +@Lazy +@Service +@ConditionalOnBean(Plugin::class) +class SchedulerService( + private val scheduler: BukkitScheduler, + private val plugin: Plugin +) { + fun asyncTask(task: Runnable): BukkitTask { + return scheduler.runTaskAsynchronously(plugin, task) + } + + fun syncTask(task: Runnable): BukkitTask { + return scheduler.runTask(plugin, task) + } + + fun asyncDelayedTask(task: Runnable, delayTicks: Long): BukkitTask { + return scheduler.runTaskLaterAsynchronously(plugin, task, delayTicks) + } + + fun syncDelayedTask(task: Runnable, delayTicks: Long): BukkitTask { + return scheduler.runTaskLater(plugin, task, delayTicks) + } + + fun asyncRepeatingTask(task: Runnable, delayTicks: Long, periodTicks: Long): BukkitTask { + return scheduler.runTaskTimerAsynchronously(plugin, task, delayTicks, periodTicks) + } + + fun syncRepeatingTask(task: Runnable, delayTicks: Long, periodTicks: Long): BukkitTask { + return scheduler.runTaskTimer(plugin, task, delayTicks, periodTicks) + } + + fun cancelTask(task: BukkitTask) { + scheduler.cancelTask(task.taskId) + } + + fun isCurrentlyRunning(task: BukkitTask): Boolean { + return scheduler.isCurrentlyRunning(task.taskId) + } + + fun isQueued(task: BukkitTask): Boolean { + return scheduler.isQueued(task.taskId) + } +} diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/util/SpringScanner.java b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/util/SpringScanner.java deleted file mode 100644 index a9512ad..0000000 --- a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/util/SpringScanner.java +++ /dev/null @@ -1,43 +0,0 @@ -package in.kyle.mcspring.util; - -import org.springframework.aop.support.AopUtils; -import org.springframework.context.ApplicationContext; -import org.springframework.stereotype.Component; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.util.HashMap; -import java.util.Map; - -import lombok.AllArgsConstructor; - -@Component -@AllArgsConstructor -public class SpringScanner { - - private final ApplicationContext context; - - public Map scanMethods(Class annotation) { - HashMap methods = new HashMap<>(); - for (String beanName : context.getBeanDefinitionNames()) { - Object object = context.getBean(beanName); - - Class clazz = getRealClass(object); - - for (Method method : clazz.getDeclaredMethods()) { - if (method.isAnnotationPresent(annotation)) { - methods.put(method, object); - } - } - } - return methods; - } - - private Class getRealClass(Object object) { - Class clazz = object.getClass(); - if (AopUtils.isAopProxy(clazz)) { - clazz = AopUtils.getTargetClass(object); - } - return clazz; - } -} diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/util/SpringScanner.kt b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/util/SpringScanner.kt new file mode 100644 index 0000000..75bea9b --- /dev/null +++ b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/util/SpringScanner.kt @@ -0,0 +1,33 @@ +package `in`.kyle.mcspring.util + +import org.springframework.aop.support.AopUtils +import org.springframework.context.ApplicationContext +import org.springframework.stereotype.Component +import java.lang.reflect.Method + +@Component +class SpringScanner( + private val context: ApplicationContext +) { + fun scanMethods(annotation: Class): Map { + val methods = mutableMapOf() + + for (beanName in context.beanDefinitionNames) { + val obj = context.getBean(beanName) + for (method in getRealClass(obj).declaredMethods) { + if (method.isAnnotationPresent(annotation)) { + methods[method] = obj + } + } + } + return methods + } + + private fun getRealClass(obj: Any): Class<*> { + return if (AopUtils.isAopProxy(obj.javaClass)) { + AopUtils.getTargetClass(obj) + } else { + obj.javaClass + } + } +} diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandExecutors.kt b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandExecutors.kt index e0a99dc..041f9a1 100644 --- a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandExecutors.kt +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandExecutors.kt @@ -6,6 +6,7 @@ import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand import kotlin.reflect.KFunction import kotlin.reflect.full.createType import kotlin.reflect.full.isSubtypeOf +import kotlin.reflect.jvm.javaType interface PluginCommandExecutors : PluginCommandBase, PluginCommand { @@ -13,7 +14,7 @@ interface PluginCommandExecutors : PluginCommandBase, PluginCommand { override fun otherwise(e: KFunction) { dirtiesState(requiredStates = arrayOf(State.CLEAN, State.MISSING_ARG)) { - execute { runWithContext(e) } + execute { runWithKotlinContext(e) } } } @@ -25,9 +26,9 @@ interface PluginCommandExecutors : PluginCommandBase, PluginCommand { it.type.isSubtypeOf(PluginCommand::class.createType()) } if (receivesPluginCommand) { - runWithContext(e, sendOutput = false) + runWithKotlinContext(e, sendOutput = false) } else { - execute { runWithContext(e) } + execute { runWithKotlinContext(e) } } } } @@ -41,17 +42,22 @@ interface PluginCommandExecutors : PluginCommandBase, PluginCommand { } } - override fun then(e: KFunction) = dirtiesState { execute { runWithContext(e) } } + override fun then(e: KFunction) = dirtiesState { execute { runWithKotlinContext(e) } } - private fun runWithContext(e: KFunction, sendOutput: Boolean = true) { + fun runWithContext(e: KFunction, types: List>, sendOutput: Boolean = true) { val nextExecutor = makeNextExecutor() child = nextExecutor - val out = injection.run(e, injections.plus(nextExecutor)) + val out = injection.callWithInjection(e, types, injections.plus(nextExecutor).plus(sender)) if (out !is Unit && sendOutput) { sendMessage(out.toString()) } } + private fun runWithKotlinContext(e: KFunction, sendOutput: Boolean = true) { + val types = e.parameters.map { it.type.javaType as Class<*> } + return runWithContext(e, types, sendOutput) + } + private fun makeNextExecutor(): PluginCommandImpl? { return if (parts.isNotEmpty()) { PluginCommandImpl(injection, sender, parts, runExecutors).apply { diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandWith.kt b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandWith.kt index 8460841..400f50a 100644 --- a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandWith.kt +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandWith.kt @@ -12,7 +12,7 @@ interface PluginCommandWith : PluginCommandBase, PluginCommand { override fun withSentence() { addCompletionStage("sentence", "with") if (nextPart() != null) { - injections.add(parts.joinToString()) + injections.add(parts.joinToString(" ")) parts.clear() } } @@ -22,6 +22,7 @@ interface PluginCommandWith : PluginCommandBase, PluginCommand { override fun withDouble(errorMessage: Err1) = with({ it.toDoubleOrNull() }, errorMessage, "double") + @Suppress("DEPRECATION") override fun withOfflinePlayer(errorMessage: Err1) { with({ Bukkit.getOfflinePlayer(it) }, errorMessage, "offline player") } diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/api/PluginCommand.kt b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/api/PluginCommand.kt index 270d60a..f0a4df0 100644 --- a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/api/PluginCommand.kt +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/api/PluginCommand.kt @@ -7,6 +7,8 @@ import kotlin.reflect.KFunction typealias Err = () -> String typealias Err1 = (String) -> String +const val defaultStageName = "UNKNOWN" + interface PluginCommand { val sender: CommandSender @@ -109,10 +111,10 @@ interface PluginCommand { fun requiresOp(errorMessage: Err) - fun with(processor: (String) -> Any?, errorMessage: Err1 = { "" }, stageName: String = "UNKNOWN") + fun with(processor: (String) -> Any?, errorMessage: Err1 = { "" }, stageName: String = defaultStageName) fun with(processor: (String) -> Any?, errorMessage: Err1 = { "" }) - = with(processor, errorMessage, "UNKNOWN") + = with(processor, errorMessage, defaultStageName) fun withString() @@ -140,17 +142,17 @@ interface PluginCommand { fun withXYZInt(errorMessage: String) - fun withMap(options: Map, errorMessage: Err1, stageName: String = "UNKNOWN") + fun withMap(options: Map, errorMessage: Err1, stageName: String = defaultStageName) fun withMap(options: Map, errorMessage: Err1) - = withMap(options, errorMessage, "UNKNOWN") + = withMap(options, errorMessage, defaultStageName) fun withMap(options: Map, errorMessage: String) - fun withAny(options: Collection, errorMessage: Err1, stageName: String = "UNKNOWN") + fun withAny(options: Collection, errorMessage: Err1, stageName: String = defaultStageName) fun withAny(options: Collection, errorMessage: Err1) - = withAny(options, errorMessage, "UNKNOWN") + = withAny(options, errorMessage, defaultStageName) fun withAny(options: Collection, errorMessage: String) diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/javacompat/PluginCommandExecutorsJavaSupport.kt b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/javacompat/PluginCommandExecutorsJavaSupport.kt index 782311f..f5242d7 100644 --- a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/javacompat/PluginCommandExecutorsJavaSupport.kt +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/javacompat/PluginCommandExecutorsJavaSupport.kt @@ -2,6 +2,7 @@ package `in`.kyle.mcspring.subcommands.plugincommand.javacompat import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandBase.State import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandExecutors +import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand import `in`.kyle.mcspring.subcommands.plugincommand.javacompat.HighIQExecutors.* import java.lang.invoke.SerializedLambda import java.lang.reflect.Method @@ -94,8 +95,11 @@ interface PluginCommandExecutorsJavaSupport : PluginCommandExecutors { private fun then(e: HighIQExecutors, function: KFunction) { dirtiesState { - val types: List> = getMethod(e).parameterTypes.toList() - injection.run(function, types) + execute { + val types: List> = getMethod(e).parameterTypes.toList() + val sendOutput = types.any { PluginCommand::class.java.isAssignableFrom(it) } + super.runWithContext(function, types, sendOutput) + } } } diff --git a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestJavaPluginCommand.java b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestJavaPluginCommand.java index d9366d8..25cb77f 100644 --- a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestJavaPluginCommand.java +++ b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestJavaPluginCommand.java @@ -3,22 +3,20 @@ import org.bukkit.entity.Player; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.List; +import in.kyle.mcspring.command.SimpleMethodInjection; +import in.kyle.mcspring.subcommands.plugincommand.PluginCommandImpl; import in.kyle.mcspring.subcommands.plugincommand.api.PluginCommand; -import in.kyle.mcspring.test.MCSpringTest; import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; -@MCSpringTest -public class TestJavaPluginCommand { - - @Autowired - TestConsole console; +class TestJavaPluginCommand { Player sender; List messages; @@ -47,7 +45,12 @@ String parse2(PluginCommand command, String parsedPart) { } } - console.run("test-arg another-arg", new Test()::parse, true, mock(Player.class)); - assertThat(messages).containsExactly("part is test-arg true"); + PluginCommandImpl pluginCommand = + new PluginCommandImpl(new SimpleMethodInjection(Collections.emptyList()), + sender, + Arrays.asList("test-arg", "another-arg"), + true); + new Test().parse(pluginCommand); + assertThat(messages).containsExactly("part is test-arg"); } } diff --git a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestPluginCommand.kt b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestPluginCommand.kt index 5c5f5c5..ee606e2 100644 --- a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestPluginCommand.kt +++ b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestPluginCommand.kt @@ -5,22 +5,11 @@ import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand import org.assertj.core.api.Assertions.assertThat import org.bukkit.command.CommandSender import org.bukkit.entity.Player -import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.mockito.Mockito.* internal class TestPluginCommand { - lateinit var sender: Player - lateinit var outputMessages: MutableList - - @BeforeEach - fun setup() { - outputMessages = mutableListOf() - sender = mock(Player::class.java) - doAnswer { outputMessages.add(it.getArgument(0)) }.`when`(sender).sendMessage(anyString()) - } - @Test fun testDirectExecutor() { class Test { @@ -34,7 +23,8 @@ internal class TestPluginCommand { fun handler2(string: String) = "handler2: $string" } - run("subcommand1", Test()::exec1) + val (sender, outputMessages) = makePlayer() + run("subcommand1", Test()::exec1, true, sender) assertThat(outputMessages).containsExactly("handler1") outputMessages.clear() @@ -47,9 +37,11 @@ internal class TestPluginCommand { class Test { fun root(command: PluginCommand) = command.then(this::exec) + @Suppress("UNUSED_PARAMETER") fun exec(sender: CommandSender) = "Hello World" } + val (sender, outputMessages) = makePlayer() run("", Test()::root, sender = sender) assertThat(outputMessages).containsExactly("Hello World") } @@ -85,6 +77,7 @@ internal class TestPluginCommand { fun exec(sentence: String) = sentence } + val (sender, outputMessages) = makePlayer() run("Hello to you world", Test()::root, sender = sender) assertThat(outputMessages).containsExactly("Hello to you world") } @@ -105,6 +98,7 @@ internal class TestPluginCommand { } } + val (sender, outputMessages) = makePlayer() run("1 2 3", Test()::root, sender = sender) assertThat(outputMessages).containsExactly("true") } @@ -114,18 +108,21 @@ internal class TestPluginCommand { class Test { fun root(command: PluginCommand) = command.on("a", this::a) - fun a(command: PluginCommand) { + private fun a(command: PluginCommand) { command.on("b", this::b) command.on("c", this::c) } private fun b(command: PluginCommand) = command.then(this::exec) - private fun c(command: PluginCommand) {} + @Suppress("UNUSED_PARAMETER") + private fun c(command: PluginCommand) { + } - fun exec(sender: CommandSender) = "Works" + private fun exec() = "Works" } + val (sender, outputMessages) = makePlayer() run("a b", Test()::root, sender = sender) assertThat(outputMessages).containsExactly("Works") outputMessages.clear() @@ -143,11 +140,12 @@ internal class TestPluginCommand { } fun a(command: PluginCommand) { - command.withInt{ "error" } + command.withInt { "error" } command.otherwise("should run if int passed or missing arg") } } + val (sender, outputMessages) = makePlayer() run("", Test()::root, sender = sender) assertThat(outputMessages).containsExactly("no subcommand at root") outputMessages.clear() @@ -165,7 +163,15 @@ internal class TestPluginCommand { fun root(command: PluginCommand) = command.withInt { "$it is not an int" } } + val (sender, outputMessages) = makePlayer() run("swag", Test()::root, sender = sender) assertThat(outputMessages).containsExactly("swag is not an int") } + + private fun makePlayer(): Pair> { + val sender = mock(Player::class.java) + val messages = mutableListOf() + doAnswer { messages.add(it.getArgument(0)) }.`when`(sender).sendMessage(anyString()) + return Pair(sender, messages) + } } diff --git a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/tab/TestTabDiscovery.kt b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/tab/TestTabDiscovery.kt index dd77f89..3f14649 100644 --- a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/tab/TestTabDiscovery.kt +++ b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/tab/TestTabDiscovery.kt @@ -10,7 +10,7 @@ import org.bukkit.entity.Player import org.junit.jupiter.api.Test import org.mockito.Mockito.mock -class TestTabDiscovery { +internal class TestTabDiscovery { private val tabDiscovery = TabDiscovery(SimpleMethodInjection(emptyList())) diff --git a/mcspring-api/mcspring-subcommands/src/test/resources/application.properties b/mcspring-api/mcspring-subcommands/src/test/resources/application.properties deleted file mode 100644 index 1494a77..0000000 --- a/mcspring-api/mcspring-subcommands/src/test/resources/application.properties +++ /dev/null @@ -1,2 +0,0 @@ -main=test -debug=true diff --git a/mcspring-api/mcspring-test/pom.xml b/mcspring-api/mcspring-test/pom.xml index 7f1245d..ba079af 100644 --- a/mcspring-api/mcspring-test/pom.xml +++ b/mcspring-api/mcspring-test/pom.xml @@ -19,6 +19,11 @@ + + org.projectlombok + lombok + 1.18.12 + org.spigotmc spigot-api @@ -32,15 +37,6 @@ org.springframework.boot spring-boot-test - - org.projectlombok - lombok - - - com.github.kylepls - BukkitTest - 0.0.1 - org.assertj assertj-core diff --git a/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/SpringSpigotSupport.java b/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/SpringSpigotSupport.java index 81d8ea0..e3ba79d 100644 --- a/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/SpringSpigotSupport.java +++ b/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/SpringSpigotSupport.java @@ -1,5 +1,6 @@ package in.kyle.mcspring.test; +import org.bukkit.entity.Player; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.info.BuildProperties; @@ -10,32 +11,21 @@ import java.util.Properties; -import in.kyle.api.bukkit.TestServer; -import in.kyle.api.bukkit.entity.TestPlayer; -import in.kyle.api.generate.api.Generator; import in.kyle.mcspring.SpringPlugin; import in.kyle.mcspring.command.SimpleMethodInjection; +import static org.mockito.Mockito.*; + @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(basePackageClasses = {SpringSpigotSupport.class, SpringPlugin.class}) @Import(SimpleMethodInjection.class) class SpringSpigotSupport { - @Bean - Generator generator() { - return Generator.create(); - } - @Bean @Scope("prototype") - TestPlayer player(Generator generator) { - return generator.create(TestPlayer.class); - } - - @Bean - TestServer server(Generator generator) { - return generator.create(TestServer.class); + Player player() { + return mock(Player.class); } @Bean diff --git a/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/command/TestCommandExecutor.java b/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/command/TestCommandExecutor.java deleted file mode 100644 index a2f4721..0000000 --- a/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/command/TestCommandExecutor.java +++ /dev/null @@ -1,49 +0,0 @@ -package in.kyle.mcspring.test.command; - -import org.springframework.stereotype.Component; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; - -import in.kyle.api.bukkit.TestCommandSender; -import in.kyle.api.bukkit.entity.TestPlayer; -import in.kyle.api.generate.api.Generator; -import lombok.RequiredArgsConstructor; - -@Component -@RequiredArgsConstructor -public class TestCommandExecutor { - - private final TestCommandRegistration registration; - - public List run(String command) { - TestPlayer player = Generator.create().create(TestPlayer.class); - return run(player, command); - } - - public List run(TestCommandSender sender, String command) { - command = command.trim(); - List parts = new ArrayList<>(Arrays.asList(command.split(" "))); - if (parts.get(0).isEmpty()) { - parts.remove(0); - } - - if (parts.size() != 0) { - String label = parts.get(0); - String[] args = parts.subList(1, parts.size()).toArray(new String[0]); - - List output = new ArrayList<>(); - sender.getMessages().subscribe(output::add); - - registration.run(label, sender, label, args); - - return output.stream() - .flatMap(s -> Arrays.stream(s.split("\n"))) - .collect(Collectors.toList()); - } else { - throw new RuntimeException("Empty command"); - } - } -} diff --git a/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/command/TestCommandExecutor.kt b/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/command/TestCommandExecutor.kt new file mode 100644 index 0000000..68e0c78 --- /dev/null +++ b/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/command/TestCommandExecutor.kt @@ -0,0 +1,40 @@ +package `in`.kyle.mcspring.test.command + +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player +import org.mockito.ArgumentMatchers +import org.mockito.Mockito +import org.mockito.invocation.InvocationOnMock +import org.springframework.stereotype.Component +import java.util.* + +@Component +class TestCommandExecutor( + val registration: TestCommandRegistration +) { + fun run(command: String): List { + val sender = Mockito.mock(Player::class.java) + val messages = ArrayList() + Mockito.doAnswer { invocationOnMock: InvocationOnMock -> + messages.add(invocationOnMock.getArgument(0)) + null + }.`when`(sender).sendMessage(ArgumentMatchers.anyString()) + run(sender, command) + return messages + } + + fun run(sender: CommandSender?, command: String) { + val command = command.trim { it <= ' ' } + val parts = command.split(" ").toMutableList() + if (parts[0].isEmpty()) { + parts.removeAt(0) + } + if (parts.size != 0) { + val label = parts[0] + val args = parts.subList(1, parts.size).toTypedArray() + registration.run(label, sender!!, label, args) + } else { + throw RuntimeException("Empty command") + } + } +} diff --git a/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/command/TestCommandRegistration.kt b/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/command/TestCommandRegistration.kt index cd98f7e..b11ddb6 100644 --- a/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/command/TestCommandRegistration.kt +++ b/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/command/TestCommandRegistration.kt @@ -16,7 +16,7 @@ import kotlin.reflect.jvm.kotlinFunction typealias CommandSig = (CommandSender, String, Array) -> Unit @Component -internal class TestCommandRegistration( +class TestCommandRegistration( private val injection: SimpleMethodInjection, private val commandResolvers: Set ) : CommandRegistration { diff --git a/mcspring-build/mcspring-plugin-manager/pom.xml b/mcspring-build/mcspring-plugin-manager/pom.xml index 65d0a82..91a19a3 100644 --- a/mcspring-build/mcspring-plugin-manager/pom.xml +++ b/mcspring-build/mcspring-plugin-manager/pom.xml @@ -34,6 +34,7 @@ in.kyle.mcspring mcspring-test ${project.parent.version} + test diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandGamemode.kt b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandGamemode.kt index 74d10b6..c726600 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandGamemode.kt +++ b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandGamemode.kt @@ -14,6 +14,7 @@ internal class CommandGamemode { val gamemodes = mutableMapOf() GameMode.values().forEach { + @Suppress("DEPRECATION") gamemodes[it.value.toString()] = it gamemodes[it.name.toLowerCase()] = it } diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandHeal.kt b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandHeal.kt index 39b08a6..9e6be48 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandHeal.kt +++ b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandHeal.kt @@ -16,6 +16,7 @@ internal class CommandHeal { } private fun executeHeal(target: Player): String { + @Suppress("DEPRECATION") target.health = target.maxHealth return "Healed ${target.name}" } diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/BukkitPluginUnloader.kt b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/BukkitPluginUnloader.kt index b8300c5..a74ebb8 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/BukkitPluginUnloader.kt +++ b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/BukkitPluginUnloader.kt @@ -31,6 +31,7 @@ class BukkitPluginUnloader( names.putAll(getDeclaredField(pluginManager, "lookupNames")) val knownCommands = SimpleCommandMap::class.java.getDeclaredField("knownCommands") knownCommands.isAccessible = true + @Suppress("UNCHECKED_CAST") commands.putAll(knownCommands.get(commandMap) as Map) } @@ -53,7 +54,6 @@ class BukkitPluginUnloader( unregister.forEach { it.value.unregister(commandMap); commands.remove(it.key) } } - @SneakyThrows private fun closeClassLoader(classLoader: ClassLoader) { if (classLoader is URLClassLoader) { setDeclaredField(classLoader, "plugin", null) @@ -62,14 +62,13 @@ class BukkitPluginUnloader( } } - @SneakyThrows private fun getDeclaredField(obj: Any, fieldName: String): T { val field = obj.javaClass.getDeclaredField(fieldName) field.isAccessible = true + @Suppress("UNCHECKED_CAST") return field[obj] as T } - @SneakyThrows private fun setDeclaredField(obj: Any, fieldName: String, value: Any?) { val field = obj.javaClass.getDeclaredField(fieldName) field.isAccessible = true diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/PluginController.kt b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/PluginController.kt index 97e40b6..e1ef618 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/PluginController.kt +++ b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/PluginController.kt @@ -66,7 +66,7 @@ class PluginController( return false } - fun getPlugin(name: String?): Plugin? { + fun getPlugin(name: String): Plugin? { return pluginManager.getPlugin(name) } diff --git a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandGamemode.kt b/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandGamemode.kt index 1f52c75..5d9403c 100644 --- a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandGamemode.kt +++ b/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandGamemode.kt @@ -1,53 +1,49 @@ package `in`.kyle.mcspring.manager.commands -import `in`.kyle.api.bukkit.entity.TestPlayer import `in`.kyle.mcspring.test.MCSpringTest import `in`.kyle.mcspring.test.command.TestCommandExecutor import org.assertj.core.api.Assertions.assertThat import org.bukkit.GameMode +import org.bukkit.entity.Player import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test +import org.mockito.Mockito import org.springframework.beans.factory.annotation.Autowired @MCSpringTest class TestCommandGamemode { - - @Autowired - lateinit var executor: TestCommandExecutor - @Autowired - lateinit var sender: TestPlayer - - @BeforeEach - fun setup() { - sender.gameMode = GameMode.SURVIVAL - } - - @Test - fun testGmc() { - val output = executor.run(sender, "gmc") - assertThat(output).first().asString().isEqualTo("Game mode set to creative") - assertThat(sender.gameMode).isEqualTo(GameMode.CREATIVE) - } - - @Test - fun testGms() { - sender.gameMode = GameMode.SPECTATOR - val output = executor.run(sender, "gms") - assertThat(output).first().asString().isEqualTo("Game mode set to survival") - assertThat(sender.gameMode).isEqualTo(GameMode.SURVIVAL) - } - - @Test - fun testGamemode() { - val output = executor.run(sender, "gm creative") - assertThat(output).first().asString().isEqualTo("Game mode set to creative") - assertThat(sender.gameMode).isEqualTo(GameMode.CREATIVE) - } - - @Test - fun testGamemodeNumeric() { - val output = executor.run(sender, "gm 1") - assertThat(output).first().asString().isEqualTo("Game mode set to creative") - assertThat(sender.gameMode).isEqualTo(GameMode.CREATIVE) - } +// +// @Autowired +// lateinit var executor: TestCommandExecutor +// @Autowired +// lateinit var sender: Player +// +// @Test +// fun testGmc() { +// val output = executor.run(sender, "gmc") +// assertThat(output).first().asString().isEqualTo("Game mode set to creative") +// assertThat(sender.gameMode).isEqualTo(GameMode.CREATIVE) +// } +// +// @Test +// fun testGms() { +// sender.gameMode = GameMode.SPECTATOR +// val output = executor.run(sender, "gms") +// assertThat(output).first().asString().isEqualTo("Game mode set to survival") +// assertThat(sender.gameMode).isEqualTo(GameMode.SURVIVAL) +// } +// +// @Test +// fun testGamemode() { +// val output = executor.run(sender, "gm creative") +// assertThat(output).first().asString().isEqualTo("Game mode set to creative") +// assertThat(sender.gameMode).isEqualTo(GameMode.CREATIVE) +// } +// +// @Test +// fun testGamemodeNumeric() { +// val output = executor.run(sender, "gm 1") +// assertThat(output).first().asString().isEqualTo("Game mode set to creative") +// assertThat(sender.gameMode).isEqualTo(GameMode.CREATIVE) +// } } diff --git a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandSpeed.kt b/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandSpeed.kt index e1bb6c8..cf28d27 100644 --- a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandSpeed.kt +++ b/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandSpeed.kt @@ -1,6 +1,5 @@ package `in`.kyle.mcspring.manager.commands -import `in`.kyle.api.bukkit.entity.TestPlayer import `in`.kyle.mcspring.test.MCSpringTest import `in`.kyle.mcspring.test.command.TestCommandExecutor import org.assertj.core.api.Assertions.assertThat @@ -9,27 +8,27 @@ import org.springframework.beans.factory.annotation.Autowired @MCSpringTest internal class TestCommandSpeed { - - @Autowired - lateinit var executor: TestCommandExecutor - @Autowired - lateinit var sender: TestPlayer - - @Test - fun testSpeed() { - val messages = executor.run(sender, "speed 10") - assertThat(messages).first().asString().matches("Speed set to [^ ]+") - assertThat(sender.walkSpeed).isEqualTo(10f) - assertThat(sender.flySpeed).isEqualTo(10f) - } - - @Test - fun testSpeedUsage() { - sender.walkSpeed = 0f - sender.flySpeed = 0f - val messages = executor.run(sender, "speed") - assertThat(messages).first().asString().startsWith("Usage: ") - assertThat(sender.walkSpeed).isEqualTo(0f) - assertThat(sender.flySpeed).isEqualTo(0f) - } +// +// @Autowired +// lateinit var executor: TestCommandExecutor +// @Autowired +// lateinit var sender: TestPlayer +// +// @Test +// fun testSpeed() { +// val messages = executor.run(sender, "speed 10") +// assertThat(messages).first().asString().matches("Speed set to [^ ]+") +// assertThat(sender.walkSpeed).isEqualTo(10f) +// assertThat(sender.flySpeed).isEqualTo(10f) +// } +// +// @Test +// fun testSpeedUsage() { +// sender.walkSpeed = 0f +// sender.flySpeed = 0f +// val messages = executor.run(sender, "speed") +// assertThat(messages).first().asString().startsWith("Usage: ") +// assertThat(sender.walkSpeed).isEqualTo(0f) +// assertThat(sender.flySpeed).isEqualTo(0f) +// } } From d5dfa4100b56b3a365fc4e3d34a5d78652eb5faf Mon Sep 17 00:00:00 2001 From: kylepls Date: Tue, 12 May 2020 00:07:52 -0600 Subject: [PATCH 16/54] Rename .java to .kt --- .../economy/{EconomyException.java => EconomyException.kt} | 0 .../mcspring/economy/{EconomyService.java => EconomyService.kt} | 0 .../economy/{VaultEconomyService.java => VaultEconomyService.kt} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/{EconomyException.java => EconomyException.kt} (100%) rename mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/{EconomyService.java => EconomyService.kt} (100%) rename mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/{VaultEconomyService.java => VaultEconomyService.kt} (100%) diff --git a/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/EconomyException.java b/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/EconomyException.kt similarity index 100% rename from mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/EconomyException.java rename to mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/EconomyException.kt diff --git a/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/EconomyService.java b/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/EconomyService.kt similarity index 100% rename from mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/EconomyService.java rename to mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/EconomyService.kt diff --git a/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/VaultEconomyService.java b/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/VaultEconomyService.kt similarity index 100% rename from mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/VaultEconomyService.java rename to mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/VaultEconomyService.kt From ae4319113bbbec41111cb63b0ed304bdd5001f95 Mon Sep 17 00:00:00 2001 From: kylepls Date: Tue, 12 May 2020 00:07:53 -0600 Subject: [PATCH 17/54] Continue conversion --- .maven.xml | 1 - mcspring-api/mcspring-base/pom.xml | 1 - .../kyle/mcspring/annotation/PluginDepend.kt | 4 +- .../java/in/kyle/mcspring/command/Command.kt | 2 +- .../kyle/mcspring/command/CommandScanner.kt | 3 +- .../mcspring/command/SimpleCommandFactory.kt | 9 +- mcspring-api/mcspring-subcommands/pom.xml | 6 +- .../subcommands/PluginCommandResolver.kt | 2 +- .../plugincommand/api/PluginCommand.kt | 4 +- .../{ => impl}/PluginCommandBase.kt | 17 ++- .../{ => impl}/PluginCommandExecutors.kt | 29 +++-- .../{ => impl}/PluginCommandImpl.kt | 25 ++--- .../{ => impl}/PluginCommandRequires.kt | 4 +- .../{ => impl}/PluginCommandWith.kt | 4 +- .../javasupport}/HighIQExecutors.kt | 4 +- .../PluginCommandExecutorsJavaSupport.kt | 8 +- .../PluginCommandWithJavaSupport.kt | 4 +- .../mcspring/subcommands/tab/TabDiscovery.kt | 4 +- .../kyle/mcspring/subcommands/TestConsole.kt | 29 ++--- .../subcommands/TestJavaPluginCommand.java | 4 +- .../mcspring/subcommands/TestPluginCommand.kt | 24 ++-- .../mcspring/subcommands/TestTabCompletion.kt | 12 +- .../TestPluginCommandExecutors.kt | 58 ++++++++++ .../TestPluginCommandRequires.kt | 41 +++++++ .../plugincommand/TestPluginCommandWith.kt | 103 ++++++++++++++++++ mcspring-api/mcspring-test/pom.xml | 12 -- .../in/kyle/mcspring/test/MCSpringTest.java | 16 --- .../in/kyle/mcspring/test/MCSpringTest.kt | 15 +++ .../test/command/TestCommandExecutor.kt | 39 ++++--- .../test/command/TestCommandRegistration.kt | 22 ++-- .../kyle/mcspring/economy/EconomyException.kt | 9 +- .../kyle/mcspring/economy/EconomyService.kt | 34 ++---- .../mcspring/economy/VaultEconomyService.kt | 101 ++++++++--------- mcspring-api/pom.xml | 5 - .../mcspring-plugin-manager/pom.xml | 4 - .../manager/commands/CommandClassLoader.kt | 14 ++- .../manager/commands/CommandGamemode.kt | 14 ++- .../mcspring/manager/commands/CommandHeal.kt | 11 +- .../mcspring/manager/commands/CommandOp.kt | 18 ++- .../manager/commands/CommandPlugin.kt | 29 +++-- .../mcspring/manager/commands/CommandSpeed.kt | 12 +- .../controller/BukkitPluginUnloader.kt | 1 - .../manager/commands/TestCommandAbout.kt | 24 ---- .../commands/TestCommandClassLoader.kt | 7 +- .../manager/commands/TestCommandGamemode.kt | 62 ++++------- .../manager/commands/TestCommandHeal.kt | 20 ---- .../manager/commands/TestCommandOp.kt | 30 ----- .../manager/commands/TestCommandSpeed.kt | 48 ++++---- 48 files changed, 515 insertions(+), 434 deletions(-) rename mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/{ => impl}/PluginCommandBase.kt (71%) rename mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/{ => impl}/PluginCommandExecutors.kt (81%) rename mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/{ => impl}/PluginCommandImpl.kt (55%) rename mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/{ => impl}/PluginCommandRequires.kt (86%) rename mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/{ => impl}/PluginCommandWith.kt (94%) rename mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/{javacompat => impl/javasupport}/HighIQExecutors.kt (91%) rename mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/{javacompat => impl/javasupport}/PluginCommandExecutorsJavaSupport.kt (94%) rename mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/{javacompat => impl/javasupport}/PluginCommandWithJavaSupport.kt (83%) create mode 100644 mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/plugincommand/TestPluginCommandExecutors.kt create mode 100644 mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/plugincommand/TestPluginCommandRequires.kt create mode 100644 mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/plugincommand/TestPluginCommandWith.kt delete mode 100644 mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/MCSpringTest.java create mode 100644 mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/MCSpringTest.kt delete mode 100644 mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandAbout.kt delete mode 100644 mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandHeal.kt delete mode 100644 mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandOp.kt diff --git a/.maven.xml b/.maven.xml index 4bd6f9a..9033461 100644 --- a/.maven.xml +++ b/.maven.xml @@ -16,7 +16,6 @@ gpg - ${env.GPG_PASSPHRASE} diff --git a/mcspring-api/mcspring-base/pom.xml b/mcspring-api/mcspring-base/pom.xml index 0a84510..bf1c955 100644 --- a/mcspring-api/mcspring-base/pom.xml +++ b/mcspring-api/mcspring-base/pom.xml @@ -62,7 +62,6 @@ org.jetbrains.kotlin kotlin-reflect 1.3.72 - compile diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/annotation/PluginDepend.kt b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/annotation/PluginDepend.kt index 7cd94c7..14d2d96 100644 --- a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/annotation/PluginDepend.kt +++ b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/annotation/PluginDepend.kt @@ -1,5 +1,5 @@ package `in`.kyle.mcspring.annotation -@kotlin.annotation.Retention(AnnotationRetention.RUNTIME) +@Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) -annotation class PluginDepend(val plugins: Array, val soft: Boolean = false) +annotation class PluginDepend(vararg val plugins: String, val soft: Boolean = false) diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/Command.kt b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/Command.kt index 2abe177..c1be8cb 100644 --- a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/Command.kt +++ b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/Command.kt @@ -1,6 +1,6 @@ package `in`.kyle.mcspring.command -@kotlin.annotation.Retention(AnnotationRetention.RUNTIME) +@Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.FUNCTION) annotation class Command( val value: String, diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandScanner.kt b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandScanner.kt index faa1889..84a07e9 100644 --- a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandScanner.kt +++ b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandScanner.kt @@ -5,7 +5,6 @@ import org.springframework.context.ApplicationContext import org.springframework.context.ApplicationContextAware import org.springframework.stereotype.Component import java.lang.reflect.Method -import java.util.* @Component internal class CommandScanner( @@ -13,7 +12,7 @@ internal class CommandScanner( private val commandRegistration: CommandRegistration ) : ApplicationContextAware { - private val registeredCommands: MutableSet = HashSet() + private val registeredCommands: MutableSet = mutableSetOf() override fun setApplicationContext(ctx: ApplicationContext) { val scan = scanner.scanMethods(Command::class.java) diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleCommandFactory.kt b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleCommandFactory.kt index 416055f..f40554e 100644 --- a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleCommandFactory.kt +++ b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleCommandFactory.kt @@ -31,9 +31,12 @@ open class SimpleCommandFactory( return CommandExecutor { commandSender: CommandSender, bukkitCommand: Command, label: String, args: Array -> try { val command = CommandResolver.Command(commandSender, args.toList(), label) - val contextParameterResolvers: List = commandResolvers.map { it.makeResolver(command) } - .plus(injection.makeResolvers(listOf(commandSender, args, label, bukkitCommand))) - val types: List> = method.parameterTypes.toList() + val injectionResolvers = + injection.makeResolvers(listOf(commandSender, args, label, bukkitCommand)) + val contextParameterResolvers = + commandResolvers.map { it.makeResolver(command) }.plus(injectionResolvers) + + val types = method.parameterTypes.toList() val parameters = injection.getParameters(types, contextParameterResolvers) val result = method.invoke(obj, *parameters) if (result !is Unit) { diff --git a/mcspring-api/mcspring-subcommands/pom.xml b/mcspring-api/mcspring-subcommands/pom.xml index 6ec179f..71b14de 100644 --- a/mcspring-api/mcspring-subcommands/pom.xml +++ b/mcspring-api/mcspring-subcommands/pom.xml @@ -17,10 +17,8 @@ mcspring-base - org.bukkit - bukkit - ${spigot.version} - provided + org.spigotmc + spigot-api in.kyle.mcspring diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/PluginCommandResolver.kt b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/PluginCommandResolver.kt index 1da8f1f..1b0168c 100644 --- a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/PluginCommandResolver.kt +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/PluginCommandResolver.kt @@ -3,8 +3,8 @@ package `in`.kyle.mcspring.subcommands import `in`.kyle.mcspring.command.CommandResolver import `in`.kyle.mcspring.command.ParameterResolver import `in`.kyle.mcspring.command.SimpleMethodInjection -import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandImpl import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand +import `in`.kyle.mcspring.subcommands.plugincommand.impl.PluginCommandImpl import org.springframework.stereotype.Component @Component diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/api/PluginCommand.kt b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/api/PluginCommand.kt index f0a4df0..4dda6e6 100644 --- a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/api/PluginCommand.kt +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/api/PluginCommand.kt @@ -1,6 +1,6 @@ package `in`.kyle.mcspring.subcommands.plugincommand.api -import `in`.kyle.mcspring.subcommands.plugincommand.javacompat.HighIQExecutors +import `in`.kyle.mcspring.subcommands.plugincommand.impl.javasupport.HighIQExecutors import org.bukkit.command.CommandSender import kotlin.reflect.KFunction @@ -45,6 +45,8 @@ interface PluginCommand { fun on(command: String, e: KFunction) + fun onAny(vararg command: String, e: KFunction) + fun on(command: String, e: HighIQExecutors.O0) fun on(command: String, e: HighIQExecutors.O1) diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandBase.kt b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandBase.kt similarity index 71% rename from mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandBase.kt rename to mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandBase.kt index b671f92..406695c 100644 --- a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandBase.kt +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandBase.kt @@ -1,5 +1,6 @@ -package `in`.kyle.mcspring.subcommands.plugincommand +package `in`.kyle.mcspring.subcommands.plugincommand.impl +import org.bukkit.ChatColor import org.bukkit.command.CommandSender interface PluginCommandBase { @@ -15,12 +16,16 @@ interface PluginCommandBase { fun nextPart(): String? - fun dirtiesState(predicate: Boolean = true, - requiredStates: Array = arrayOf(State.CLEAN), - resultingState: State = State.SUCCESS, + fun dirtiesState(requiredStates: Array = arrayOf(State.CLEAN), + resultingState: State = State.COMPLETED, action: () -> Unit) - fun sendMessage(message: String) + fun sendMessage(message: String) { + if (message.isNotBlank()) { + val colored = ChatColor.translateAlternateColorCodes('&', message) + colored.lines().forEach { sender.sendMessage(it) } + } + } fun addCompletion(completion: String, type: String) { var stage = if (completions.size != 0) completions.last() else addCompletionStage(type, type) @@ -49,6 +54,6 @@ interface PluginCommandBase { enum class State { CLEAN, MISSING_ARG, - SUCCESS + COMPLETED } } diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandExecutors.kt b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandExecutors.kt similarity index 81% rename from mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandExecutors.kt rename to mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandExecutors.kt index 041f9a1..a42ec73 100644 --- a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandExecutors.kt +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandExecutors.kt @@ -1,7 +1,8 @@ -package `in`.kyle.mcspring.subcommands.plugincommand +package `in`.kyle.mcspring.subcommands.plugincommand.impl import `in`.kyle.mcspring.command.SimpleMethodInjection -import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandBase.State +import `in`.kyle.mcspring.subcommands.plugincommand.impl.PluginCommandBase.State +import `in`.kyle.mcspring.subcommands.plugincommand.api.Err1 import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand import kotlin.reflect.KFunction import kotlin.reflect.full.createType @@ -20,7 +21,21 @@ interface PluginCommandExecutors : PluginCommandBase, PluginCommand { override fun on(command: String, e: KFunction) { addCompletion(command, "on") - if (nextPart().equals(command)) { + onPartCondition({ it.equals(command) }, e) + } + + override fun onAny(vararg command: String, e: KFunction) = command.forEach { on(it, e) } + + override fun onInvalid(errorMessage: Err1) { + if (nextPart() != null) { + dirtiesState { + execute { sendMessage(errorMessage(parts[0])) } + } + } + } + + fun onPartCondition(condition: (String?) -> Boolean, e: KFunction) { + if (condition(nextPart())) { dirtiesState { val receivesPluginCommand = e.parameters.any { it.type.isSubtypeOf(PluginCommand::class.createType()) @@ -34,14 +49,6 @@ interface PluginCommandExecutors : PluginCommandBase, PluginCommand { } } - override fun onInvalid(errorMessage: (String) -> String) { - if (nextPart() != null) { - dirtiesState { - execute { sendMessage(errorMessage(parts[0])) } - } - } - } - override fun then(e: KFunction) = dirtiesState { execute { runWithKotlinContext(e) } } fun runWithContext(e: KFunction, types: List>, sendOutput: Boolean = true) { diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandImpl.kt b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandImpl.kt similarity index 55% rename from mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandImpl.kt rename to mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandImpl.kt index 7b7dea8..be47c4b 100644 --- a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandImpl.kt +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandImpl.kt @@ -1,15 +1,13 @@ -package `in`.kyle.mcspring.subcommands.plugincommand +package `in`.kyle.mcspring.subcommands.plugincommand.impl import `in`.kyle.mcspring.command.SimpleMethodInjection -import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandBase.CompletionStage -import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandBase.State -import `in`.kyle.mcspring.subcommands.plugincommand.javacompat.PluginCommandExecutorsJavaSupport -import `in`.kyle.mcspring.subcommands.plugincommand.javacompat.PluginCommandWithJavaSupport -import org.bukkit.ChatColor +import `in`.kyle.mcspring.subcommands.plugincommand.impl.PluginCommandBase.CompletionStage +import `in`.kyle.mcspring.subcommands.plugincommand.impl.PluginCommandBase.State +import `in`.kyle.mcspring.subcommands.plugincommand.impl.javasupport.PluginCommandExecutorsJavaSupport +import `in`.kyle.mcspring.subcommands.plugincommand.impl.javasupport.PluginCommandWithJavaSupport import org.bukkit.command.CommandSender -import java.lang.RuntimeException -open class PluginCommandImpl( +class PluginCommandImpl( override val injection: SimpleMethodInjection, override val sender: CommandSender, override val parts: MutableList, @@ -22,17 +20,10 @@ open class PluginCommandImpl( override var child: PluginCommandBase? = null override val completions: MutableList = mutableListOf() - override fun sendMessage(message: String) { - if (message.isNotBlank()) { - sender.sendMessage(ChatColor.translateAlternateColorCodes('&', message)) - } - } - - override fun dirtiesState(predicate: Boolean, - requiredStates: Array, + override fun dirtiesState(requiredStates: Array, resultingState: State, action: () -> Unit) { - if (state in requiredStates && predicate) { + if (state in requiredStates) { action() state = resultingState } diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandRequires.kt b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandRequires.kt similarity index 86% rename from mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandRequires.kt rename to mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandRequires.kt index 3f0b8b9..81bc5af 100644 --- a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandRequires.kt +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandRequires.kt @@ -1,10 +1,10 @@ -package `in`.kyle.mcspring.subcommands.plugincommand +package `in`.kyle.mcspring.subcommands.plugincommand.impl import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand import org.bukkit.command.ConsoleCommandSender import org.bukkit.entity.Player -interface PluginCommandRequires : PluginCommandBase, PluginCommand { +internal interface PluginCommandRequires : PluginCommandBase, PluginCommand { override fun requires(predicate: Boolean, errorMessage: () -> String) { if (!predicate) { diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandWith.kt b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandWith.kt similarity index 94% rename from mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandWith.kt rename to mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandWith.kt index 400f50a..05c0b96 100644 --- a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/PluginCommandWith.kt +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandWith.kt @@ -1,8 +1,8 @@ -package `in`.kyle.mcspring.subcommands.plugincommand +package `in`.kyle.mcspring.subcommands.plugincommand.impl -import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandBase.State import `in`.kyle.mcspring.subcommands.plugincommand.api.Err1 import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand +import `in`.kyle.mcspring.subcommands.plugincommand.impl.PluginCommandBase.State import org.bukkit.Bukkit interface PluginCommandWith : PluginCommandBase, PluginCommand { diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/javacompat/HighIQExecutors.kt b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/javasupport/HighIQExecutors.kt similarity index 91% rename from mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/javacompat/HighIQExecutors.kt rename to mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/javasupport/HighIQExecutors.kt index 53a9745..04188d0 100644 --- a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/javacompat/HighIQExecutors.kt +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/javasupport/HighIQExecutors.kt @@ -1,8 +1,6 @@ -package `in`.kyle.mcspring.subcommands.plugincommand.javacompat +package `in`.kyle.mcspring.subcommands.plugincommand.impl.javasupport import java.io.Serializable -import java.lang.invoke.SerializedLambda -import java.lang.reflect.Method interface HighIQExecutors { diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/javacompat/PluginCommandExecutorsJavaSupport.kt b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/javasupport/PluginCommandExecutorsJavaSupport.kt similarity index 94% rename from mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/javacompat/PluginCommandExecutorsJavaSupport.kt rename to mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/javasupport/PluginCommandExecutorsJavaSupport.kt index f5242d7..a464e3c 100644 --- a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/javacompat/PluginCommandExecutorsJavaSupport.kt +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/javasupport/PluginCommandExecutorsJavaSupport.kt @@ -1,9 +1,9 @@ -package `in`.kyle.mcspring.subcommands.plugincommand.javacompat +package `in`.kyle.mcspring.subcommands.plugincommand.impl.javasupport -import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandBase.State -import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandExecutors import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand -import `in`.kyle.mcspring.subcommands.plugincommand.javacompat.HighIQExecutors.* +import `in`.kyle.mcspring.subcommands.plugincommand.impl.PluginCommandBase.State +import `in`.kyle.mcspring.subcommands.plugincommand.impl.PluginCommandExecutors +import `in`.kyle.mcspring.subcommands.plugincommand.impl.javasupport.HighIQExecutors.* import java.lang.invoke.SerializedLambda import java.lang.reflect.Method import kotlin.reflect.KFunction diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/javacompat/PluginCommandWithJavaSupport.kt b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/javasupport/PluginCommandWithJavaSupport.kt similarity index 83% rename from mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/javacompat/PluginCommandWithJavaSupport.kt rename to mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/javasupport/PluginCommandWithJavaSupport.kt index 54544a3..482b629 100644 --- a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/javacompat/PluginCommandWithJavaSupport.kt +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/javasupport/PluginCommandWithJavaSupport.kt @@ -1,6 +1,6 @@ -package `in`.kyle.mcspring.subcommands.plugincommand.javacompat +package `in`.kyle.mcspring.subcommands.plugincommand.impl.javasupport -import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandWith +import `in`.kyle.mcspring.subcommands.plugincommand.impl.PluginCommandWith interface PluginCommandWithJavaSupport : PluginCommandWith { diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/tab/TabDiscovery.kt b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/tab/TabDiscovery.kt index 8d1f69d..e4cbb96 100644 --- a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/tab/TabDiscovery.kt +++ b/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/tab/TabDiscovery.kt @@ -1,8 +1,8 @@ package `in`.kyle.mcspring.subcommands.tab import `in`.kyle.mcspring.command.SimpleMethodInjection -import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandBase.State -import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandImpl +import `in`.kyle.mcspring.subcommands.plugincommand.impl.PluginCommandBase.State +import `in`.kyle.mcspring.subcommands.plugincommand.impl.PluginCommandImpl import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand import org.bukkit.command.CommandSender import org.springframework.stereotype.Component diff --git a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestConsole.kt b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestConsole.kt index 46087ca..1ecb9e7 100644 --- a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestConsole.kt +++ b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestConsole.kt @@ -1,29 +1,30 @@ package `in`.kyle.mcspring.subcommands import `in`.kyle.mcspring.command.SimpleMethodInjection -import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandImpl import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand +import `in`.kyle.mcspring.subcommands.plugincommand.impl.PluginCommandImpl import org.bukkit.command.CommandSender import org.bukkit.entity.Player import org.mockito.Mockito.mock -import java.util.function.Consumer object TestConsole { - private val injection: SimpleMethodInjection = SimpleMethodInjection(emptyList()) - - fun run(commandString: String, - consumer: (PluginCommand) -> Unit, - runExecutors: Boolean = true, - sender: CommandSender = mock(Player::class.java)): PluginCommandImpl { - val parts = if (!commandString.isBlank()) commandString.split(" ") else emptyList() - val command = PluginCommandImpl(injection, sender, parts.toMutableList(), runExecutors) + fun runCommand(commandString: String, + consumer: (PluginCommand) -> Unit, + runExecutors: Boolean = true, + sender: CommandSender = mock(Player::class.java)): PluginCommandImpl { + val command = makeCommand(commandString, sender, runExecutors) consumer(command) return command } - fun run(commandString: String, - consumer: Consumer, - runExecutors: Boolean = true, - sender: CommandSender = mock(Player::class.java)) = run(commandString, { consumer.accept(it) }, runExecutors, sender) + fun makeCommand(command: String, + player: CommandSender = mock(Player::class.java), + runExecutors: Boolean = true): + PluginCommandImpl { + return PluginCommandImpl(SimpleMethodInjection(listOf()), + player, + command.split(" ").filter { it.isNotBlank() }.toMutableList(), + runExecutors) + } } diff --git a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestJavaPluginCommand.java b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestJavaPluginCommand.java index 25cb77f..55eff0f 100644 --- a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestJavaPluginCommand.java +++ b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestJavaPluginCommand.java @@ -10,7 +10,7 @@ import java.util.List; import in.kyle.mcspring.command.SimpleMethodInjection; -import in.kyle.mcspring.subcommands.plugincommand.PluginCommandImpl; +import in.kyle.mcspring.subcommands.plugincommand.impl.PluginCommandImpl; import in.kyle.mcspring.subcommands.plugincommand.api.PluginCommand; import static org.assertj.core.api.Assertions.*; @@ -48,7 +48,7 @@ String parse2(PluginCommand command, String parsedPart) { PluginCommandImpl pluginCommand = new PluginCommandImpl(new SimpleMethodInjection(Collections.emptyList()), sender, - Arrays.asList("test-arg", "another-arg"), + new ArrayList<>(Arrays.asList("test-arg", "another-arg")), true); new Test().parse(pluginCommand); assertThat(messages).containsExactly("part is test-arg"); diff --git a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestPluginCommand.kt b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestPluginCommand.kt index ee606e2..5a8ed32 100644 --- a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestPluginCommand.kt +++ b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestPluginCommand.kt @@ -1,6 +1,6 @@ package `in`.kyle.mcspring.subcommands -import `in`.kyle.mcspring.subcommands.TestConsole.run +import `in`.kyle.mcspring.subcommands.TestConsole.runCommand import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand import org.assertj.core.api.Assertions.assertThat import org.bukkit.command.CommandSender @@ -24,11 +24,11 @@ internal class TestPluginCommand { } val (sender, outputMessages) = makePlayer() - run("subcommand1", Test()::exec1, true, sender) + runCommand("subcommand1", Test()::exec1, true, sender) assertThat(outputMessages).containsExactly("handler1") outputMessages.clear() - run("test-string subcommand2", Test()::exec2, sender = sender) + runCommand("test-string subcommand2", Test()::exec2, sender = sender) assertThat(outputMessages).containsExactly("handler2: test-string") } @@ -42,7 +42,7 @@ internal class TestPluginCommand { } val (sender, outputMessages) = makePlayer() - run("", Test()::root, sender = sender) + runCommand("", Test()::root, sender = sender) assertThat(outputMessages).containsExactly("Hello World") } @@ -78,7 +78,7 @@ internal class TestPluginCommand { } val (sender, outputMessages) = makePlayer() - run("Hello to you world", Test()::root, sender = sender) + runCommand("Hello to you world", Test()::root, sender = sender) assertThat(outputMessages).containsExactly("Hello to you world") } @@ -99,7 +99,7 @@ internal class TestPluginCommand { } val (sender, outputMessages) = makePlayer() - run("1 2 3", Test()::root, sender = sender) + runCommand("1 2 3", Test()::root, sender = sender) assertThat(outputMessages).containsExactly("true") } @@ -123,11 +123,11 @@ internal class TestPluginCommand { } val (sender, outputMessages) = makePlayer() - run("a b", Test()::root, sender = sender) + runCommand("a b", Test()::root, sender = sender) assertThat(outputMessages).containsExactly("Works") outputMessages.clear() - run("a c", Test()::root, sender = sender) + runCommand("a c", Test()::root, sender = sender) assertThat(outputMessages).isEmpty() } @@ -146,14 +146,14 @@ internal class TestPluginCommand { } val (sender, outputMessages) = makePlayer() - run("", Test()::root, sender = sender) + runCommand("", Test()::root, sender = sender) assertThat(outputMessages).containsExactly("no subcommand at root") outputMessages.clear() - run("a", Test()::root, sender = sender) + runCommand("a", Test()::root, sender = sender) assertThat(outputMessages).containsExactly("should run if int passed or missing arg") outputMessages.clear() - run("a 2", Test()::root, sender = sender) + runCommand("a 2", Test()::root, sender = sender) assertThat(outputMessages).containsExactly("should run if int passed or missing arg") } @@ -164,7 +164,7 @@ internal class TestPluginCommand { } val (sender, outputMessages) = makePlayer() - run("swag", Test()::root, sender = sender) + runCommand("swag", Test()::root, sender = sender) assertThat(outputMessages).containsExactly("swag is not an int") } diff --git a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestTabCompletion.kt b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestTabCompletion.kt index a7c1149..a90f359 100644 --- a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestTabCompletion.kt +++ b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestTabCompletion.kt @@ -1,6 +1,6 @@ package `in`.kyle.mcspring.subcommands -import `in`.kyle.mcspring.subcommands.TestConsole.run +import `in`.kyle.mcspring.subcommands.TestConsole.runCommand import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.fail @@ -17,7 +17,7 @@ internal class TestTabCompletion { } } - val command = run("hello world", Test()::base, runExecutors = false) + val command = runCommand("hello world", Test()::base, runExecutors = false) assertThat(command.completions).hasSize(2) assertThat(command.completions.first().completions).isEmpty() @@ -34,7 +34,7 @@ internal class TestTabCompletion { fun fail(): Unit = fail("Should not run") } - run("", Test()::base, runExecutors = false) + runCommand("", Test()::base, runExecutors = false) } @Test @@ -53,7 +53,7 @@ internal class TestTabCompletion { fun fail(): Unit = fail("Should not run") } - val command = run("subcommand-1 subcommand-2", Test()::base, runExecutors = false) + val command = runCommand("subcommand-1 subcommand-2", Test()::base, runExecutors = false) assertThat(command.completions).hasSize(1) assertThat(command.completions[0].completions).hasSize(3) assertThat(command.child).isNotNull() @@ -72,7 +72,7 @@ internal class TestTabCompletion { fun fail(): Unit = fail() } - val command = run("", Test()::base, runExecutors = false) + val command = runCommand("", Test()::base, runExecutors = false) assertThat(command.completions).hasSize(1) assertThat(command.completions[0].completions).containsExactly("a", "b", "c") } @@ -83,7 +83,7 @@ internal class TestTabCompletion { fun base(command: PluginCommand) = command.withAny(listOf("a", "b", "c")) { "" } } - val command = run("", Test()::base, runExecutors = false) + val command = runCommand("", Test()::base, runExecutors = false) assertThat(command.completions).hasSize(1) assertThat(command.completions[0].completions).containsExactly("a", "b", "c") } diff --git a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/plugincommand/TestPluginCommandExecutors.kt b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/plugincommand/TestPluginCommandExecutors.kt new file mode 100644 index 0000000..a468cd5 --- /dev/null +++ b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/plugincommand/TestPluginCommandExecutors.kt @@ -0,0 +1,58 @@ +package `in`.kyle.mcspring.subcommands.plugincommand + +import `in`.kyle.mcspring.subcommands.TestConsole.makeCommand +import `in`.kyle.mcspring.subcommands.plugincommand.impl.PluginCommandBase.State +import org.assertj.core.api.Assertions.assertThat +import org.bukkit.entity.Player +import org.junit.jupiter.api.Test +import org.mockito.ArgumentMatchers.anyString +import org.mockito.Mockito.* + +class TestPluginCommandExecutors { + + @Test + fun testOtherwise() { + val player = mock(Player::class.java) + val command = makeCommand("", player) + command.otherwise("output") + verify(player, only()).sendMessage("output") + } + + @Test + fun testOn() { + val player = mock(Player::class.java) + val command = makeCommand("optionB", player) + command.on("optionA", TestClass()::fail) + command.on("optionB", TestClass()::execute) + verify(player, only()).sendMessage("output") + } + + @Test + fun testOnMissingPart() { + val player = mock(Player::class.java) + val command = makeCommand("", player) + command.on("optionA", TestClass()::fail) + command.on("optionB", TestClass()::fail) + verify(player, never()).sendMessage(anyString()) + assertThat(command.state).isEqualTo(State.CLEAN) + } + + @Test + fun testOnInvalid() { + val player = mock(Player::class.java) + val command = makeCommand("invalidOption", player) + + command.on("optionA", TestClass()::fail) + command.on("optionB", TestClass()::fail) + assertThat(command.state).isEqualTo(State.CLEAN) + command.onInvalid { "no valid option $it" } + assertThat(command.state).isEqualTo(State.COMPLETED) + + verify(player, only()).sendMessage("no valid option invalidOption") + } + + class TestClass { + fun fail(): Any = fail() + fun execute() = "output" + } +} diff --git a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/plugincommand/TestPluginCommandRequires.kt b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/plugincommand/TestPluginCommandRequires.kt new file mode 100644 index 0000000..1f5a92b --- /dev/null +++ b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/plugincommand/TestPluginCommandRequires.kt @@ -0,0 +1,41 @@ +package `in`.kyle.mcspring.subcommands.plugincommand + +import `in`.kyle.mcspring.subcommands.TestConsole.makeCommand +import `in`.kyle.mcspring.subcommands.plugincommand.impl.PluginCommandBase.State +import org.assertj.core.api.Assertions.assertThat +import org.bukkit.command.ConsoleCommandSender +import org.bukkit.entity.Player +import org.junit.jupiter.api.Test +import org.mockito.ArgumentMatchers.anyString +import org.mockito.Mockito.* + +class TestPluginCommandRequires { + @Test + fun testRequires() { + val player = mock(Player::class.java) + val command = makeCommand("test", player) + command.requires(true) {"error"} + verify(player, never()).sendMessage(anyString()) + + command.requires(false) {"error"} + verify(player, only()).sendMessage("error") + } + + @Test + fun testRequiresPlayerSender() { + val player = mock(Player::class.java) + val command = makeCommand("test", player) + command.requiresPlayerSender { "error" } + verify(player, never()).sendMessage(anyString()) + assertThat(command.state).isEqualTo(State.CLEAN) + } + + @Test + fun testRequiresPlayerSenderFail() { + val console = mock(ConsoleCommandSender::class.java) + val command = makeCommand("test", console) + command.requiresPlayerSender { "error" } + verify(console, only()).sendMessage("error") + assertThat(command.state).isEqualTo(State.COMPLETED) + } +} diff --git a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/plugincommand/TestPluginCommandWith.kt b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/plugincommand/TestPluginCommandWith.kt new file mode 100644 index 0000000..ff41ccc --- /dev/null +++ b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/plugincommand/TestPluginCommandWith.kt @@ -0,0 +1,103 @@ +package `in`.kyle.mcspring.subcommands.plugincommand + +import `in`.kyle.mcspring.subcommands.TestConsole.makeCommand +import `in`.kyle.mcspring.subcommands.plugincommand.impl.PluginCommandBase.State +import org.assertj.core.api.Assertions.assertThat +import org.bukkit.entity.Player +import org.junit.jupiter.api.Test +import org.mockito.Mockito.* + +internal class TestPluginCommandWith { + + @Test + fun testWithString() { + val command = makeCommand("one two three") + command.withString() + assertThat(command.injections).containsExactly("one") + assertThat(command.parts).containsExactly("two", "three") + } + + @Test + fun testWithSentence() { + val command = makeCommand("one two three") + command.withSentence() + assertThat(command.injections).containsExactly("one two three") + assertThat(command.parts).isEmpty() + } + + @Test + fun testWithInt() { + val command = makeCommand("123") + command.withInt { "" } + assertThat(command.injections).containsExactly(123) + assertThat(command.parts).isEmpty() + } + + @Test + fun testWithIntInvalid() { + val player = mock(Player::class.java) + val command = makeCommand("onetwothree", player) + command.withInt { "invalid int $it" } + assertThat(command.injections).isEmpty() + verify(player, only()).sendMessage("invalid int onetwothree") + } + + @Test + fun testWithDouble() { + val command = makeCommand("123.456") + command.withDouble { "" } + assertThat(command.injections).containsExactly(123.456) + assertThat(command.parts).isEmpty() + } + + @Test + fun testWithDoubleInvalid() { + val player = mock(Player::class.java) + val command = makeCommand("onetwothree.fourfivesix", player) + command.withDouble { "invalid double $it" } + assertThat(command.injections).isEmpty() + verify(player, only()).sendMessage("invalid double onetwothree.fourfivesix") + } + + @Test + fun testWithMap() { + val options = mapOf("optionA" to 'a', "optionB" to 'b', "optionC" to 'c') + val command = makeCommand("optionB") + command.withMap(options) { "invalid option $it" } + assertThat(command.injections).containsExactly('b') + assertThat(command.parts).isEmpty() + } + + @Test + fun testWithAny() { + val options = listOf("optionA", "optionB", "optionC") + val command = makeCommand("optionB") + command.withAny(options) { "invalid option $it" } + assertThat(command.injections).containsExactly("optionB") + assertThat(command.parts).isEmpty() + } + + @Test + fun testWith() { + var command = makeCommand("option") + command.with({ "output" }) + assertThat(command.injections).containsExactly("output") + assertThat(command.parts).isEmpty() + assertThat(command.state).isEqualTo(State.CLEAN) + + command = makeCommand("option") + command.with({ null }) + assertThat(command.injections).isEmpty() + assertThat(command.parts).containsExactly("option") + assertThat(command.state).isEqualTo(State.COMPLETED) + } + + @Test + fun testWithNoOption() { + val command = makeCommand("") + command.with({ "output" }) + assertThat(command.injections).isEmpty() + assertThat(command.parts).isEmpty() + assertThat(command.state).isEqualTo(State.MISSING_ARG) + } +} diff --git a/mcspring-api/mcspring-test/pom.xml b/mcspring-api/mcspring-test/pom.xml index ba079af..5835a29 100644 --- a/mcspring-api/mcspring-test/pom.xml +++ b/mcspring-api/mcspring-test/pom.xml @@ -11,19 +11,7 @@ mcspring-test - - - jitpack.io - https://jitpack.io - - - - - org.projectlombok - lombok - 1.18.12 - org.spigotmc spigot-api diff --git a/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/MCSpringTest.java b/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/MCSpringTest.java deleted file mode 100644 index cd498ac..0000000 --- a/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/MCSpringTest.java +++ /dev/null @@ -1,16 +0,0 @@ -package in.kyle.mcspring.test; - -import org.springframework.boot.test.context.SpringBootTest; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Inherited; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -@Inherited -@SpringBootTest(classes = SpringSpigotSupport.class) -public @interface MCSpringTest { -} diff --git a/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/MCSpringTest.kt b/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/MCSpringTest.kt new file mode 100644 index 0000000..7485ded --- /dev/null +++ b/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/MCSpringTest.kt @@ -0,0 +1,15 @@ +package `in`.kyle.mcspring.test + +import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.junit.jupiter.MockitoExtension +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.test.context.junit.jupiter.SpringExtension +import java.lang.annotation.Inherited + +@Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) +@Retention(AnnotationRetention.RUNTIME) +@Inherited +@SpringBootTest(classes = [SpringSpigotSupport::class]) +@ExtendWith(SpringExtension::class, MockitoExtension::class) +annotation class MCSpringTest + diff --git a/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/command/TestCommandExecutor.kt b/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/command/TestCommandExecutor.kt index 68e0c78..170e5aa 100644 --- a/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/command/TestCommandExecutor.kt +++ b/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/command/TestCommandExecutor.kt @@ -2,37 +2,36 @@ package `in`.kyle.mcspring.test.command import org.bukkit.command.CommandSender import org.bukkit.entity.Player -import org.mockito.ArgumentMatchers -import org.mockito.Mockito -import org.mockito.invocation.InvocationOnMock +import org.mockito.ArgumentMatchers.anyString +import org.mockito.Mockito.doAnswer +import org.mockito.Mockito.mock import org.springframework.stereotype.Component -import java.util.* @Component class TestCommandExecutor( - val registration: TestCommandRegistration + private val registration: TestCommandRegistration ) { + + fun makeTestPlayer(): Pair> { + val sender = mock(Player::class.java) + val messages = mutableListOf() + doAnswer { messages.add(it.getArgument(0)); null } + .`when`(sender).sendMessage(anyString()) + return Pair(sender, messages) + } + fun run(command: String): List { - val sender = Mockito.mock(Player::class.java) - val messages = ArrayList() - Mockito.doAnswer { invocationOnMock: InvocationOnMock -> - messages.add(invocationOnMock.getArgument(0)) - null - }.`when`(sender).sendMessage(ArgumentMatchers.anyString()) + val (sender, messages) = makeTestPlayer() run(sender, command) return messages } - fun run(sender: CommandSender?, command: String) { - val command = command.trim { it <= ' ' } - val parts = command.split(" ").toMutableList() - if (parts[0].isEmpty()) { - parts.removeAt(0) - } - if (parts.size != 0) { + fun run(sender: CommandSender, command: String) { + val parts = command.trim().split(" ").filter { it.isNotBlank() }.toMutableList() + if (parts.isNotEmpty()) { val label = parts[0] - val args = parts.subList(1, parts.size).toTypedArray() - registration.run(label, sender!!, label, args) + val args = parts.subList(1, parts.size) + registration.run(label, sender, label, args.toTypedArray()) } else { throw RuntimeException("Empty command") } diff --git a/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/command/TestCommandRegistration.kt b/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/command/TestCommandRegistration.kt index b11ddb6..fac88fa 100644 --- a/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/command/TestCommandRegistration.kt +++ b/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/command/TestCommandRegistration.kt @@ -8,7 +8,6 @@ import org.bukkit.command.CommandSender import org.springframework.stereotype.Component import java.lang.reflect.Method import java.util.* -import java.util.function.Consumer import kotlin.reflect.KFunction import kotlin.reflect.jvm.javaMethod import kotlin.reflect.jvm.kotlinFunction @@ -25,26 +24,19 @@ class TestCommandRegistration( override fun register(command: Command, method: Method, obj: Any) { @Suppress("UNCHECKED_CAST") - val executor = makeExecutor(method.kotlinFunction as KFunction) - getAllNames(command).forEach(Consumer { key: String -> commandExecutors[key] = executor }) + val executor = makeExecutor(method.kotlinFunction as KFunction, obj) + command.aliases.plus(command.value).forEach { commandExecutors[it] = executor } } - private fun getAllNames(command: Command): List { - val commands = command.aliases.toMutableList() - commands.add(command.value) - return commands - } - - private fun makeExecutor(e: KFunction): CommandSig { + private fun makeExecutor(e: KFunction, obj: Any): CommandSig { return { sender: CommandSender, label: String, args: Array -> val temp = CommandResolver.Command(sender, args.toList(), label) - val thisResolvers = injection.makeResolvers(listOf(sender, args, label)) + val miscResolvers = injection.makeResolvers(listOf(sender)) val contextResolvers = commandResolvers.map { it.makeResolver(temp) } - val parameters = injection.getParameters(e.javaMethod!!.parameterTypes.toList(), - contextResolvers.plus - (thisResolvers)) - val result = e.call(*parameters) + val parameterTypes = e.javaMethod!!.parameterTypes.toList() + val parameters = injection.getParameters(parameterTypes, contextResolvers.plus(miscResolvers)) + val result = e.call(obj, *parameters) if (result !is Unit) { sender.sendMessage(result.toString()) } diff --git a/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/EconomyException.kt b/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/EconomyException.kt index be27449..78cdff1 100644 --- a/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/EconomyException.kt +++ b/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/EconomyException.kt @@ -1,8 +1,3 @@ -package in.kyle.mcspring.economy; +package `in`.kyle.mcspring.economy -public class EconomyException extends RuntimeException { - - EconomyException(String message) { - super(message); - } -} +class EconomyException internal constructor(message: String) : RuntimeException(message) diff --git a/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/EconomyService.kt b/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/EconomyService.kt index 085616e..ba642ad 100644 --- a/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/EconomyService.kt +++ b/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/EconomyService.kt @@ -1,25 +1,15 @@ -package in.kyle.mcspring.economy; +package `in`.kyle.mcspring.economy -import org.bukkit.OfflinePlayer; +import org.bukkit.OfflinePlayer +import java.math.BigDecimal -import java.math.BigDecimal; - -public interface EconomyService { - - void deposit(OfflinePlayer player, BigDecimal amount); - - void withdraw(OfflinePlayer player, BigDecimal amount); - - void transfer(OfflinePlayer origin, OfflinePlayer destination, BigDecimal amount); - - boolean hasAmount(OfflinePlayer player, BigDecimal amount); - - void createAccount(OfflinePlayer player); - - String format(BigDecimal amount); - - BigDecimal getBalance(OfflinePlayer player); - - boolean hasAccount(OfflinePlayer player); - +interface EconomyService { + fun deposit(player: OfflinePlayer, amount: BigDecimal) + fun withdraw(player: OfflinePlayer, amount: BigDecimal) + fun transfer(origin: OfflinePlayer, destination: OfflinePlayer, amount: BigDecimal) + fun hasAmount(player: OfflinePlayer, amount: BigDecimal): Boolean + fun createAccount(player: OfflinePlayer) + fun format(amount: BigDecimal): String + fun getBalance(player: OfflinePlayer): BigDecimal + fun hasAccount(player: OfflinePlayer): Boolean } diff --git a/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/VaultEconomyService.kt b/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/VaultEconomyService.kt index 06809e0..5633cd9 100644 --- a/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/VaultEconomyService.kt +++ b/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/VaultEconomyService.kt @@ -1,74 +1,59 @@ -package in.kyle.mcspring.economy; +package `in`.kyle.mcspring.economy -import in.kyle.mcspring.annotation.PluginDepend; -import net.milkbowl.vault.economy.Economy; -import net.milkbowl.vault.economy.EconomyResponse; - -import org.bukkit.OfflinePlayer; -import org.bukkit.Server; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.context.annotation.DependsOn; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Service; - -import java.math.BigDecimal; +import `in`.kyle.mcspring.annotation.PluginDepend +import net.milkbowl.vault.economy.Economy +import net.milkbowl.vault.economy.EconomyResponse +import org.bukkit.OfflinePlayer +import org.bukkit.Server +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass +import org.springframework.context.annotation.Lazy +import org.springframework.stereotype.Service +import java.math.BigDecimal @Lazy @Service -@ConditionalOnClass(Economy.class) -@PluginDepend(plugins = "Vault") -class VaultEconomyService implements EconomyService { - - private final Economy economy; - - public VaultEconomyService(Server server) { - economy = server.getServicesManager().getRegistration(Economy.class).getProvider(); - } - - @Override - public void deposit(OfflinePlayer player, BigDecimal amount) { - assertEconomyResponse(economy.depositPlayer(player, amount.doubleValue())); +@ConditionalOnClass(Economy::class) +@PluginDepend("Vault") +internal class VaultEconomyService(server: Server) : EconomyService { + + private val economy: Economy = server.servicesManager.getRegistration(Economy::class.java)!!.provider + + override fun deposit(player: OfflinePlayer, amount: BigDecimal) { + assertEconomyResponse(economy.depositPlayer(player, amount.toDouble())) } - - @Override - public void withdraw(OfflinePlayer player, BigDecimal amount) { - assertEconomyResponse(economy.withdrawPlayer(player, amount.doubleValue())); + + override fun withdraw(player: OfflinePlayer, amount: BigDecimal) { + assertEconomyResponse(economy.withdrawPlayer(player, amount.toDouble())) } - - @Override - public void transfer(OfflinePlayer origin, OfflinePlayer destination, BigDecimal amount) { - withdraw(origin, amount); - deposit(destination, amount); + + override fun transfer(origin: OfflinePlayer, destination: OfflinePlayer, amount: BigDecimal) { + withdraw(origin, amount) + deposit(destination, amount) } - - @Override - public boolean hasAmount(OfflinePlayer player, BigDecimal amount) { - return economy.has(player, amount.doubleValue()); + + override fun hasAmount(player: OfflinePlayer, amount: BigDecimal): Boolean { + return economy.has(player, amount.toDouble()) } - - @Override - public void createAccount(OfflinePlayer player) { - economy.createPlayerAccount(player); + + override fun createAccount(player: OfflinePlayer) { + economy.createPlayerAccount(player) } - - @Override - public String format(BigDecimal amount) { - return economy.format(amount.doubleValue()); + + override fun format(amount: BigDecimal): String { + return economy.format(amount.toDouble()) } - - @Override - public BigDecimal getBalance(OfflinePlayer player) { - return BigDecimal.valueOf(economy.getBalance(player)); + + override fun getBalance(player: OfflinePlayer): BigDecimal { + return BigDecimal.valueOf(economy.getBalance(player)) } - - @Override - public boolean hasAccount(OfflinePlayer player) { - return economy.hasAccount(player); + + override fun hasAccount(player: OfflinePlayer): Boolean { + return economy.hasAccount(player) } - - private void assertEconomyResponse(EconomyResponse response) { + + private fun assertEconomyResponse(response: EconomyResponse) { if (response.type != EconomyResponse.ResponseType.SUCCESS) { - throw new EconomyException(response.errorMessage); + throw EconomyException(response.errorMessage) } } } diff --git a/mcspring-api/pom.xml b/mcspring-api/pom.xml index c010fd8..1f85996 100644 --- a/mcspring-api/pom.xml +++ b/mcspring-api/pom.xml @@ -73,13 +73,8 @@ 1.8 - - -Xjvm-default=enable - - - diff --git a/mcspring-build/mcspring-plugin-manager/pom.xml b/mcspring-build/mcspring-plugin-manager/pom.xml index 91a19a3..2333bc6 100644 --- a/mcspring-build/mcspring-plugin-manager/pom.xml +++ b/mcspring-build/mcspring-plugin-manager/pom.xml @@ -21,10 +21,6 @@ in.kyle.mcspring mcspring-subcommands - - org.projectlombok - lombok - org.springframework.boot spring-boot-starter-test diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandClassLoader.kt b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandClassLoader.kt index 4ce54a3..4328700 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandClassLoader.kt +++ b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandClassLoader.kt @@ -1,16 +1,20 @@ package `in`.kyle.mcspring.manager.commands import `in`.kyle.mcspring.command.Command -import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandImpl +import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand import org.springframework.stereotype.Component @Component internal class CommandClassLoader { - @Command(value = "classloader", description = "Show ClassLoader information for a specific class", usage = "/classloader ") - fun classLoader(command: PluginCommandImpl) { - command.withString(); - command.then(::executeClassLoader); + @Command( + value = "classloader", + description = "Show ClassLoader information for a specific class", + usage = "/classloader " + ) + fun classLoader(command: PluginCommand) { + command.withString() + command.then(this::executeClassLoader) command.otherwise("Usage: /classloader ") } diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandGamemode.kt b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandGamemode.kt index c726600..a120760 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandGamemode.kt +++ b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandGamemode.kt @@ -1,15 +1,21 @@ package `in`.kyle.mcspring.manager.commands import `in`.kyle.mcspring.command.Command -import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandImpl +import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand import org.bukkit.GameMode import org.bukkit.entity.Player import org.springframework.stereotype.Component @Component internal class CommandGamemode { - @Command(value = "gamemode", aliases = ["gm"], description = "Set your game mode", usage = "/gamemode ") - fun gamemode(command: PluginCommandImpl) { + + @Command( + value = "gamemode", + aliases = ["gm"], + description = "Set your game mode", + usage = "/gamemode " + ) + fun gamemode(command: PluginCommand) { command.requiresPlayerSender { "Only players can run this command." } val gamemodes = mutableMapOf() @@ -20,7 +26,7 @@ internal class CommandGamemode { } command.withMap(gamemodes) { "$it is not a valid game mode" } - command.then(::gamemodeExecutor) + command.then(this::gamemodeExecutor) command.otherwise("Usage: /gamemode ") } diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandHeal.kt b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandHeal.kt index 9e6be48..8e20a6f 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandHeal.kt +++ b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandHeal.kt @@ -1,14 +1,19 @@ package `in`.kyle.mcspring.manager.commands import `in`.kyle.mcspring.command.Command -import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandImpl +import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand import org.bukkit.entity.Player import org.springframework.stereotype.Component @Component internal class CommandHeal { - @Command(value = "heal", description = "Heal yourself or another player", usage = "/heal ?") - fun heal(command: PluginCommandImpl) { + + @Command( + value = "heal", + description = "Heal yourself or another player", + usage = "/heal ?" + ) + fun heal(command: PluginCommand) { command.requiresPlayerSender { "Sender must be a player" } command.withPlayer { "Player $it not found" } command.then(this::executeHeal) diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandOp.kt b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandOp.kt index e69c044..45c2e13 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandOp.kt +++ b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandOp.kt @@ -1,21 +1,27 @@ package `in`.kyle.mcspring.manager.commands import `in`.kyle.mcspring.command.Command -import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandImpl +import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand import org.bukkit.command.CommandSender import org.springframework.stereotype.Component @Component internal class CommandOp { - @Command(value = "op", description = "Toggle yourself or another players OP status", usage = "/op ?") - fun op(command: PluginCommandImpl) { + + @Command( + value = "op", + description = "Toggle yourself or another players OP status", + usage = "/op ?" + ) + fun op(command: PluginCommand) { command.withPlayer { "Player $it not found" } - command.then(::toggleOp) - command.otherwise(::toggleOp) + command.then(this::toggleOp) + command.otherwise(this::toggleOp) } private fun toggleOp(target: CommandSender): String { target.isOp = !target.isOp - return "${target.name} is ${if (target.isOp) "now" else "no longer"} op" + val modifier = if (target.isOp) "now" else "no longer" + return "${target.name} is $modifier op" } } diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandPlugin.kt b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandPlugin.kt index 390039a..29e3576 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandPlugin.kt +++ b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandPlugin.kt @@ -2,7 +2,7 @@ package `in`.kyle.mcspring.manager.commands import `in`.kyle.mcspring.command.Command import `in`.kyle.mcspring.manager.controller.PluginController -import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandImpl +import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand import org.bukkit.plugin.Plugin import org.springframework.boot.autoconfigure.condition.ConditionalOnBean import org.springframework.stereotype.Component @@ -11,27 +11,32 @@ import java.nio.file.Path @Component @ConditionalOnBean(Plugin::class) internal class CommandPlugin( - private val pluginController: PluginController + private val pluginController: PluginController ) { - @Command(value = "plugin", aliases = ["pl"], description = "Load/unload/reload a specific plugin", usage = "/plugin ") - fun plugin(command: PluginCommandImpl) { - command.on("load", ::load) - command.on("unload", ::unload) - command.on("list", ::executeListPlugins) + @Command( + value = "plugin", + aliases = ["pl"], + description = "Load/unload/reload a specific plugin", + usage = "/plugin " + ) + fun plugin(command: PluginCommand) { + command.on("load", this::load) + command.on("unload", this::unload) + command.on("list", this::executeListPlugins) command.otherwise("Usage: /plugin ") } - private fun load(command: PluginCommandImpl) { - command.withMap(pluginController.loadablePlugins) { "Plugin $it not found or is already loaded" } - command.then(::executeLoad) + private fun load(command: PluginCommand) { + command.withMap(pluginController.loadablePlugins) { "Plugin $it not found or is already loaded" } + command.then(this::executeLoad) command.otherwise("Usage: /plugin load ") } - private fun unload(command: PluginCommandImpl) { + private fun unload(command: PluginCommand) { val plugins = pluginController.plugins.associateBy({ it.name }, { it }) command.withMap(plugins) { "Plugin $it is not loaded" } - command.then(::executeDisable) + command.then(this::executeDisable) command.otherwise("Usage: /plugin unload ") } diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandSpeed.kt b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandSpeed.kt index 77a1a84..3d3b5fc 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandSpeed.kt +++ b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandSpeed.kt @@ -1,18 +1,22 @@ package `in`.kyle.mcspring.manager.commands import `in`.kyle.mcspring.command.Command -import `in`.kyle.mcspring.subcommands.plugincommand.PluginCommandImpl +import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand import org.bukkit.entity.Player import org.springframework.stereotype.Component @Component internal class CommandSpeed { - @Command(value = "speed", description = "Set your movement and fly speed", usage = "/speed ") - fun speed(command: PluginCommandImpl) { + @Command( + value = "speed", + description = "Set your movement and fly speed", + usage = "/speed " + ) + fun speed(command: PluginCommand) { command.requiresPlayerSender { "Sender must be a player" } command.withDouble("Speed value must be an integer") - command.then(::speedExecutor) + command.then(this::speedExecutor) command.otherwise("Usage: /speed ") } diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/BukkitPluginUnloader.kt b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/BukkitPluginUnloader.kt index a74ebb8..490e7c8 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/BukkitPluginUnloader.kt +++ b/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/BukkitPluginUnloader.kt @@ -1,6 +1,5 @@ package `in`.kyle.mcspring.manager.controller -import lombok.SneakyThrows import org.bukkit.command.Command import org.bukkit.command.CommandMap import org.bukkit.command.PluginCommand diff --git a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandAbout.kt b/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandAbout.kt deleted file mode 100644 index c158f0d..0000000 --- a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandAbout.kt +++ /dev/null @@ -1,24 +0,0 @@ -package `in`.kyle.mcspring.manager.commands - -import `in`.kyle.mcspring.test.MCSpringTest -import `in`.kyle.mcspring.test.command.TestCommandExecutor -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test -import org.springframework.beans.factory.annotation.Autowired - -@MCSpringTest -internal class TestCommandAbout { - - @Autowired - lateinit var commandExecutor: TestCommandExecutor - - @Test - fun testAbout() { - val output: List = commandExecutor.run("about") - assertThat(output).hasSize(4) - assertThat(output[0]).matches("Plugin Name: [^ ]+") - assertThat(output[1]).matches("Plugin Version: [^ ]+") - assertThat(output[2]).matches("Spring Version: [^ ]+") - assertThat(output[3]).matches("Bukkit Version: [^ ]+") - } -} diff --git a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandClassLoader.kt b/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandClassLoader.kt index 11b4a80..53de286 100644 --- a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandClassLoader.kt +++ b/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandClassLoader.kt @@ -9,12 +9,9 @@ import org.springframework.beans.factory.annotation.Autowired @MCSpringTest internal class TestCommandClassLoader { - @Autowired - lateinit var commandExecutor: TestCommandExecutor - @Test - fun testClassLoader() { - val output: List = commandExecutor.run("classloader " + javaClass.name) + fun testClassLoader(@Autowired commandExecutor: TestCommandExecutor) { + val output = commandExecutor.run("classloader " + javaClass.name) assertThat(output).hasSize(2) assertThat(output[0]).isEqualTo("ClassLoader: ${javaClass.classLoader}") assertThat(output[1]).isEqualTo("Domain: ${javaClass.protectionDomain.codeSource.location}") diff --git a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandGamemode.kt b/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandGamemode.kt index 5d9403c..120d29f 100644 --- a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandGamemode.kt +++ b/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandGamemode.kt @@ -4,46 +4,32 @@ import `in`.kyle.mcspring.test.MCSpringTest import `in`.kyle.mcspring.test.command.TestCommandExecutor import org.assertj.core.api.Assertions.assertThat import org.bukkit.GameMode -import org.bukkit.entity.Player -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.mockito.Mockito +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.CsvSource +import org.mockito.Mockito.times +import org.mockito.Mockito.verify import org.springframework.beans.factory.annotation.Autowired @MCSpringTest class TestCommandGamemode { -// -// @Autowired -// lateinit var executor: TestCommandExecutor -// @Autowired -// lateinit var sender: Player -// -// @Test -// fun testGmc() { -// val output = executor.run(sender, "gmc") -// assertThat(output).first().asString().isEqualTo("Game mode set to creative") -// assertThat(sender.gameMode).isEqualTo(GameMode.CREATIVE) -// } -// -// @Test -// fun testGms() { -// sender.gameMode = GameMode.SPECTATOR -// val output = executor.run(sender, "gms") -// assertThat(output).first().asString().isEqualTo("Game mode set to survival") -// assertThat(sender.gameMode).isEqualTo(GameMode.SURVIVAL) -// } -// -// @Test -// fun testGamemode() { -// val output = executor.run(sender, "gm creative") -// assertThat(output).first().asString().isEqualTo("Game mode set to creative") -// assertThat(sender.gameMode).isEqualTo(GameMode.CREATIVE) -// } -// -// @Test -// fun testGamemodeNumeric() { -// val output = executor.run(sender, "gm 1") -// assertThat(output).first().asString().isEqualTo("Game mode set to creative") -// assertThat(sender.gameMode).isEqualTo(GameMode.CREATIVE) -// } + + @Autowired + lateinit var executor: TestCommandExecutor + + @ParameterizedTest + @CsvSource( + "gmc, CREATIVE", + "gms, SURVIVAL", + "gm creative, CREATIVE", + "gm survival, SURVIVAL", + "gm 1, CREATIVE", + "gm 0, SURVIVAL" + ) + fun testGamemodes(command: String, targetGameMode: GameMode) { + val (sender, messages) = executor.makeTestPlayer() + executor.run(sender, command) + assertThat(messages).first().asString() + .isEqualTo("Game mode set to ${targetGameMode.name.toLowerCase()}") + verify(sender, times(1)).gameMode = targetGameMode + } } diff --git a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandHeal.kt b/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandHeal.kt deleted file mode 100644 index bcd9788..0000000 --- a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandHeal.kt +++ /dev/null @@ -1,20 +0,0 @@ -package `in`.kyle.mcspring.manager.commands - -import `in`.kyle.mcspring.test.MCSpringTest -import `in`.kyle.mcspring.test.command.TestCommandExecutor -import org.assertj.core.api.Assertions -import org.junit.jupiter.api.Test -import org.springframework.beans.factory.annotation.Autowired - -@MCSpringTest -internal class TestCommandHeal { - - @Autowired - lateinit var commandExecutor: TestCommandExecutor - - @Test - fun testHeal() { - val output = commandExecutor.run("heal") - Assertions.assertThat(output).first().asString().matches("Healed [^ ]+") - } -} diff --git a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandOp.kt b/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandOp.kt deleted file mode 100644 index da11516..0000000 --- a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandOp.kt +++ /dev/null @@ -1,30 +0,0 @@ -package `in`.kyle.mcspring.manager.commands - -import `in`.kyle.mcspring.test.MCSpringTest -import `in`.kyle.mcspring.test.command.TestCommandExecutor -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test -import org.springframework.beans.factory.annotation.Autowired - -@MCSpringTest -internal class TestCommandOp { - - @Autowired - lateinit var executor: TestCommandExecutor - - @Test - fun testOpSelf() { - val messages = executor.run("op") - assertThat(messages).hasSize(1) - assertThat(messages[0]).matches("[^ ]+ is now op") - } - - @Test - fun testOpOther() { - // TODO: 2020-03-13 Need better testing instrumentation - // server.getOnlinePlayers().add(target); - // List messages = executor.run("op " + target.getName()); - // assertThat(messages).hasSize(1); - // assertThat(messages.get(0)).matches(target.getName() + " is now op"); - } -} diff --git a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandSpeed.kt b/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandSpeed.kt index cf28d27..bdd060a 100644 --- a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandSpeed.kt +++ b/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandSpeed.kt @@ -4,31 +4,31 @@ import `in`.kyle.mcspring.test.MCSpringTest import `in`.kyle.mcspring.test.command.TestCommandExecutor import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.mockito.ArgumentMatchers.anyFloat +import org.mockito.Mockito.* import org.springframework.beans.factory.annotation.Autowired @MCSpringTest -internal class TestCommandSpeed { -// -// @Autowired -// lateinit var executor: TestCommandExecutor -// @Autowired -// lateinit var sender: TestPlayer -// -// @Test -// fun testSpeed() { -// val messages = executor.run(sender, "speed 10") -// assertThat(messages).first().asString().matches("Speed set to [^ ]+") -// assertThat(sender.walkSpeed).isEqualTo(10f) -// assertThat(sender.flySpeed).isEqualTo(10f) -// } -// -// @Test -// fun testSpeedUsage() { -// sender.walkSpeed = 0f -// sender.flySpeed = 0f -// val messages = executor.run(sender, "speed") -// assertThat(messages).first().asString().startsWith("Usage: ") -// assertThat(sender.walkSpeed).isEqualTo(0f) -// assertThat(sender.flySpeed).isEqualTo(0f) -// } +internal class TestCommandSpeed() { + + @Autowired + lateinit var executor: TestCommandExecutor + + @Test + fun testSpeed() { + val (sender, messages) = executor.makeTestPlayer() + executor.run(sender, "speed 10") + assertThat(messages).first().asString().matches("Speed set to [^ ]+") + verify(sender, times(1)).walkSpeed = 10F + verify(sender, times(1)).flySpeed = 10F + } + + @Test + fun testSpeedUsage() { + val (sender, messages) = executor.makeTestPlayer() + executor.run(sender, "speed") + assertThat(messages).first().asString().matches("Usage: .+") + verify(sender, never()).walkSpeed = anyFloat() + verify(sender, never()).flySpeed = anyFloat() + } } From 8b554d5a26cfd4bf095afa0e475d53d6579eb01e Mon Sep 17 00:00:00 2001 From: kylepls Date: Tue, 12 May 2020 12:50:33 -0600 Subject: [PATCH 18/54] Rename .java to .kt --- .../test/{SpringSpigotSupport.java => SpringSpigotSupport.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/{SpringSpigotSupport.java => SpringSpigotSupport.kt} (100%) diff --git a/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/SpringSpigotSupport.java b/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/SpringSpigotSupport.kt similarity index 100% rename from mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/SpringSpigotSupport.java rename to mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/SpringSpigotSupport.kt From 9d8aa5bfdb067010a3c3034d6c60680865f8b11b Mon Sep 17 00:00:00 2001 From: kylepls Date: Tue, 12 May 2020 12:50:34 -0600 Subject: [PATCH 19/54] Cleanup vault api --- .../kyle/mcspring/test/SpringSpigotSupport.kt | 38 +++--------- mcspring-api/mcspring-vault/pom.xml | 34 ++++++++--- .../kyle/mcspring/economy/EconomyService.kt | 15 ----- .../api}/EconomyException.kt | 2 +- .../kyle/mcspring/vault/api/EconomyService.kt | 60 +++++++++++++++++++ .../impl}/VaultEconomyService.kt | 19 +++++- 6 files changed, 112 insertions(+), 56 deletions(-) delete mode 100644 mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/EconomyService.kt rename mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/{economy => vault/api}/EconomyException.kt (70%) create mode 100644 mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/vault/api/EconomyService.kt rename mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/{economy => vault/impl}/VaultEconomyService.kt (71%) diff --git a/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/SpringSpigotSupport.kt b/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/SpringSpigotSupport.kt index e3ba79d..7a9ae95 100644 --- a/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/SpringSpigotSupport.kt +++ b/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/SpringSpigotSupport.kt @@ -1,35 +1,11 @@ -package in.kyle.mcspring.test; +package `in`.kyle.mcspring.test -import org.bukkit.entity.Player; -import org.springframework.boot.SpringBootConfiguration; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.info.BuildProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Import; -import org.springframework.context.annotation.Scope; - -import java.util.Properties; - -import in.kyle.mcspring.SpringPlugin; -import in.kyle.mcspring.command.SimpleMethodInjection; - -import static org.mockito.Mockito.*; +import `in`.kyle.mcspring.SpringPlugin +import org.springframework.boot.SpringBootConfiguration +import org.springframework.boot.autoconfigure.EnableAutoConfiguration +import org.springframework.context.annotation.ComponentScan @SpringBootConfiguration @EnableAutoConfiguration -@ComponentScan(basePackageClasses = {SpringSpigotSupport.class, SpringPlugin.class}) -@Import(SimpleMethodInjection.class) -class SpringSpigotSupport { - - @Bean - @Scope("prototype") - Player player() { - return mock(Player.class); - } - - @Bean - BuildProperties buildProperties() { - return new BuildProperties(new Properties()); - } -} +@ComponentScan(basePackageClasses = [SpringSpigotSupport::class, SpringPlugin::class]) +internal open class SpringSpigotSupport diff --git a/mcspring-api/mcspring-vault/pom.xml b/mcspring-api/mcspring-vault/pom.xml index 4f13e62..292c0b7 100644 --- a/mcspring-api/mcspring-vault/pom.xml +++ b/mcspring-api/mcspring-vault/pom.xml @@ -10,25 +10,33 @@ 4.0.0 - 1.7.3 + 1.7 mcspring-vault - vault-repo - http://nexus.hc.to/content/repositories/pub_releases + jitpack.io + https://jitpack.io + + + jcenter + JCenter + https://jcenter.bintray.com/ + + + ${project.parent.groupId} mcspring-base - net.milkbowl.vault - Vault + com.github.MilkBowl + VaultAPI ${vault.version} provided @@ -51,8 +59,20 @@ maven-source-plugin - org.apache.maven.plugins - maven-javadoc-plugin + org.jetbrains.dokka + dokka-maven-plugin + 0.10.1 + + + pre-site + + dokka + + + + + html + diff --git a/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/EconomyService.kt b/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/EconomyService.kt deleted file mode 100644 index ba642ad..0000000 --- a/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/EconomyService.kt +++ /dev/null @@ -1,15 +0,0 @@ -package `in`.kyle.mcspring.economy - -import org.bukkit.OfflinePlayer -import java.math.BigDecimal - -interface EconomyService { - fun deposit(player: OfflinePlayer, amount: BigDecimal) - fun withdraw(player: OfflinePlayer, amount: BigDecimal) - fun transfer(origin: OfflinePlayer, destination: OfflinePlayer, amount: BigDecimal) - fun hasAmount(player: OfflinePlayer, amount: BigDecimal): Boolean - fun createAccount(player: OfflinePlayer) - fun format(amount: BigDecimal): String - fun getBalance(player: OfflinePlayer): BigDecimal - fun hasAccount(player: OfflinePlayer): Boolean -} diff --git a/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/EconomyException.kt b/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/vault/api/EconomyException.kt similarity index 70% rename from mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/EconomyException.kt rename to mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/vault/api/EconomyException.kt index 78cdff1..ea20dd8 100644 --- a/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/EconomyException.kt +++ b/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/vault/api/EconomyException.kt @@ -1,3 +1,3 @@ -package `in`.kyle.mcspring.economy +package `in`.kyle.mcspring.vault.api class EconomyException internal constructor(message: String) : RuntimeException(message) diff --git a/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/vault/api/EconomyService.kt b/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/vault/api/EconomyService.kt new file mode 100644 index 0000000..05a6c4c --- /dev/null +++ b/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/vault/api/EconomyService.kt @@ -0,0 +1,60 @@ +package `in`.kyle.mcspring.vault.api + +import org.bukkit.OfflinePlayer +import java.math.BigDecimal + +// https://github.com/MilkBowl/VaultAPI/blob/master/src/main/java/net/milkbowl/vault/economy/Economy.java +/** + * A wrapper for the [VaultAPI](http://milkbowl.github.io/VaultAPI/) + */ +interface EconomyService { + + /** + * Attempts to create a player account for the given player + * @return if the account creation was successful + */ + fun createAccount(player: OfflinePlayer) + + /** + * Deposit a **non-negative** balance to a player + * @throws EconomyException if the transaction fails + */ + fun deposit(player: OfflinePlayer, amount: BigDecimal) + + /** + * Format amount into a human readable String This provides translation into + * economy specific formatting to improve consistency between plugins. + * @return Human-readable string describing amount + */ + fun format(amount: BigDecimal): String + + /** + * Gets balance of a player + * @return Amount currently held in players account + */ + fun getBalance(player: OfflinePlayer): BigDecimal + + /** + * Checks if the player account has the **non-negative** amount + * @return True if [player] has [amount], False else wise + */ + fun hasAmount(player: OfflinePlayer, amount: BigDecimal): Boolean + + /** + * Checks if this player has an account on the server yet + * This will always return true if the player has joined the server at least once + * as all major economy plugins auto-generate a player account when the player joins the server + * @return if the player has an account + */ + fun hasAccount(player: OfflinePlayer): Boolean + + /** + * Transfer an amount between two players + */ + fun transfer(origin: OfflinePlayer, destination: OfflinePlayer, amount: BigDecimal) + + /** + * Withdraw a **non-negative** amount from a player + */ + fun withdraw(player: OfflinePlayer, amount: BigDecimal) +} diff --git a/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/VaultEconomyService.kt b/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/vault/impl/VaultEconomyService.kt similarity index 71% rename from mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/VaultEconomyService.kt rename to mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/vault/impl/VaultEconomyService.kt index 5633cd9..252b446 100644 --- a/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/economy/VaultEconomyService.kt +++ b/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/vault/impl/VaultEconomyService.kt @@ -1,6 +1,8 @@ -package `in`.kyle.mcspring.economy +package `in`.kyle.mcspring.vault.impl import `in`.kyle.mcspring.annotation.PluginDepend +import `in`.kyle.mcspring.vault.api.EconomyException +import `in`.kyle.mcspring.vault.api.EconomyService import net.milkbowl.vault.economy.Economy import net.milkbowl.vault.economy.EconomyResponse import org.bukkit.OfflinePlayer @@ -10,6 +12,7 @@ import org.springframework.context.annotation.Lazy import org.springframework.stereotype.Service import java.math.BigDecimal +@Suppress("unused") @Lazy @Service @ConditionalOnClass(Economy::class) @@ -19,24 +22,32 @@ internal class VaultEconomyService(server: Server) : EconomyService { private val economy: Economy = server.servicesManager.getRegistration(Economy::class.java)!!.provider override fun deposit(player: OfflinePlayer, amount: BigDecimal) { + requirePositive(amount) assertEconomyResponse(economy.depositPlayer(player, amount.toDouble())) } override fun withdraw(player: OfflinePlayer, amount: BigDecimal) { + requirePositive(amount) assertEconomyResponse(economy.withdrawPlayer(player, amount.toDouble())) } override fun transfer(origin: OfflinePlayer, destination: OfflinePlayer, amount: BigDecimal) { + requirePositive(amount) + require(hasAmount(origin, amount)) { "Player ${origin.name} does not have the required amount $amount" } withdraw(origin, amount) + if (!hasAccount(destination)) { + createAccount(destination) + } deposit(destination, amount) } override fun hasAmount(player: OfflinePlayer, amount: BigDecimal): Boolean { + requirePositive(amount) return economy.has(player, amount.toDouble()) } override fun createAccount(player: OfflinePlayer) { - economy.createPlayerAccount(player) + assert(economy.createPlayerAccount(player)) { "Failed to create account for ${player.name}" } } override fun format(amount: BigDecimal): String { @@ -56,4 +67,8 @@ internal class VaultEconomyService(server: Server) : EconomyService { throw EconomyException(response.errorMessage) } } + + private fun requirePositive(decimal: BigDecimal) { + require(decimal.toDouble() > 0) { "The decimal amount must be positive, got: $decimal" } + } } From 616150acdda11540dedad1950214fd7807d784e8 Mon Sep 17 00:00:00 2001 From: kylepls Date: Tue, 12 May 2020 20:48:32 -0600 Subject: [PATCH 20/54] start orchid --- mcspring-api/mcspring-base/pom.xml | 9 +++ .../kyle/mcspring/annotation/PluginDepend.kt | 3 + .../java/in/kyle/mcspring/command/Command.kt | 4 ++ .../mcspring/command/SimpleMethodInjection.kt | 4 -- .../mcspring/scheduler/SchedulerService.kt | 31 ++++++++ mcspring-api/mcspring-jar-loader/pom.xml | 10 +-- .../boot/loader/mcspring/McSpringLoader.java | 30 -------- .../boot/loader/mcspring/McSpringLoader.kt | 18 +++++ mcspring-api/mcspring-subcommands/pom.xml | 4 +- mcspring-api/mcspring-vault/pom.xml | 20 ------ .../{api => economy}/EconomyException.kt | 2 +- .../vault/{api => economy}/EconomyService.kt | 2 +- .../{impl => economy}/VaultEconomyService.kt | 4 +- pom.xml | 72 +++++++++++++++++++ src/orchid/resources/changelog/1.0.0.md | 1 + src/orchid/resources/config.yml | 38 ++++++++++ src/orchid/resources/homepage.md | 5 ++ src/orchid/resources/pages/changelog.md | 4 ++ src/orchid/resources/wiki/summary.md | 6 ++ 19 files changed, 197 insertions(+), 70 deletions(-) delete mode 100644 mcspring-api/mcspring-jar-loader/src/main/java/org/springframework/boot/loader/mcspring/McSpringLoader.java create mode 100644 mcspring-api/mcspring-jar-loader/src/main/java/org/springframework/boot/loader/mcspring/McSpringLoader.kt rename mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/vault/{api => economy}/EconomyException.kt (68%) rename mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/vault/{api => economy}/EconomyService.kt (97%) rename mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/vault/{impl => economy}/VaultEconomyService.kt (94%) create mode 100644 src/orchid/resources/changelog/1.0.0.md create mode 100644 src/orchid/resources/config.yml create mode 100644 src/orchid/resources/homepage.md create mode 100644 src/orchid/resources/pages/changelog.md create mode 100644 src/orchid/resources/wiki/summary.md diff --git a/mcspring-api/mcspring-base/pom.xml b/mcspring-api/mcspring-base/pom.xml index bf1c955..9e50b6d 100644 --- a/mcspring-api/mcspring-base/pom.xml +++ b/mcspring-api/mcspring-base/pom.xml @@ -64,4 +64,13 @@ 1.3.72 + + + + + + + + + diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/annotation/PluginDepend.kt b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/annotation/PluginDepend.kt index 14d2d96..397e866 100644 --- a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/annotation/PluginDepend.kt +++ b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/annotation/PluginDepend.kt @@ -1,5 +1,8 @@ package `in`.kyle.mcspring.annotation +/** + * Declares a dependency in the `plugin.yml` + */ @Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) annotation class PluginDepend(vararg val plugins: String, val soft: Boolean = false) diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/Command.kt b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/Command.kt index c1be8cb..68d2076 100644 --- a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/Command.kt +++ b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/Command.kt @@ -1,5 +1,9 @@ package `in`.kyle.mcspring.command +/** + * Defines a command to be handled by a certain method. + * Values from this annotation will be reflected in the `plugin.yml`. + */ @Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.FUNCTION) annotation class Command( diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleMethodInjection.kt b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleMethodInjection.kt index 7a00a1b..0f40f35 100644 --- a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleMethodInjection.kt +++ b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleMethodInjection.kt @@ -7,10 +7,6 @@ import kotlin.reflect.full.isSubclassOf import kotlin.reflect.jvm.isAccessible import kotlin.reflect.jvm.javaType -/** - * Used to inject method parameters - * Does not support annotated parameters - */ @Lazy @Component class SimpleMethodInjection( diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/scheduler/SchedulerService.kt b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/scheduler/SchedulerService.kt index 0aa083b..bab2e68 100644 --- a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/scheduler/SchedulerService.kt +++ b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/scheduler/SchedulerService.kt @@ -7,6 +7,10 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean import org.springframework.context.annotation.Lazy import org.springframework.stereotype.Service +/** + * Convenience methods for Bukkit scheduling + * @see [BukkitScheduler] + */ @Lazy @Service @ConditionalOnBean(Plugin::class) @@ -14,38 +18,65 @@ class SchedulerService( private val scheduler: BukkitScheduler, private val plugin: Plugin ) { + /** + * @see BukkitScheduler.runTaskAsynchronously + */ fun asyncTask(task: Runnable): BukkitTask { return scheduler.runTaskAsynchronously(plugin, task) } + /** + * @see BukkitScheduler.runTask + */ fun syncTask(task: Runnable): BukkitTask { return scheduler.runTask(plugin, task) } + /** + * @see BukkitScheduler.runTaskLaterAsynchronously + */ fun asyncDelayedTask(task: Runnable, delayTicks: Long): BukkitTask { return scheduler.runTaskLaterAsynchronously(plugin, task, delayTicks) } + /** + * @see BukkitScheduler.scheduleSyncDelayedTask + */ fun syncDelayedTask(task: Runnable, delayTicks: Long): BukkitTask { return scheduler.runTaskLater(plugin, task, delayTicks) } + /** + * @see BukkitScheduler.runTaskTimerAsynchronously + */ fun asyncRepeatingTask(task: Runnable, delayTicks: Long, periodTicks: Long): BukkitTask { return scheduler.runTaskTimerAsynchronously(plugin, task, delayTicks, periodTicks) } + /** + * @see BukkitScheduler.runTaskTimer + */ fun syncRepeatingTask(task: Runnable, delayTicks: Long, periodTicks: Long): BukkitTask { return scheduler.runTaskTimer(plugin, task, delayTicks, periodTicks) } + /** + * @see BukkitScheduler.cancelTask + */ fun cancelTask(task: BukkitTask) { scheduler.cancelTask(task.taskId) } + /** + * @see BukkitScheduler.isCurrentlyRunning + */ fun isCurrentlyRunning(task: BukkitTask): Boolean { return scheduler.isCurrentlyRunning(task.taskId) } + /** + * @see BukkitScheduler.isQueued + */ fun isQueued(task: BukkitTask): Boolean { return scheduler.isQueued(task.taskId) } diff --git a/mcspring-api/mcspring-jar-loader/pom.xml b/mcspring-api/mcspring-jar-loader/pom.xml index b851777..d5283c8 100644 --- a/mcspring-api/mcspring-jar-loader/pom.xml +++ b/mcspring-api/mcspring-jar-loader/pom.xml @@ -12,17 +12,9 @@ mcspring-jar-loader - - org.projectlombok - lombok - - - org.spigotmc - spigot-api - org.springframework.boot spring-boot-loader - \ No newline at end of file + diff --git a/mcspring-api/mcspring-jar-loader/src/main/java/org/springframework/boot/loader/mcspring/McSpringLoader.java b/mcspring-api/mcspring-jar-loader/src/main/java/org/springframework/boot/loader/mcspring/McSpringLoader.java deleted file mode 100644 index ec13147..0000000 --- a/mcspring-api/mcspring-jar-loader/src/main/java/org/springframework/boot/loader/mcspring/McSpringLoader.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.springframework.boot.loader.mcspring; - -import org.springframework.boot.loader.JarLauncher; -import org.springframework.boot.loader.archive.Archive; - -import java.lang.reflect.Method; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.List; - -import lombok.SneakyThrows; - -// Packaging is so that it all blends in with the Spring loader -public class McSpringLoader extends JarLauncher { - - @SneakyThrows - public void launch(ClassLoader classLoader) { - List activeArchives = getClassPathArchives(); - Method addURL = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); - addURL.setAccessible(true); - for (Archive archive : activeArchives) { - addURL.invoke(classLoader, archive.getUrl()); - } - } - - @Override - protected String getMainClass() { - return ""; - } -} diff --git a/mcspring-api/mcspring-jar-loader/src/main/java/org/springframework/boot/loader/mcspring/McSpringLoader.kt b/mcspring-api/mcspring-jar-loader/src/main/java/org/springframework/boot/loader/mcspring/McSpringLoader.kt new file mode 100644 index 0000000..37e9731 --- /dev/null +++ b/mcspring-api/mcspring-jar-loader/src/main/java/org/springframework/boot/loader/mcspring/McSpringLoader.kt @@ -0,0 +1,18 @@ +package org.springframework.boot.loader.mcspring + +import org.springframework.boot.loader.JarLauncher +import java.net.URL +import java.net.URLClassLoader + +// Packaging is so that it all blends in with the Spring loader +class McSpringLoader : JarLauncher() { + + fun launch(classLoader: ClassLoader?) { + val activeArchives = classPathArchives + val addURL = URLClassLoader::class.java.getDeclaredMethod("addURL", URL::class.java) + addURL.isAccessible = true + activeArchives.forEach { addURL(classLoader, it.url) } + } + + override fun getMainClass() = "" +} diff --git a/mcspring-api/mcspring-subcommands/pom.xml b/mcspring-api/mcspring-subcommands/pom.xml index 71b14de..7a52f90 100644 --- a/mcspring-api/mcspring-subcommands/pom.xml +++ b/mcspring-api/mcspring-subcommands/pom.xml @@ -13,7 +13,7 @@ - ${project.parent.groupId} + in.kyle.mcspring mcspring-base @@ -23,7 +23,7 @@ in.kyle.mcspring mcspring-test - ${project.parent.version} + 0.0.9 test diff --git a/mcspring-api/mcspring-vault/pom.xml b/mcspring-api/mcspring-vault/pom.xml index 292c0b7..6c6d777 100644 --- a/mcspring-api/mcspring-vault/pom.xml +++ b/mcspring-api/mcspring-vault/pom.xml @@ -21,14 +21,6 @@ - - - jcenter - JCenter - https://jcenter.bintray.com/ - - - ${project.parent.groupId} @@ -61,18 +53,6 @@ org.jetbrains.dokka dokka-maven-plugin - 0.10.1 - - - pre-site - - dokka - - - - - html - diff --git a/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/vault/api/EconomyException.kt b/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/vault/economy/EconomyException.kt similarity index 68% rename from mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/vault/api/EconomyException.kt rename to mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/vault/economy/EconomyException.kt index ea20dd8..66e07f6 100644 --- a/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/vault/api/EconomyException.kt +++ b/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/vault/economy/EconomyException.kt @@ -1,3 +1,3 @@ -package `in`.kyle.mcspring.vault.api +package `in`.kyle.mcspring.vault.economy class EconomyException internal constructor(message: String) : RuntimeException(message) diff --git a/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/vault/api/EconomyService.kt b/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/vault/economy/EconomyService.kt similarity index 97% rename from mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/vault/api/EconomyService.kt rename to mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/vault/economy/EconomyService.kt index 05a6c4c..96fbef5 100644 --- a/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/vault/api/EconomyService.kt +++ b/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/vault/economy/EconomyService.kt @@ -1,4 +1,4 @@ -package `in`.kyle.mcspring.vault.api +package `in`.kyle.mcspring.vault.economy import org.bukkit.OfflinePlayer import java.math.BigDecimal diff --git a/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/vault/impl/VaultEconomyService.kt b/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/vault/economy/VaultEconomyService.kt similarity index 94% rename from mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/vault/impl/VaultEconomyService.kt rename to mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/vault/economy/VaultEconomyService.kt index 252b446..eb56462 100644 --- a/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/vault/impl/VaultEconomyService.kt +++ b/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/vault/economy/VaultEconomyService.kt @@ -1,8 +1,6 @@ -package `in`.kyle.mcspring.vault.impl +package `in`.kyle.mcspring.vault.economy import `in`.kyle.mcspring.annotation.PluginDepend -import `in`.kyle.mcspring.vault.api.EconomyException -import `in`.kyle.mcspring.vault.api.EconomyService import net.milkbowl.vault.economy.Economy import net.milkbowl.vault.economy.EconomyResponse import org.bukkit.OfflinePlayer diff --git a/pom.xml b/pom.xml index a315bc2..005694a 100644 --- a/pom.xml +++ b/pom.xml @@ -24,6 +24,7 @@ ${java.version} 1.15.1-R0.1-SNAPSHOT 2.2.4.RELEASE + 0.20.0 @@ -81,6 +82,18 @@ + + + jcenter + JCenter + https://jcenter.bintray.com/ + + + kotlinx + https://kotlin.bintray.com/kotlinx + + + @@ -165,6 +178,14 @@ + + org.jetbrains.dokka + dokka-maven-plugin + + + io.github.javaeden.orchid + orchid-maven-plugin + @@ -231,6 +252,57 @@ + + org.jetbrains.dokka + dokka-maven-plugin + 0.10.1 + + + pre-site + + dokka + javadoc + + + + + html + true + false + + + + io.github.javaeden.orchid + orchid-maven-plugin + ${orchid.version} + + + io.github.javaeden.orchid + OrchidDocs + ${orchid.version} + + + io.github.javaeden.orchid + OrchidKotlindoc + ${orchid.version} + + + io.github.javaeden.orchid + OrchidPluginDocs + ${orchid.version} + + + + + Editorial + ${project.version} + https://kyle.github.io/project + + + + build + + diff --git a/src/orchid/resources/changelog/1.0.0.md b/src/orchid/resources/changelog/1.0.0.md new file mode 100644 index 0000000..12de9a0 --- /dev/null +++ b/src/orchid/resources/changelog/1.0.0.md @@ -0,0 +1 @@ +new stuff lol diff --git a/src/orchid/resources/config.yml b/src/orchid/resources/config.yml new file mode 100644 index 0000000..b474fff --- /dev/null +++ b/src/orchid/resources/config.yml @@ -0,0 +1,38 @@ +site: + about: + siteName: mcspring + siteDescription: Add Spring to any plugin +Editorial: + social: + github: 'kylepls/mcspring' + legacySearch: false + metaComponents: + - type: 'orchidSearch' + menu: + - type: 'separator' + title: 'Wiki' + - type: 'wiki' + - type: 'page' + itemId: 'Changelog' + - type: 'separator' + title: 'API Docs' + - type: 'sourcedocPages' + moduleType: 'kotlindoc' + node: 'classes' + asSubmenu: true + submenuTitle: 'Classes' + - type: 'sourcedocPages' + moduleType: 'kotlindoc' + node: 'packages' + asSubmenu: true + submenuTitle: 'Packages' + +kotlindoc: + sourceDirs: + - './../../../../app/src/main/kotlin' + sourcePages: + menu: + - type: 'sourcedocPageLinks' + moduleType: 'kotlindoc' + itemTitleType: 'SIGNATURE' + includeItems: true diff --git a/src/orchid/resources/homepage.md b/src/orchid/resources/homepage.md new file mode 100644 index 0000000..7678aae --- /dev/null +++ b/src/orchid/resources/homepage.md @@ -0,0 +1,5 @@ +--- +components: + - type: 'pageContent' + - type: 'readme' +--- diff --git a/src/orchid/resources/pages/changelog.md b/src/orchid/resources/pages/changelog.md new file mode 100644 index 0000000..6463454 --- /dev/null +++ b/src/orchid/resources/pages/changelog.md @@ -0,0 +1,4 @@ +--- +components: + - type: changelog +--- diff --git a/src/orchid/resources/wiki/summary.md b/src/orchid/resources/wiki/summary.md new file mode 100644 index 0000000..54e757a --- /dev/null +++ b/src/orchid/resources/wiki/summary.md @@ -0,0 +1,6 @@ +- [Installation](installation.md) +- [Basic Configuration](configuration.md) +- [Features](features.md) + - [Feature One](features/one.md) + - [Feature Two](features/two.md) +- [Extending](extending.md) From ad23d9f2b4f01dc21aa402b0a5573bd94786831c Mon Sep 17 00:00:00 2001 From: kylepls Date: Wed, 13 May 2020 13:00:11 -0600 Subject: [PATCH 21/54] start orchid --- .gitignore | 2 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 55190 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 172 ++++++++++ gradlew.bat | 84 +++++ mcspring-api/mcspring-base/build.gradle.kts | 13 + mcspring-api/mcspring-base/pom.xml | 76 ----- .../in/kyle/mcspring/SpringPlugin.kt | 2 +- .../in/kyle/mcspring/SpringSpigotSupport.kt | 0 .../kyle/mcspring/annotation/PluginDepend.kt | 0 .../command/BukkitCommandRegistration.kt | 0 .../in/kyle/mcspring/command/Command.kt | 0 .../mcspring/command/CommandController.kt | 0 .../mcspring/command/CommandRegistration.kt | 0 .../kyle/mcspring/command/CommandResolver.kt | 0 .../kyle/mcspring/command/CommandScanner.kt | 0 .../mcspring/command/ParameterResolver.kt | 0 .../mcspring/command/SimpleCommandFactory.kt | 0 .../mcspring/command/SimpleMethodInjection.kt | 0 .../command/SimpleSpringParameterResolver.kt | 0 .../mcspring/event/EventHandlerSupport.kt | 0 .../in/kyle/mcspring/event/EventService.kt | 0 .../scheduler/ScheduledAnnotationSupport.kt | 0 .../mcspring/scheduler/SchedulerService.kt | 0 .../in/kyle/mcspring/util/SpringScanner.kt | 2 +- .../mcspring-jar-loader/build.gradle.kts | 3 + mcspring-api/mcspring-jar-loader/pom.xml | 20 -- .../boot/loader/mcspring/McSpringLoader.kt | 0 .../mcspring-subcommands/build.gradle.kts | 9 + mcspring-api/mcspring-subcommands/pom.xml | 30 -- .../subcommands/PluginCommandResolver.kt | 0 .../plugincommand/api/PluginCommand.kt | 0 .../plugincommand/impl/PluginCommandBase.kt | 0 .../impl/PluginCommandExecutors.kt | 0 .../plugincommand/impl/PluginCommandImpl.kt | 0 .../impl/PluginCommandRequires.kt | 0 .../plugincommand/impl/PluginCommandWith.kt | 0 .../impl/javasupport/HighIQExecutors.kt | 0 .../PluginCommandExecutorsJavaSupport.kt | 0 .../PluginCommandWithJavaSupport.kt | 0 .../subcommands/tab/TabCommandFactory.kt | 0 .../mcspring/subcommands/tab/TabDiscovery.kt | 0 mcspring-api/mcspring-test/build.gradle.kts | 8 + .../in/kyle/mcspring/test/MCSpringTest.kt | 0 .../kyle/mcspring/test/SpringSpigotSupport.kt | 0 .../test/command/TestCommandExecutor.kt | 0 .../test/command/TestCommandRegistration.kt | 0 mcspring-api/mcspring-vault/build.gradle.kts | 15 + mcspring-api/mcspring-vault/pom.xml | 59 ---- .../vault/economy/EconomyException.kt | 0 .../mcspring/vault/economy/EconomyService.kt | 0 .../vault/economy/VaultEconomyService.kt | 0 mcspring-api/pom.xml | 80 ----- .../mcspring-archetype/build.gradle.kts | 3 + .../mcspring-maven-plugin/build.gradle.kts | 11 + .../mcspring-plugin-layout/build.gradle.kts | 4 + .../kyle/mcspring/layout/McSpringLayout.java | 0 .../layout/McSpringLayoutFactory.java | 0 .../mcspring-plugin-manager/build.gradle.kts | 5 + .../mcspring-plugin-manager/pom.xml | 36 -- .../mcspring/manager/commands/CommandAbout.kt | 0 .../manager/commands/CommandClassLoader.kt | 0 .../manager/commands/CommandGamemode.kt | 0 .../mcspring/manager/commands/CommandHeal.kt | 0 .../mcspring/manager/commands/CommandOp.kt | 0 .../manager/commands/CommandPlugin.kt | 0 .../mcspring/manager/commands/CommandSpeed.kt | 0 .../controller/BukkitPluginUnloader.kt | 0 .../manager/controller/LogFileController.kt | 0 .../manager/controller/PluginController.kt | 0 mcspring-build/pom.xml | 69 ---- mcspring-examples/pom.xml | 30 -- .../simple-factions-addon/build.gradle.kts | 4 + .../simple-factions-addon/pom.xml | 76 ----- .../java/test/other/stats/FactionsStats.java | 31 -- .../kotlin/test/other/stats/FactionsStats.kt | 25 ++ .../simple-factions/build.gradle.kts | 4 + mcspring-examples/simple-factions/pom.xml | 31 -- .../org/example/factions/api/Faction.java | 0 .../org/example/factions/api/FactionsApi.java | 0 .../factions/commands/FactionCommand.java | 0 .../controller/FactionsController.java | 0 pom.xml | 309 ------------------ src/orchid/resources/config.yml | 30 +- 84 files changed, 390 insertions(+), 859 deletions(-) create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat create mode 100644 mcspring-api/mcspring-base/build.gradle.kts delete mode 100644 mcspring-api/mcspring-base/pom.xml rename mcspring-api/mcspring-base/src/main/{java => kotlin}/in/kyle/mcspring/SpringPlugin.kt (98%) rename mcspring-api/mcspring-base/src/main/{java => kotlin}/in/kyle/mcspring/SpringSpigotSupport.kt (100%) rename mcspring-api/mcspring-base/src/main/{java => kotlin}/in/kyle/mcspring/annotation/PluginDepend.kt (100%) rename mcspring-api/mcspring-base/src/main/{java => kotlin}/in/kyle/mcspring/command/BukkitCommandRegistration.kt (100%) rename mcspring-api/mcspring-base/src/main/{java => kotlin}/in/kyle/mcspring/command/Command.kt (100%) rename mcspring-api/mcspring-base/src/main/{java => kotlin}/in/kyle/mcspring/command/CommandController.kt (100%) rename mcspring-api/mcspring-base/src/main/{java => kotlin}/in/kyle/mcspring/command/CommandRegistration.kt (100%) rename mcspring-api/mcspring-base/src/main/{java => kotlin}/in/kyle/mcspring/command/CommandResolver.kt (100%) rename mcspring-api/mcspring-base/src/main/{java => kotlin}/in/kyle/mcspring/command/CommandScanner.kt (100%) rename mcspring-api/mcspring-base/src/main/{java => kotlin}/in/kyle/mcspring/command/ParameterResolver.kt (100%) rename mcspring-api/mcspring-base/src/main/{java => kotlin}/in/kyle/mcspring/command/SimpleCommandFactory.kt (100%) rename mcspring-api/mcspring-base/src/main/{java => kotlin}/in/kyle/mcspring/command/SimpleMethodInjection.kt (100%) rename mcspring-api/mcspring-base/src/main/{java => kotlin}/in/kyle/mcspring/command/SimpleSpringParameterResolver.kt (100%) rename mcspring-api/mcspring-base/src/main/{java => kotlin}/in/kyle/mcspring/event/EventHandlerSupport.kt (100%) rename mcspring-api/mcspring-base/src/main/{java => kotlin}/in/kyle/mcspring/event/EventService.kt (100%) rename mcspring-api/mcspring-base/src/main/{java => kotlin}/in/kyle/mcspring/scheduler/ScheduledAnnotationSupport.kt (100%) rename mcspring-api/mcspring-base/src/main/{java => kotlin}/in/kyle/mcspring/scheduler/SchedulerService.kt (100%) rename mcspring-api/mcspring-base/src/main/{java => kotlin}/in/kyle/mcspring/util/SpringScanner.kt (96%) create mode 100644 mcspring-api/mcspring-jar-loader/build.gradle.kts delete mode 100644 mcspring-api/mcspring-jar-loader/pom.xml rename mcspring-api/mcspring-jar-loader/src/main/{java => kotlin}/org/springframework/boot/loader/mcspring/McSpringLoader.kt (100%) create mode 100644 mcspring-api/mcspring-subcommands/build.gradle.kts delete mode 100644 mcspring-api/mcspring-subcommands/pom.xml rename mcspring-api/mcspring-subcommands/src/main/{java => kotlin}/in/kyle/mcspring/subcommands/PluginCommandResolver.kt (100%) rename mcspring-api/mcspring-subcommands/src/main/{java => kotlin}/in/kyle/mcspring/subcommands/plugincommand/api/PluginCommand.kt (100%) rename mcspring-api/mcspring-subcommands/src/main/{java => kotlin}/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandBase.kt (100%) rename mcspring-api/mcspring-subcommands/src/main/{java => kotlin}/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandExecutors.kt (100%) rename mcspring-api/mcspring-subcommands/src/main/{java => kotlin}/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandImpl.kt (100%) rename mcspring-api/mcspring-subcommands/src/main/{java => kotlin}/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandRequires.kt (100%) rename mcspring-api/mcspring-subcommands/src/main/{java => kotlin}/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandWith.kt (100%) rename mcspring-api/mcspring-subcommands/src/main/{java => kotlin}/in/kyle/mcspring/subcommands/plugincommand/impl/javasupport/HighIQExecutors.kt (100%) rename mcspring-api/mcspring-subcommands/src/main/{java => kotlin}/in/kyle/mcspring/subcommands/plugincommand/impl/javasupport/PluginCommandExecutorsJavaSupport.kt (100%) rename mcspring-api/mcspring-subcommands/src/main/{java => kotlin}/in/kyle/mcspring/subcommands/plugincommand/impl/javasupport/PluginCommandWithJavaSupport.kt (100%) rename mcspring-api/mcspring-subcommands/src/main/{java => kotlin}/in/kyle/mcspring/subcommands/tab/TabCommandFactory.kt (100%) rename mcspring-api/mcspring-subcommands/src/main/{java => kotlin}/in/kyle/mcspring/subcommands/tab/TabDiscovery.kt (100%) create mode 100644 mcspring-api/mcspring-test/build.gradle.kts rename mcspring-api/mcspring-test/src/main/{java => kotlin}/in/kyle/mcspring/test/MCSpringTest.kt (100%) rename mcspring-api/mcspring-test/src/main/{java => kotlin}/in/kyle/mcspring/test/SpringSpigotSupport.kt (100%) rename mcspring-api/mcspring-test/src/main/{java => kotlin}/in/kyle/mcspring/test/command/TestCommandExecutor.kt (100%) rename mcspring-api/mcspring-test/src/main/{java => kotlin}/in/kyle/mcspring/test/command/TestCommandRegistration.kt (100%) create mode 100644 mcspring-api/mcspring-vault/build.gradle.kts delete mode 100644 mcspring-api/mcspring-vault/pom.xml rename mcspring-api/mcspring-vault/src/main/{java => kotlin}/in/kyle/mcspring/vault/economy/EconomyException.kt (100%) rename mcspring-api/mcspring-vault/src/main/{java => kotlin}/in/kyle/mcspring/vault/economy/EconomyService.kt (100%) rename mcspring-api/mcspring-vault/src/main/{java => kotlin}/in/kyle/mcspring/vault/economy/VaultEconomyService.kt (100%) delete mode 100644 mcspring-api/pom.xml create mode 100644 mcspring-build/mcspring-archetype/build.gradle.kts create mode 100644 mcspring-build/mcspring-maven-plugin/build.gradle.kts create mode 100644 mcspring-build/mcspring-plugin-layout/build.gradle.kts rename mcspring-build/mcspring-plugin-layout/src/main/{java => kotlin}/in/kyle/mcspring/layout/McSpringLayout.java (100%) rename mcspring-build/mcspring-plugin-layout/src/main/{java => kotlin}/in/kyle/mcspring/layout/McSpringLayoutFactory.java (100%) create mode 100644 mcspring-build/mcspring-plugin-manager/build.gradle.kts delete mode 100644 mcspring-build/mcspring-plugin-manager/pom.xml rename mcspring-build/mcspring-plugin-manager/src/main/{java => kotlin}/in/kyle/mcspring/manager/commands/CommandAbout.kt (100%) rename mcspring-build/mcspring-plugin-manager/src/main/{java => kotlin}/in/kyle/mcspring/manager/commands/CommandClassLoader.kt (100%) rename mcspring-build/mcspring-plugin-manager/src/main/{java => kotlin}/in/kyle/mcspring/manager/commands/CommandGamemode.kt (100%) rename mcspring-build/mcspring-plugin-manager/src/main/{java => kotlin}/in/kyle/mcspring/manager/commands/CommandHeal.kt (100%) rename mcspring-build/mcspring-plugin-manager/src/main/{java => kotlin}/in/kyle/mcspring/manager/commands/CommandOp.kt (100%) rename mcspring-build/mcspring-plugin-manager/src/main/{java => kotlin}/in/kyle/mcspring/manager/commands/CommandPlugin.kt (100%) rename mcspring-build/mcspring-plugin-manager/src/main/{java => kotlin}/in/kyle/mcspring/manager/commands/CommandSpeed.kt (100%) rename mcspring-build/mcspring-plugin-manager/src/main/{java => kotlin}/in/kyle/mcspring/manager/controller/BukkitPluginUnloader.kt (100%) rename mcspring-build/mcspring-plugin-manager/src/main/{java => kotlin}/in/kyle/mcspring/manager/controller/LogFileController.kt (100%) rename mcspring-build/mcspring-plugin-manager/src/main/{java => kotlin}/in/kyle/mcspring/manager/controller/PluginController.kt (100%) delete mode 100644 mcspring-build/pom.xml delete mode 100644 mcspring-examples/pom.xml create mode 100644 mcspring-examples/simple-factions-addon/build.gradle.kts delete mode 100644 mcspring-examples/simple-factions-addon/pom.xml delete mode 100644 mcspring-examples/simple-factions-addon/src/main/java/test/other/stats/FactionsStats.java create mode 100644 mcspring-examples/simple-factions-addon/src/main/kotlin/test/other/stats/FactionsStats.kt create mode 100644 mcspring-examples/simple-factions/build.gradle.kts delete mode 100644 mcspring-examples/simple-factions/pom.xml rename mcspring-examples/simple-factions/src/main/{java => kotlin}/org/example/factions/api/Faction.java (100%) rename mcspring-examples/simple-factions/src/main/{java => kotlin}/org/example/factions/api/FactionsApi.java (100%) rename mcspring-examples/simple-factions/src/main/{java => kotlin}/org/example/factions/commands/FactionCommand.java (100%) rename mcspring-examples/simple-factions/src/main/{java => kotlin}/org/example/factions/controller/FactionsController.java (100%) delete mode 100644 pom.xml diff --git a/.gitignore b/.gitignore index 6742be5..9c421ec 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ *.iml dependency-reduced-pom.xml **/target/** +**/build/** +.gradle pom.xml.versionsBackup **/spigot/** !**/archetype-resources/spigot/** diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..87b738cbd051603d91cc39de6cb000dd98fe6b02 GIT binary patch literal 55190 zcmafaW0WS*vSoFbZQHhO+s0S6%`V%vZQJa!ZQHKus_B{g-pt%P_q|ywBQt-*Stldc z$+IJ3?^KWm27v+sf`9-50uuadKtMnL*BJ;1^6ynvR7H?hQcjE>7)art9Bu0Pcm@7C z@c%WG|JzYkP)<@zR9S^iR_sA`azaL$mTnGKnwDyMa;8yL_0^>Ba^)phg0L5rOPTbm7g*YIRLg-2^{qe^`rb!2KqS zk~5wEJtTdD?)3+}=eby3x6%i)sb+m??NHC^u=tcG8p$TzB<;FL(WrZGV&cDQb?O0GMe6PBV=V z?tTO*5_HTW$xea!nkc~Cnx#cL_rrUGWPRa6l+A{aiMY=<0@8y5OC#UcGeE#I>nWh}`#M#kIn-$A;q@u-p71b#hcSItS!IPw?>8 zvzb|?@Ahb22L(O4#2Sre&l9H(@TGT>#Py)D&eW-LNb!=S;I`ZQ{w;MaHW z#to!~TVLgho_Pm%zq@o{K3Xq?I|MVuVSl^QHnT~sHlrVxgsqD-+YD?Nz9@HA<;x2AQjxP)r6Femg+LJ-*)k%EZ}TTRw->5xOY z9#zKJqjZgC47@AFdk1$W+KhTQJKn7e>A&?@-YOy!v_(}GyV@9G#I?bsuto4JEp;5|N{orxi_?vTI4UF0HYcA( zKyGZ4<7Fk?&LZMQb6k10N%E*$gr#T&HsY4SPQ?yerqRz5c?5P$@6dlD6UQwZJ*Je9 z7n-@7!(OVdU-mg@5$D+R%gt82Lt%&n6Yr4=|q>XT%&^z_D*f*ug8N6w$`woqeS-+#RAOfSY&Rz z?1qYa5xi(7eTCrzCFJfCxc%j{J}6#)3^*VRKF;w+`|1n;Xaojr2DI{!<3CaP`#tXs z*`pBQ5k@JLKuCmovFDqh_`Q;+^@t_;SDm29 zCNSdWXbV?9;D4VcoV`FZ9Ggrr$i<&#Dx3W=8>bSQIU_%vf)#(M2Kd3=rN@^d=QAtC zI-iQ;;GMk|&A++W5#hK28W(YqN%?!yuW8(|Cf`@FOW5QbX|`97fxmV;uXvPCqxBD zJ9iI37iV)5TW1R+fV16y;6}2tt~|0J3U4E=wQh@sx{c_eu)t=4Yoz|%Vp<#)Qlh1V z0@C2ZtlT>5gdB6W)_bhXtcZS)`9A!uIOa`K04$5>3&8An+i9BD&GvZZ=7#^r=BN=k za+=Go;qr(M)B~KYAz|<^O3LJON}$Q6Yuqn8qu~+UkUKK~&iM%pB!BO49L+?AL7N7o z(OpM(C-EY753=G=WwJHE`h*lNLMNP^c^bBk@5MyP5{v7x>GNWH>QSgTe5 z!*GPkQ(lcbEs~)4ovCu!Zt&$${9$u(<4@9%@{U<-ksAqB?6F`bQ;o-mvjr)Jn7F&j$@`il1Mf+-HdBs<-`1FahTxmPMMI)@OtI&^mtijW6zGZ67O$UOv1Jj z;a3gmw~t|LjPkW3!EZ=)lLUhFzvO;Yvj9g`8hm%6u`;cuek_b-c$wS_0M4-N<@3l|88 z@V{Sd|M;4+H6guqMm4|v=C6B7mlpP(+It%0E;W`dxMOf9!jYwWj3*MRk`KpS_jx4c z=hrKBkFK;gq@;wUV2eqE3R$M+iUc+UD0iEl#-rECK+XmH9hLKrC={j@uF=f3UiceB zU5l$FF7#RKjx+6!JHMG5-!@zI-eG=a-!Bs^AFKqN_M26%cIIcSs61R$yuq@5a3c3& z4%zLs!g}+C5%`ja?F`?5-og0lv-;(^e<`r~p$x%&*89_Aye1N)9LNVk?9BwY$Y$$F^!JQAjBJvywXAesj7lTZ)rXuxv(FFNZVknJha99lN=^h`J2> zl5=~(tKwvHHvh|9-41@OV`c;Ws--PE%{7d2sLNbDp;A6_Ka6epzOSFdqb zBa0m3j~bT*q1lslHsHqaHIP%DF&-XMpCRL(v;MV#*>mB^&)a=HfLI7efblG z(@hzN`|n+oH9;qBklb=d^S0joHCsArnR1-h{*dIUThik>ot^!6YCNjg;J_i3h6Rl0ji)* zo(tQ~>xB!rUJ(nZjCA^%X;)H{@>uhR5|xBDA=d21p@iJ!cH?+%U|VSh2S4@gv`^)^ zNKD6YlVo$%b4W^}Rw>P1YJ|fTb$_(7C;hH+ z1XAMPb6*p^h8)e5nNPKfeAO}Ik+ZN_`NrADeeJOq4Ak;sD~ zTe77no{Ztdox56Xi4UE6S7wRVxJzWxKj;B%v7|FZ3cV9MdfFp7lWCi+W{}UqekdpH zdO#eoOuB3Fu!DU`ErfeoZWJbWtRXUeBzi zBTF-AI7yMC^ntG+8%mn(I6Dw}3xK8v#Ly{3w3_E?J4(Q5JBq~I>u3!CNp~Ekk&YH` z#383VO4O42NNtcGkr*K<+wYZ>@|sP?`AQcs5oqX@-EIqgK@Pmp5~p6O6qy4ml~N{D z{=jQ7k(9!CM3N3Vt|u@%ssTw~r~Z(}QvlROAkQQ?r8OQ3F0D$aGLh zny+uGnH5muJ<67Z=8uilKvGuANrg@s3Vu_lU2ajb?rIhuOd^E@l!Kl0hYIxOP1B~Q zggUmXbh$bKL~YQ#!4fos9UUVG#}HN$lIkM<1OkU@r>$7DYYe37cXYwfK@vrHwm;pg zbh(hEU|8{*d$q7LUm+x&`S@VbW*&p-sWrplWnRM|I{P;I;%U`WmYUCeJhYc|>5?&& zj}@n}w~Oo=l}iwvi7K6)osqa;M8>fRe}>^;bLBrgA;r^ZGgY@IC^ioRmnE&H4)UV5 zO{7egQ7sBAdoqGsso5q4R(4$4Tjm&&C|7Huz&5B0wXoJzZzNc5Bt)=SOI|H}+fbit z-PiF5(NHSy>4HPMrNc@SuEMDuKYMQ--G+qeUPqO_9mOsg%1EHpqoX^yNd~~kbo`cH zlV0iAkBFTn;rVb>EK^V6?T~t~3vm;csx+lUh_%ROFPy0(omy7+_wYjN!VRDtwDu^h4n|xpAMsLepm% zggvs;v8+isCW`>BckRz1MQ=l>K6k^DdT`~sDXTWQ<~+JtY;I~I>8XsAq3yXgxe>`O zZdF*{9@Z|YtS$QrVaB!8&`&^W->_O&-JXn1n&~}o3Z7FL1QE5R*W2W@=u|w~7%EeC1aRfGtJWxImfY-D3t!!nBkWM> zafu>^Lz-ONgT6ExjV4WhN!v~u{lt2-QBN&UxwnvdH|I%LS|J-D;o>@@sA62@&yew0 z)58~JSZP!(lX;da!3`d)D1+;K9!lyNlkF|n(UduR-%g>#{`pvrD^ClddhJyfL7C-(x+J+9&7EsC~^O`&}V%)Ut8^O_7YAXPDpzv8ir4 zl`d)(;imc6r16k_d^)PJZ+QPxxVJS5e^4wX9D=V2zH&wW0-p&OJe=}rX`*->XT=;_qI&)=WHkYnZx6bLoUh_)n-A}SF_ z9z7agNTM5W6}}ui=&Qs@pO5$zHsOWIbd_&%j^Ok5PJ3yUWQw*i4*iKO)_er2CDUME ztt+{Egod~W-fn^aLe)aBz)MOc_?i-stTj}~iFk7u^-gGSbU;Iem06SDP=AEw9SzuF zeZ|hKCG3MV(z_PJg0(JbqTRf4T{NUt%kz&}4S`)0I%}ZrG!jgW2GwP=WTtkWS?DOs znI9LY!dK+1_H0h+i-_~URb^M;4&AMrEO_UlDV8o?E>^3x%ZJyh$JuDMrtYL8|G3If zPf2_Qb_W+V?$#O; zydKFv*%O;Y@o_T_UAYuaqx1isMKZ^32JtgeceA$0Z@Ck0;lHbS%N5)zzAW9iz; z8tTKeK7&qw!8XVz-+pz>z-BeIzr*#r0nB^cntjQ9@Y-N0=e&ZK72vlzX>f3RT@i7@ z=z`m7jNk!9%^xD0ug%ptZnM>F;Qu$rlwo}vRGBIymPL)L|x}nan3uFUw(&N z24gdkcb7!Q56{0<+zu zEtc5WzG2xf%1<@vo$ZsuOK{v9gx^0`gw>@h>ZMLy*h+6ueoie{D#}}` zK2@6Xxq(uZaLFC%M!2}FX}ab%GQ8A0QJ?&!vaI8Gv=vMhd);6kGguDmtuOElru()) zuRk&Z{?Vp!G~F<1#s&6io1`poBqpRHyM^p;7!+L??_DzJ8s9mYFMQ0^%_3ft7g{PD zZd}8E4EV}D!>F?bzcX=2hHR_P`Xy6?FOK)mCj)Ym4s2hh z0OlOdQa@I;^-3bhB6mpw*X5=0kJv8?#XP~9){G-+0ST@1Roz1qi8PhIXp1D$XNqVG zMl>WxwT+K`SdO1RCt4FWTNy3!i?N>*-lbnn#OxFJrswgD7HjuKpWh*o@QvgF&j+CT z{55~ZsUeR1aB}lv#s_7~+9dCix!5(KR#c?K?e2B%P$fvrsZxy@GP#R#jwL{y#Ld$} z7sF>QT6m|}?V;msb?Nlohj7a5W_D$y+4O6eI;Zt$jVGymlzLKscqer9#+p2$0It&u zWY!dCeM6^B^Z;ddEmhi?8`scl=Lhi7W%2|pT6X6^%-=q90DS(hQ-%c+E*ywPvmoF(KqDoW4!*gmQIklm zk#!GLqv|cs(JRF3G?=AYY19{w@~`G3pa z@xR9S-Hquh*&5Yas*VI};(%9%PADn`kzm zeWMJVW=>>wap*9|R7n#!&&J>gq04>DTCMtj{P^d12|2wXTEKvSf?$AvnE!peqV7i4 zE>0G%CSn%WCW1yre?yi9*aFP{GvZ|R4JT}M%x_%Hztz2qw?&28l&qW<6?c6ym{f$d z5YCF+k#yEbjCN|AGi~-NcCG8MCF1!MXBFL{#7q z)HO+WW173?kuI}^Xat;Q^gb4Hi0RGyB}%|~j8>`6X4CPo+|okMbKy9PHkr58V4bX6<&ERU)QlF8%%huUz&f+dwTN|tk+C&&o@Q1RtG`}6&6;ncQuAcfHoxd5AgD7`s zXynq41Y`zRSiOY@*;&1%1z>oNcWTV|)sjLg1X8ijg1Y zbIGL0X*Sd}EXSQ2BXCKbJmlckY(@EWn~Ut2lYeuw1wg?hhj@K?XB@V_ZP`fyL~Yd3n3SyHU-RwMBr6t-QWE5TinN9VD4XVPU; zonIIR!&pGqrLQK)=#kj40Im%V@ij0&Dh0*s!lnTw+D`Dt-xmk-jmpJv$1-E-vfYL4 zqKr#}Gm}~GPE+&$PI@4ag@=M}NYi7Y&HW82Q`@Y=W&PE31D110@yy(1vddLt`P%N^ z>Yz195A%tnt~tvsSR2{m!~7HUc@x<&`lGX1nYeQUE(%sphTi>JsVqSw8xql*Ys@9B z>RIOH*rFi*C`ohwXjyeRBDt8p)-u{O+KWP;$4gg||%*u{$~yEj+Al zE(hAQRQ1k7MkCq9s4^N3ep*$h^L%2Vq?f?{+cicpS8lo)$Cb69b98au+m2J_e7nYwID0@`M9XIo1H~|eZFc8Hl!qly612ADCVpU zY8^*RTMX(CgehD{9v|^9vZ6Rab`VeZ2m*gOR)Mw~73QEBiktViBhR!_&3l$|be|d6 zupC`{g89Y|V3uxl2!6CM(RNpdtynaiJ~*DqSTq9Mh`ohZnb%^3G{k;6%n18$4nAqR zjPOrP#-^Y9;iw{J@XH9=g5J+yEVh|e=4UeY<^65`%gWtdQ=-aqSgtywM(1nKXh`R4 zzPP&7r)kv_uC7X9n=h=!Zrf<>X=B5f<9~Q>h#jYRD#CT7D~@6@RGNyO-#0iq0uHV1 zPJr2O4d_xLmg2^TmG7|dpfJ?GGa`0|YE+`2Rata9!?$j#e9KfGYuLL(*^z z!SxFA`$qm)q-YKh)WRJZ@S+-sD_1E$V?;(?^+F3tVcK6 z2fE=8hV*2mgiAbefU^uvcM?&+Y&E}vG=Iz!%jBF7iv){lyC`)*yyS~D8k+Mx|N3bm zI~L~Z$=W9&`x)JnO;8c>3LSDw!fzN#X3qi|0`sXY4?cz{*#xz!kvZ9bO=K3XbN z5KrgN=&(JbXH{Wsu9EdmQ-W`i!JWEmfI;yVTT^a-8Ch#D8xf2dtyi?7p z%#)W3n*a#ndFpd{qN|+9Jz++AJQO#-Y7Z6%*%oyEP5zs}d&kKIr`FVEY z;S}@d?UU=tCdw~EJ{b}=9x}S2iv!!8<$?d7VKDA8h{oeD#S-$DV)-vPdGY@x08n)@ zag?yLF_E#evvRTj4^CcrLvBL=fft&@HOhZ6Ng4`8ijt&h2y}fOTC~7GfJi4vpomA5 zOcOM)o_I9BKz}I`q)fu+Qnfy*W`|mY%LO>eF^a z;$)?T4F-(X#Q-m}!-k8L_rNPf`Mr<9IWu)f&dvt=EL+ESYmCvErd@8B9hd)afc(ZL94S z?rp#h&{7Ah5IJftK4VjATklo7@hm?8BX*~oBiz)jyc9FuRw!-V;Uo>p!CWpLaIQyt zAs5WN)1CCeux-qiGdmbIk8LR`gM+Qg=&Ve}w?zA6+sTL)abU=-cvU`3E?p5$Hpkxw znu0N659qR=IKnde*AEz_7z2pdi_Bh-sb3b=PdGO1Pdf_q2;+*Cx9YN7p_>rl``knY zRn%aVkcv1(W;`Mtp_DNOIECtgq%ufk-mu_<+Fu3Q17Tq4Rr(oeq)Yqk_CHA7LR@7@ zIZIDxxhS&=F2IQfusQ+Nsr%*zFK7S4g!U0y@3H^Yln|i;0a5+?RPG;ZSp6Tul>ezM z`40+516&719qT)mW|ArDSENle5hE2e8qY+zfeZoy12u&xoMgcP)4=&P-1Ib*-bAy` zlT?>w&B|ei-rCXO;sxo7*G;!)_p#%PAM-?m$JP(R%x1Hfas@KeaG%LO?R=lmkXc_MKZW}3f%KZ*rAN?HYvbu2L$ zRt_uv7~-IejlD1x;_AhwGXjB94Q=%+PbxuYzta*jw?S&%|qb=(JfJ?&6P=R7X zV%HP_!@-zO*zS}46g=J}#AMJ}rtWBr21e6hOn&tEmaM%hALH7nlm2@LP4rZ>2 zebe5aH@k!e?ij4Zwak#30|}>;`bquDQK*xmR=zc6vj0yuyC6+U=LusGnO3ZKFRpen z#pwzh!<+WBVp-!$MAc<0i~I%fW=8IO6K}bJ<-Scq>e+)951R~HKB?Mx2H}pxPHE@} zvqpq5j81_jtb_WneAvp<5kgdPKm|u2BdQx9%EzcCN&U{l+kbkhmV<1}yCTDv%&K^> zg;KCjwh*R1f_`6`si$h6`jyIKT7rTv5#k~x$mUyIw)_>Vr)D4fwIs@}{FSX|5GB1l z4vv;@oS@>Bu7~{KgUa_8eg#Lk6IDT2IY$41$*06{>>V;Bwa(-@N;ex4;D`(QK*b}{ z{#4$Hmt)FLqERgKz=3zXiV<{YX6V)lvYBr3V>N6ajeI~~hGR5Oe>W9r@sg)Na(a4- zxm%|1OKPN6^%JaD^^O~HbLSu=f`1px>RawOxLr+1b2^28U*2#h*W^=lSpSY4(@*^l z{!@9RSLG8Me&RJYLi|?$c!B0fP=4xAM4rerxX{xy{&i6=AqXueQAIBqO+pmuxy8Ib z4X^}r!NN3-upC6B#lt7&x0J;)nb9O~xjJMemm$_fHuP{DgtlU3xiW0UesTzS30L+U zQzDI3p&3dpONhd5I8-fGk^}@unluzu%nJ$9pzoO~Kk!>dLxw@M)M9?pNH1CQhvA`z zV;uacUtnBTdvT`M$1cm9`JrT3BMW!MNVBy%?@ZX%;(%(vqQAz<7I!hlDe|J3cn9=} zF7B;V4xE{Ss76s$W~%*$JviK?w8^vqCp#_G^jN0j>~Xq#Zru26e#l3H^{GCLEXI#n z?n~F-Lv#hU(bZS`EI9(xGV*jT=8R?CaK)t8oHc9XJ;UPY0Hz$XWt#QyLBaaz5+}xM zXk(!L_*PTt7gwWH*HLWC$h3Ho!SQ-(I||nn_iEC{WT3S{3V{8IN6tZ1C+DiFM{xlI zeMMk{o5;I6UvaC)@WKp9D+o?2Vd@4)Ue-nYci()hCCsKR`VD;hr9=vA!cgGL%3k^b(jADGyPi2TKr(JNh8mzlIR>n(F_hgiV(3@Ds(tjbNM7GoZ;T|3 zWzs8S`5PrA!9){jBJuX4y`f<4;>9*&NY=2Sq2Bp`M2(fox7ZhIDe!BaQUb@P(ub9D zlP8!p(AN&CwW!V&>H?yPFMJ)d5x#HKfwx;nS{Rr@oHqpktOg)%F+%1#tsPtq7zI$r zBo-Kflhq-=7_eW9B2OQv=@?|y0CKN77)N;z@tcg;heyW{wlpJ1t`Ap!O0`Xz{YHqO zI1${8Hag^r!kA<2_~bYtM=<1YzQ#GGP+q?3T7zYbIjN6Ee^V^b&9en$8FI*NIFg9G zPG$OXjT0Ku?%L7fat8Mqbl1`azf1ltmKTa(HH$Dqlav|rU{zP;Tbnk-XkGFQ6d+gi z-PXh?_kEJl+K98&OrmzgPIijB4!Pozbxd0H1;Usy!;V>Yn6&pu*zW8aYx`SC!$*ti zSn+G9p=~w6V(fZZHc>m|PPfjK6IN4(o=IFu?pC?+`UZAUTw!e`052{P=8vqT^(VeG z=psASIhCv28Y(;7;TuYAe>}BPk5Qg=8$?wZj9lj>h2kwEfF_CpK=+O6Rq9pLn4W)# zeXCKCpi~jsfqw7Taa0;!B5_C;B}e56W1s8@p*)SPzA;Fd$Slsn^=!_&!mRHV*Lmt| zBGIDPuR>CgS4%cQ4wKdEyO&Z>2aHmja;Pz+n|7(#l%^2ZLCix%>@_mbnyPEbyrHaz z>j^4SIv;ZXF-Ftzz>*t4wyq)ng8%0d;(Z_ExZ-cxwei=8{(br-`JYO(f23Wae_MqE z3@{Mlf^%M5G1SIN&en1*| zH~ANY1h3&WNsBy$G9{T=`kcxI#-X|>zLX2r*^-FUF+m0{k)n#GTG_mhG&fJfLj~K& zU~~6othMlvMm9<*SUD2?RD+R17|Z4mgR$L*R3;nBbo&Vm@39&3xIg;^aSxHS>}gwR zmzs?h8oPnNVgET&dx5^7APYx6Vv6eou07Zveyd+^V6_LzI$>ic+pxD_8s~ zC<}ucul>UH<@$KM zT4oI=62M%7qQO{}re-jTFqo9Z;rJKD5!X5$iwUsh*+kcHVhID08MB5cQD4TBWB(rI zuWc%CA}}v|iH=9gQ?D$1#Gu!y3o~p7416n54&Hif`U-cV?VrUMJyEqo_NC4#{puzU zzXEE@UppeeRlS9W*^N$zS`SBBi<@tT+<%3l@KhOy^%MWB9(A#*J~DQ;+MK*$rxo6f zcx3$3mcx{tly!q(p2DQrxcih|)0do_ZY77pyHGE#Q(0k*t!HUmmMcYFq%l$-o6%lS zDb49W-E?rQ#Hl``C3YTEdGZjFi3R<>t)+NAda(r~f1cT5jY}s7-2^&Kvo&2DLTPYP zhVVo-HLwo*vl83mtQ9)PR#VBg)FN}+*8c-p8j`LnNUU*Olm1O1Qqe62D#$CF#?HrM zy(zkX|1oF}Z=T#3XMLWDrm(|m+{1&BMxHY7X@hM_+cV$5-t!8HT(dJi6m9{ja53Yw z3f^`yb6Q;(e|#JQIz~B*=!-GbQ4nNL-NL z@^NWF_#w-Cox@h62;r^;Y`NX8cs?l^LU;5IWE~yvU8TqIHij!X8ydbLlT0gwmzS9} z@5BccG?vO;rvCs$mse1*ANi-cYE6Iauz$Fbn3#|ToAt5v7IlYnt6RMQEYLldva{~s zvr>1L##zmeoYgvIXJ#>bbuCVuEv2ZvZ8I~PQUN3wjP0UC)!U+wn|&`V*8?)` zMSCuvnuGec>QL+i1nCPGDAm@XSMIo?A9~C?g2&G8aNKjWd2pDX{qZ?04+2 zeyLw}iEd4vkCAWwa$ zbrHlEf3hfN7^1g~aW^XwldSmx1v~1z(s=1az4-wl} z`mM+G95*N*&1EP#u3}*KwNrPIgw8Kpp((rdEOO;bT1;6ea~>>sK+?!;{hpJ3rR<6UJb`O8P4@{XGgV%63_fs%cG8L zk9Fszbdo4tS$g0IWP1>t@0)E%-&9yj%Q!fiL2vcuL;90fPm}M==<>}Q)&sp@STFCY z^p!RzmN+uXGdtPJj1Y-khNyCb6Y$Vs>eZyW zPaOV=HY_T@FwAlleZCFYl@5X<<7%5DoO(7S%Lbl55?{2vIr_;SXBCbPZ(up;pC6Wx={AZL?shYOuFxLx1*>62;2rP}g`UT5+BHg(ju z&7n5QSvSyXbioB9CJTB#x;pexicV|9oaOpiJ9VK6EvKhl4^Vsa(p6cIi$*Zr0UxQ z;$MPOZnNae2Duuce~7|2MCfhNg*hZ9{+8H3?ts9C8#xGaM&sN;2lriYkn9W>&Gry! z3b(Xx1x*FhQkD-~V+s~KBfr4M_#0{`=Yrh90yj}Ph~)Nx;1Y^8<418tu!$1<3?T*~ z7Dl0P3Uok-7w0MPFQexNG1P5;y~E8zEvE49>$(f|XWtkW2Mj`udPn)pb%} zrA%wRFp*xvDgC767w!9`0vx1=q!)w!G+9(-w&p*a@WXg{?T&%;qaVcHo>7ca%KX$B z^7|KBPo<2;kM{2mRnF8vKm`9qGV%|I{y!pKm8B(q^2V;;x2r!1VJ^Zz8bWa)!-7a8 zSRf@dqEPlsj!7}oNvFFAA)75})vTJUwQ03hD$I*j6_5xbtd_JkE2`IJD_fQ;a$EkO z{fQ{~e%PKgPJsD&PyEvDmg+Qf&p*-qu!#;1k2r_(H72{^(Z)htgh@F?VIgK#_&eS- z$~(qInec>)XIkv@+{o6^DJLpAb>!d}l1DK^(l%#OdD9tKK6#|_R?-%0V!`<9Hj z3w3chDwG*SFte@>Iqwq`J4M&{aHXzyigT620+Vf$X?3RFfeTcvx_e+(&Q*z)t>c0e zpZH$1Z3X%{^_vylHVOWT6tno=l&$3 z9^eQ@TwU#%WMQaFvaYp_we%_2-9=o{+ck zF{cKJCOjpW&qKQquyp2BXCAP920dcrZ}T1@piukx_NY;%2W>@Wca%=Ch~x5Oj58Hv z;D-_ALOZBF(Mqbcqjd}P3iDbek#Dwzu`WRs`;hRIr*n0PV7vT+%Io(t}8KZ zpp?uc2eW!v28ipep0XNDPZt7H2HJ6oey|J3z!ng#1H~x_k%35P+Cp%mqXJ~cV0xdd z^4m5^K_dQ^Sg?$P`))ccV=O>C{Ds(C2WxX$LMC5vy=*44pP&)X5DOPYfqE${)hDg< z3hcG%U%HZ39=`#Ko4Uctg&@PQLf>?0^D|4J(_1*TFMOMB!Vv1_mnOq$BzXQdOGqgy zOp#LBZ!c>bPjY1NTXksZmbAl0A^Y&(%a3W-k>bE&>K?px5Cm%AT2E<&)Y?O*?d80d zgI5l~&Mve;iXm88Q+Fw7{+`PtN4G7~mJWR^z7XmYQ>uoiV!{tL)hp|= zS(M)813PM`d<501>{NqaPo6BZ^T{KBaqEVH(2^Vjeq zgeMeMpd*1tE@@);hGjuoVzF>Cj;5dNNwh40CnU+0DSKb~GEMb_# zT8Z&gz%SkHq6!;_6dQFYE`+b`v4NT7&@P>cA1Z1xmXy<2htaDhm@XXMp!g($ zw(7iFoH2}WR`UjqjaqOQ$ecNt@c|K1H1kyBArTTjLp%-M`4nzOhkfE#}dOpcd;b#suq8cPJ&bf5`6Tq>ND(l zib{VrPZ>{KuaIg}Y$W>A+nrvMg+l4)-@2jpAQ5h(Tii%Ni^-UPVg{<1KGU2EIUNGaXcEkOedJOusFT9X3%Pz$R+-+W+LlRaY-a$5r?4V zbPzgQl22IPG+N*iBRDH%l{Zh$fv9$RN1sU@Hp3m=M}{rX%y#;4(x1KR2yCO7Pzo>rw(67E{^{yUR`91nX^&MxY@FwmJJbyPAoWZ9Z zcBS$r)&ogYBn{DOtD~tIVJUiq|1foX^*F~O4hlLp-g;Y2wKLLM=?(r3GDqsPmUo*? zwKMEi*%f)C_@?(&&hk>;m07F$X7&i?DEK|jdRK=CaaNu-)pX>n3}@%byPKVkpLzBq z{+Py&!`MZ^4@-;iY`I4#6G@aWMv{^2VTH7|WF^u?3vsB|jU3LgdX$}=v7#EHRN(im zI(3q-eU$s~r=S#EWqa_2!G?b~ z<&brq1vvUTJH380=gcNntZw%7UT8tLAr-W49;9y^=>TDaTC|cKA<(gah#2M|l~j)w zY8goo28gj$n&zcNgqX1Qn6=<8?R0`FVO)g4&QtJAbW3G#D)uNeac-7cH5W#6i!%BH z=}9}-f+FrtEkkrQ?nkoMQ1o-9_b+&=&C2^h!&mWFga#MCrm85hW;)1pDt;-uvQG^D zntSB?XA*0%TIhtWDS!KcI}kp3LT>!(Nlc(lQN?k^bS8Q^GGMfo}^|%7s;#r+pybl@?KA++|FJ zr%se9(B|g*ERQU96az%@4gYrxRRxaM2*b}jNsG|0dQi;Rw{0WM0E>rko!{QYAJJKY z)|sX0N$!8d9E|kND~v|f>3YE|uiAnqbkMn)hu$if4kUkzKqoNoh8v|S>VY1EKmgO} zR$0UU2o)4i4yc1inx3}brso+sio{)gfbLaEgLahj8(_Z#4R-v) zglqwI%`dsY+589a8$Mu7#7_%kN*ekHupQ#48DIN^uhDxblDg3R1yXMr^NmkR z7J_NWCY~fhg}h!_aXJ#?wsZF$q`JH>JWQ9`jbZzOBpS`}-A$Vgkq7+|=lPx9H7QZG z8i8guMN+yc4*H*ANr$Q-3I{FQ-^;8ezWS2b8rERp9TMOLBxiG9J*g5=?h)mIm3#CGi4JSq1ohFrcrxx@`**K5%T}qbaCGldV!t zVeM)!U3vbf5FOy;(h08JnhSGxm)8Kqxr9PsMeWi=b8b|m_&^@#A3lL;bVKTBx+0v8 zLZeWAxJ~N27lsOT2b|qyp$(CqzqgW@tyy?CgwOe~^i;ZH zlL``i4r!>i#EGBNxV_P@KpYFQLz4Bdq{#zA&sc)*@7Mxsh9u%e6Ke`?5Yz1jkTdND zR8!u_yw_$weBOU}24(&^Bm|(dSJ(v(cBct}87a^X(v>nVLIr%%D8r|&)mi+iBc;B;x;rKq zd8*X`r?SZsTNCPQqoFOrUz8nZO?225Z#z(B!4mEp#ZJBzwd7jW1!`sg*?hPMJ$o`T zR?KrN6OZA1H{9pA;p0cSSu;@6->8aJm1rrO-yDJ7)lxuk#npUk7WNER1Wwnpy%u zF=t6iHzWU(L&=vVSSc^&D_eYP3TM?HN!Tgq$SYC;pSIPWW;zeNm7Pgub#yZ@7WPw#f#Kl)W4%B>)+8%gpfoH1qZ;kZ*RqfXYeGXJ_ zk>2otbp+1By`x^1V!>6k5v8NAK@T;89$`hE0{Pc@Q$KhG0jOoKk--Qx!vS~lAiypV zCIJ&6B@24`!TxhJ4_QS*S5;;Pk#!f(qIR7*(c3dN*POKtQe)QvR{O2@QsM%ujEAWEm) z+PM=G9hSR>gQ`Bv2(k}RAv2+$7qq(mU`fQ+&}*i%-RtSUAha>70?G!>?w%F(b4k!$ zvm;E!)2`I?etmSUFW7WflJ@8Nx`m_vE2HF#)_BiD#FaNT|IY@!uUbd4v$wTglIbIX zblRy5=wp)VQzsn0_;KdM%g<8@>#;E?vypTf=F?3f@SSdZ;XpX~J@l1;p#}_veWHp>@Iq_T z@^7|h;EivPYv1&u0~l9(a~>dV9Uw10QqB6Dzu1G~-l{*7IktljpK<_L8m0|7VV_!S zRiE{u97(%R-<8oYJ{molUd>vlGaE-C|^<`hppdDz<7OS13$#J zZ+)(*rZIDSt^Q$}CRk0?pqT5PN5TT`Ya{q(BUg#&nAsg6apPMhLTno!SRq1e60fl6GvpnwDD4N> z9B=RrufY8+g3_`@PRg+(+gs2(bd;5#{uTZk96CWz#{=&h9+!{_m60xJxC%r&gd_N! z>h5UzVX%_7@CUeAA1XFg_AF%(uS&^1WD*VPS^jcC!M2v@RHZML;e(H-=(4(3O&bX- zI6>usJOS+?W&^S&DL{l|>51ZvCXUKlH2XKJPXnHjs*oMkNM#ZDLx!oaM5(%^)5XaP zk6&+P16sA>vyFe9v`Cp5qnbE#r#ltR5E+O3!WnKn`56Grs2;sqr3r# zp@Zp<^q`5iq8OqOlJ`pIuyK@3zPz&iJ0Jcc`hDQ1bqos2;}O|$i#}e@ua*x5VCSx zJAp}+?Hz++tm9dh3Fvm_bO6mQo38al#>^O0g)Lh^&l82+&x)*<n7^Sw-AJo9tEzZDwyJ7L^i7|BGqHu+ea6(&7jKpBq>~V z8CJxurD)WZ{5D0?s|KMi=e7A^JVNM6sdwg@1Eg_+Bw=9j&=+KO1PG|y(mP1@5~x>d z=@c{EWU_jTSjiJl)d(>`qEJ;@iOBm}alq8;OK;p(1AdH$)I9qHNmxxUArdzBW0t+Qeyl)m3?D09770g z)hzXEOy>2_{?o%2B%k%z4d23!pZcoxyW1Ik{|m7Q1>fm4`wsRrl)~h z_=Z*zYL+EG@DV1{6@5@(Ndu!Q$l_6Qlfoz@79q)Kmsf~J7t1)tl#`MD<;1&CAA zH8;i+oBm89dTTDl{aH`cmTPTt@^K-%*sV+t4X9q0Z{A~vEEa!&rRRr=0Rbz4NFCJr zLg2u=0QK@w9XGE=6(-JgeP}G#WG|R&tfHRA3a9*zh5wNTBAD;@YYGx%#E4{C#Wlfo z%-JuW9=FA_T6mR2-Vugk1uGZvJbFvVVWT@QOWz$;?u6+CbyQsbK$>O1APk|xgnh_8 zc)s@Mw7#0^wP6qTtyNq2G#s?5j~REyoU6^lT7dpX{T-rhZWHD%dik*=EA7bIJgOVf_Ga!yC8V^tkTOEHe+JK@Fh|$kfNxO^= z#lpV^(ZQ-3!^_BhV>aXY~GC9{8%1lOJ}6vzXDvPhC>JrtXwFBC+!3a*Z-%#9}i z#<5&0LLIa{q!rEIFSFc9)>{-_2^qbOg5;_A9 ztQ))C6#hxSA{f9R3Eh^`_f${pBJNe~pIQ`tZVR^wyp}=gLK}e5_vG@w+-mp#Fu>e| z*?qBp5CQ5zu+Fi}xAs)YY1;bKG!htqR~)DB$ILN6GaChoiy%Bq@i+1ZnANC0U&D z_4k$=YP47ng+0NhuEt}6C;9-JDd8i5S>`Ml==9wHDQFOsAlmtrVwurYDw_)Ihfk35 zJDBbe!*LUpg%4n>BExWz>KIQ9vexUu^d!7rc_kg#Bf= z7TLz|l*y*3d2vi@c|pX*@ybf!+Xk|2*z$@F4K#MT8Dt4zM_EcFmNp31#7qT6(@GG? zdd;sSY9HHuDb=w&|K%sm`bYX#%UHKY%R`3aLMO?{T#EI@FNNFNO>p@?W*i0z(g2dt z{=9Ofh80Oxv&)i35AQN>TPMjR^UID-T7H5A?GI{MD_VeXZ%;uo41dVm=uT&ne2h0i zv*xI%9vPtdEK@~1&V%p1sFc2AA`9?H)gPnRdlO~URx!fiSV)j?Tf5=5F>hnO=$d$x zzaIfr*wiIc!U1K*$JO@)gP4%xp!<*DvJSv7p}(uTLUb=MSb@7_yO+IsCj^`PsxEl& zIxsi}s3L?t+p+3FXYqujGhGwTx^WXgJ1}a@Yq5mwP0PvGEr*qu7@R$9j>@-q1rz5T zriz;B^(ex?=3Th6h;7U`8u2sDlfS{0YyydK=*>-(NOm9>S_{U|eg(J~C7O zIe{|LK=Y`hXiF_%jOM8Haw3UtaE{hWdzo3BbD6ud7br4cODBtN(~Hl+odP0SSWPw;I&^m)yLw+nd#}3#z}?UIcX3=SssI}`QwY=% zAEXTODk|MqTx}2DVG<|~(CxgLyi*A{m>M@1h^wiC)4Hy>1K7@|Z&_VPJsaQoS8=ex zDL&+AZdQa>ylxhT_Q$q=60D5&%pi6+qlY3$3c(~rsITX?>b;({FhU!7HOOhSP7>bmTkC8KM%!LRGI^~y3Ug+gh!QM=+NZXznM)?L3G=4=IMvFgX3BAlyJ z`~jjA;2z+65D$j5xbv9=IWQ^&-K3Yh`vC(1Qz2h2`o$>Cej@XRGff!it$n{@WEJ^N z41qk%Wm=}mA*iwCqU_6}Id!SQd13aFER3unXaJJXIsSnxvG2(hSCP{i&QH$tL&TPx zDYJsuk+%laN&OvKb-FHK$R4dy%M7hSB*yj#-nJy?S9tVoxAuDei{s}@+pNT!vLOIC z8g`-QQW8FKp3cPsX%{)0B+x+OhZ1=L7F-jizt|{+f1Ga7%+!BXqjCjH&x|3%?UbN# zh?$I1^YokvG$qFz5ySK+Ja5=mkR&p{F}ev**rWdKMko+Gj^?Or=UH?SCg#0F(&a_y zXOh}dPv0D9l0RVedq1~jCNV=8?vZfU-Xi|nkeE->;ohG3U7z+^0+HV17~-_Mv#mV` zzvwUJJ15v5wwKPv-)i@dsEo@#WEO9zie7mdRAbgL2kjbW4&lk$vxkbq=w5mGKZK6@ zjXWctDkCRx58NJD_Q7e}HX`SiV)TZMJ}~zY6P1(LWo`;yDynY_5_L?N-P`>ALfmyl z8C$a~FDkcwtzK9m$tof>(`Vu3#6r#+v8RGy#1D2)F;vnsiL&P-c^PO)^B-4VeJteLlT@25sPa z%W~q5>YMjj!mhN})p$47VA^v$Jo6_s{!y?}`+h+VM_SN`!11`|;C;B};B&Z<@%FOG z_YQVN+zFF|q5zKab&e4GH|B;sBbKimHt;K@tCH+S{7Ry~88`si7}S)1E{21nldiu5 z_4>;XTJa~Yd$m4A9{Qbd)KUAm7XNbZ4xHbg3a8-+1uf*$1PegabbmCzgC~1WB2F(W zYj5XhVos!X!QHuZXCatkRsdEsSCc+D2?*S7a+(v%toqyxhjz|`zdrUvsxQS{J>?c& zvx*rHw^8b|v^7wq8KWVofj&VUitbm*a&RU_ln#ZFA^3AKEf<#T%8I!Lg3XEsdH(A5 zlgh&M_XEoal)i#0tcq8c%Gs6`xu;vvP2u)D9p!&XNt z!TdF_H~;`g@fNXkO-*t<9~;iEv?)Nee%hVe!aW`N%$cFJ(Dy9+Xk*odyFj72T!(b%Vo5zvCGZ%3tkt$@Wcx8BWEkefI1-~C_3y*LjlQ5%WEz9WD8i^ z2MV$BHD$gdPJV4IaV)G9CIFwiV=ca0cfXdTdK7oRf@lgyPx;_7*RRFk=?@EOb9Gcz zg~VZrzo*Snp&EE{$CWr)JZW)Gr;{B2ka6B!&?aknM-FENcl%45#y?oq9QY z3^1Y5yn&^D67Da4lI}ljDcphaEZw2;tlYuzq?uB4b9Mt6!KTW&ptxd^vF;NbX=00T z@nE1lIBGgjqs?ES#P{ZfRb6f!At51vk%<0X%d_~NL5b8UyfQMPDtfU@>ijA0NP3UU zh{lCf`Wu7cX!go`kUG`1K=7NN@SRGjUKuo<^;@GS!%iDXbJs`o6e`v3O8-+7vRkFm z)nEa$sD#-v)*Jb>&Me+YIW3PsR1)h=-Su)))>-`aRcFJG-8icomO4J@60 zw10l}BYxi{eL+Uu0xJYk-Vc~BcR49Qyyq!7)PR27D`cqGrik=?k1Of>gY7q@&d&Ds zt7&WixP`9~jjHO`Cog~RA4Q%uMg+$z^Gt&vn+d3&>Ux{_c zm|bc;k|GKbhZLr-%p_f%dq$eiZ;n^NxoS-Nu*^Nx5vm46)*)=-Bf<;X#?`YC4tLK; z?;u?shFbXeks+dJ?^o$l#tg*1NA?(1iFff@I&j^<74S!o;SWR^Xi);DM%8XiWpLi0 zQE2dL9^a36|L5qC5+&Pf0%>l&qQ&)OU4vjd)%I6{|H+pw<0(a``9w(gKD&+o$8hOC zNAiShtc}e~ob2`gyVZx59y<6Fpl*$J41VJ-H*e-yECWaDMmPQi-N8XI3 z%iI@ljc+d}_okL1CGWffeaejlxWFVDWu%e=>H)XeZ|4{HlbgC-Uvof4ISYQzZ0Um> z#Ov{k1c*VoN^f(gfiueuag)`TbjL$XVq$)aCUBL_M`5>0>6Ska^*Knk__pw{0I>jA zzh}Kzg{@PNi)fcAk7jMAdi-_RO%x#LQszDMS@_>iFoB+zJ0Q#CQJzFGa8;pHFdi`^ zxnTC`G$7Rctm3G8t8!SY`GwFi4gF|+dAk7rh^rA{NXzc%39+xSYM~($L(pJ(8Zjs* zYdN_R^%~LiGHm9|ElV4kVZGA*T$o@YY4qpJOxGHlUi*S*A(MrgQ{&xoZQo+#PuYRs zv3a$*qoe9gBqbN|y|eaH=w^LE{>kpL!;$wRahY(hhzRY;d33W)m*dfem@)>pR54Qy z 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 \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..6d57edc --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/mcspring-api/mcspring-base/build.gradle.kts b/mcspring-api/mcspring-base/build.gradle.kts new file mode 100644 index 0000000..0126974 --- /dev/null +++ b/mcspring-api/mcspring-base/build.gradle.kts @@ -0,0 +1,13 @@ +plugins { + id("org.jetbrains.dokka") +} + +dependencies { + compile(project(":mcspring-api:mcspring-jar-loader")) + compile("org.jetbrains.kotlin:kotlin-reflect:1.3.72") + compile("org.springframework.boot:spring-boot-loader") + compile("org.springframework.boot:spring-boot-starter") + testCompile("org.springframework.boot:spring-boot-starter-test") + compileOnly("org.apache.logging.log4j:log4j-core:2.12.1") + compileOnly("org.spigotmc:spigot-api") +} diff --git a/mcspring-api/mcspring-base/pom.xml b/mcspring-api/mcspring-base/pom.xml deleted file mode 100644 index 9e50b6d..0000000 --- a/mcspring-api/mcspring-base/pom.xml +++ /dev/null @@ -1,76 +0,0 @@ - - - - mcspring-api - in.kyle.mcspring - 0.0.9 - - - 4.0.0 - - mcspring-base - - - - spigot-repo - https://hub.spigotmc.org/nexus/content/repositories/snapshots/ - - - - - - org.spigotmc - spigot-api - - - in.kyle.mcspring - mcspring-jar-loader - ${project.version} - - - org.springframework.boot - spring-boot-loader - - - org.springframework.boot - spring-boot-starter - - - org.springframework.boot - spring-boot-starter-logging - - - - - org.apache.logging.log4j - log4j-core - 2.12.1 - provided - - - org.springframework.boot - spring-boot-starter-aop - - - org.springframework.boot - spring-boot-starter-test - test - - - org.jetbrains.kotlin - kotlin-reflect - 1.3.72 - - - - - - - - - - - - diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/SpringPlugin.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/SpringPlugin.kt similarity index 98% rename from mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/SpringPlugin.kt rename to mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/SpringPlugin.kt index 6bb16f0..b74c156 100644 --- a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/SpringPlugin.kt +++ b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/SpringPlugin.kt @@ -10,7 +10,7 @@ import org.springframework.context.ConfigurableApplicationContext import org.springframework.core.io.DefaultResourceLoader import java.util.* -class SpringPlugin( +internal class SpringPlugin( private val plugin: Plugin ) { diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/SpringSpigotSupport.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/SpringSpigotSupport.kt similarity index 100% rename from mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/SpringSpigotSupport.kt rename to mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/SpringSpigotSupport.kt diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/annotation/PluginDepend.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/annotation/PluginDepend.kt similarity index 100% rename from mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/annotation/PluginDepend.kt rename to mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/annotation/PluginDepend.kt diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/BukkitCommandRegistration.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/BukkitCommandRegistration.kt similarity index 100% rename from mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/BukkitCommandRegistration.kt rename to mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/BukkitCommandRegistration.kt diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/Command.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/Command.kt similarity index 100% rename from mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/Command.kt rename to mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/Command.kt diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandController.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/CommandController.kt similarity index 100% rename from mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandController.kt rename to mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/CommandController.kt diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandRegistration.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/CommandRegistration.kt similarity index 100% rename from mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandRegistration.kt rename to mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/CommandRegistration.kt diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandResolver.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/CommandResolver.kt similarity index 100% rename from mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandResolver.kt rename to mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/CommandResolver.kt diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandScanner.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/CommandScanner.kt similarity index 100% rename from mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/CommandScanner.kt rename to mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/CommandScanner.kt diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/ParameterResolver.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/ParameterResolver.kt similarity index 100% rename from mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/ParameterResolver.kt rename to mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/ParameterResolver.kt diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleCommandFactory.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/SimpleCommandFactory.kt similarity index 100% rename from mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleCommandFactory.kt rename to mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/SimpleCommandFactory.kt diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleMethodInjection.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/SimpleMethodInjection.kt similarity index 100% rename from mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleMethodInjection.kt rename to mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/SimpleMethodInjection.kt diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleSpringParameterResolver.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/SimpleSpringParameterResolver.kt similarity index 100% rename from mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/command/SimpleSpringParameterResolver.kt rename to mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/SimpleSpringParameterResolver.kt diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/event/EventHandlerSupport.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/event/EventHandlerSupport.kt similarity index 100% rename from mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/event/EventHandlerSupport.kt rename to mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/event/EventHandlerSupport.kt diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/event/EventService.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/event/EventService.kt similarity index 100% rename from mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/event/EventService.kt rename to mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/event/EventService.kt diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/scheduler/ScheduledAnnotationSupport.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/scheduler/ScheduledAnnotationSupport.kt similarity index 100% rename from mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/scheduler/ScheduledAnnotationSupport.kt rename to mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/scheduler/ScheduledAnnotationSupport.kt diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/scheduler/SchedulerService.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/scheduler/SchedulerService.kt similarity index 100% rename from mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/scheduler/SchedulerService.kt rename to mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/scheduler/SchedulerService.kt diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/util/SpringScanner.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/util/SpringScanner.kt similarity index 96% rename from mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/util/SpringScanner.kt rename to mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/util/SpringScanner.kt index 75bea9b..240465f 100644 --- a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/util/SpringScanner.kt +++ b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/util/SpringScanner.kt @@ -6,7 +6,7 @@ import org.springframework.stereotype.Component import java.lang.reflect.Method @Component -class SpringScanner( +internal class SpringScanner( private val context: ApplicationContext ) { fun scanMethods(annotation: Class): Map { diff --git a/mcspring-api/mcspring-jar-loader/build.gradle.kts b/mcspring-api/mcspring-jar-loader/build.gradle.kts new file mode 100644 index 0000000..062b32a --- /dev/null +++ b/mcspring-api/mcspring-jar-loader/build.gradle.kts @@ -0,0 +1,3 @@ +dependencies { + compile("org.springframework.boot:spring-boot-loader") +} diff --git a/mcspring-api/mcspring-jar-loader/pom.xml b/mcspring-api/mcspring-jar-loader/pom.xml deleted file mode 100644 index d5283c8..0000000 --- a/mcspring-api/mcspring-jar-loader/pom.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - mcspring-api - in.kyle.mcspring - 0.0.9 - - 4.0.0 - - mcspring-jar-loader - - - - org.springframework.boot - spring-boot-loader - - - diff --git a/mcspring-api/mcspring-jar-loader/src/main/java/org/springframework/boot/loader/mcspring/McSpringLoader.kt b/mcspring-api/mcspring-jar-loader/src/main/kotlin/org/springframework/boot/loader/mcspring/McSpringLoader.kt similarity index 100% rename from mcspring-api/mcspring-jar-loader/src/main/java/org/springframework/boot/loader/mcspring/McSpringLoader.kt rename to mcspring-api/mcspring-jar-loader/src/main/kotlin/org/springframework/boot/loader/mcspring/McSpringLoader.kt diff --git a/mcspring-api/mcspring-subcommands/build.gradle.kts b/mcspring-api/mcspring-subcommands/build.gradle.kts new file mode 100644 index 0000000..e0d2a6f --- /dev/null +++ b/mcspring-api/mcspring-subcommands/build.gradle.kts @@ -0,0 +1,9 @@ +plugins { + id("org.jetbrains.dokka") +} + +dependencies { + compile(project(":mcspring-api:mcspring-base")) + testCompile(project(":mcspring-api:mcspring-test")) + compileOnly("org.spigotmc:spigot-api") +} diff --git a/mcspring-api/mcspring-subcommands/pom.xml b/mcspring-api/mcspring-subcommands/pom.xml deleted file mode 100644 index 7a52f90..0000000 --- a/mcspring-api/mcspring-subcommands/pom.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - mcspring-api - in.kyle.mcspring - 0.0.9 - - 4.0.0 - - mcspring-subcommands - - - - in.kyle.mcspring - mcspring-base - - - org.spigotmc - spigot-api - - - in.kyle.mcspring - mcspring-test - 0.0.9 - test - - - diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/PluginCommandResolver.kt b/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/PluginCommandResolver.kt similarity index 100% rename from mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/PluginCommandResolver.kt rename to mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/PluginCommandResolver.kt diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/api/PluginCommand.kt b/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/api/PluginCommand.kt similarity index 100% rename from mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/api/PluginCommand.kt rename to mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/api/PluginCommand.kt diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandBase.kt b/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandBase.kt similarity index 100% rename from mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandBase.kt rename to mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandBase.kt diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandExecutors.kt b/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandExecutors.kt similarity index 100% rename from mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandExecutors.kt rename to mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandExecutors.kt diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandImpl.kt b/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandImpl.kt similarity index 100% rename from mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandImpl.kt rename to mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandImpl.kt diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandRequires.kt b/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandRequires.kt similarity index 100% rename from mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandRequires.kt rename to mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandRequires.kt diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandWith.kt b/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandWith.kt similarity index 100% rename from mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandWith.kt rename to mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandWith.kt diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/javasupport/HighIQExecutors.kt b/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/javasupport/HighIQExecutors.kt similarity index 100% rename from mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/javasupport/HighIQExecutors.kt rename to mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/javasupport/HighIQExecutors.kt diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/javasupport/PluginCommandExecutorsJavaSupport.kt b/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/javasupport/PluginCommandExecutorsJavaSupport.kt similarity index 100% rename from mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/javasupport/PluginCommandExecutorsJavaSupport.kt rename to mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/javasupport/PluginCommandExecutorsJavaSupport.kt diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/javasupport/PluginCommandWithJavaSupport.kt b/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/javasupport/PluginCommandWithJavaSupport.kt similarity index 100% rename from mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/plugincommand/impl/javasupport/PluginCommandWithJavaSupport.kt rename to mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/javasupport/PluginCommandWithJavaSupport.kt diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/tab/TabCommandFactory.kt b/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/tab/TabCommandFactory.kt similarity index 100% rename from mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/tab/TabCommandFactory.kt rename to mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/tab/TabCommandFactory.kt diff --git a/mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/tab/TabDiscovery.kt b/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/tab/TabDiscovery.kt similarity index 100% rename from mcspring-api/mcspring-subcommands/src/main/java/in/kyle/mcspring/subcommands/tab/TabDiscovery.kt rename to mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/tab/TabDiscovery.kt diff --git a/mcspring-api/mcspring-test/build.gradle.kts b/mcspring-api/mcspring-test/build.gradle.kts new file mode 100644 index 0000000..23a65d3 --- /dev/null +++ b/mcspring-api/mcspring-test/build.gradle.kts @@ -0,0 +1,8 @@ +dependencies { + compile(project(":mcspring-api:mcspring-base")) + compile("org.springframework.boot:spring-boot-test") + compile("org.springframework.boot:spring-boot-starter-test") + compile("org.assertj:assertj-core:3.15.0") + compile("org.junit.jupiter:junit-jupiter-engine:5.3.2") + compileOnly("org.spigotmc:spigot-api") +} diff --git a/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/MCSpringTest.kt b/mcspring-api/mcspring-test/src/main/kotlin/in/kyle/mcspring/test/MCSpringTest.kt similarity index 100% rename from mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/MCSpringTest.kt rename to mcspring-api/mcspring-test/src/main/kotlin/in/kyle/mcspring/test/MCSpringTest.kt diff --git a/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/SpringSpigotSupport.kt b/mcspring-api/mcspring-test/src/main/kotlin/in/kyle/mcspring/test/SpringSpigotSupport.kt similarity index 100% rename from mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/SpringSpigotSupport.kt rename to mcspring-api/mcspring-test/src/main/kotlin/in/kyle/mcspring/test/SpringSpigotSupport.kt diff --git a/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/command/TestCommandExecutor.kt b/mcspring-api/mcspring-test/src/main/kotlin/in/kyle/mcspring/test/command/TestCommandExecutor.kt similarity index 100% rename from mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/command/TestCommandExecutor.kt rename to mcspring-api/mcspring-test/src/main/kotlin/in/kyle/mcspring/test/command/TestCommandExecutor.kt diff --git a/mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/command/TestCommandRegistration.kt b/mcspring-api/mcspring-test/src/main/kotlin/in/kyle/mcspring/test/command/TestCommandRegistration.kt similarity index 100% rename from mcspring-api/mcspring-test/src/main/java/in/kyle/mcspring/test/command/TestCommandRegistration.kt rename to mcspring-api/mcspring-test/src/main/kotlin/in/kyle/mcspring/test/command/TestCommandRegistration.kt diff --git a/mcspring-api/mcspring-vault/build.gradle.kts b/mcspring-api/mcspring-vault/build.gradle.kts new file mode 100644 index 0000000..a1e332b --- /dev/null +++ b/mcspring-api/mcspring-vault/build.gradle.kts @@ -0,0 +1,15 @@ +plugins { + id("org.jetbrains.dokka") +} + +repositories { + maven { url = uri("https://jitpack.io") } +} + +dependencies { + compile(project(":mcspring-api:mcspring-base")) + compileOnly("org.spigotmc:spigot-api") + compileOnly("com.github.MilkBowl:VaultAPI:1.7") { + exclude(group = "org.bukkit", module = "bukkit") + } +} diff --git a/mcspring-api/mcspring-vault/pom.xml b/mcspring-api/mcspring-vault/pom.xml deleted file mode 100644 index 6c6d777..0000000 --- a/mcspring-api/mcspring-vault/pom.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - - mcspring-api - in.kyle.mcspring - 0.0.9 - - 4.0.0 - - - 1.7 - - mcspring-vault - - - - jitpack.io - https://jitpack.io - - - - - - ${project.parent.groupId} - mcspring-base - - - com.github.MilkBowl - VaultAPI - ${vault.version} - provided - - - * - * - - - - - org.spigotmc - spigot-api - - - - - - - org.apache.maven.plugins - maven-source-plugin - - - org.jetbrains.dokka - dokka-maven-plugin - - - - diff --git a/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/vault/economy/EconomyException.kt b/mcspring-api/mcspring-vault/src/main/kotlin/in/kyle/mcspring/vault/economy/EconomyException.kt similarity index 100% rename from mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/vault/economy/EconomyException.kt rename to mcspring-api/mcspring-vault/src/main/kotlin/in/kyle/mcspring/vault/economy/EconomyException.kt diff --git a/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/vault/economy/EconomyService.kt b/mcspring-api/mcspring-vault/src/main/kotlin/in/kyle/mcspring/vault/economy/EconomyService.kt similarity index 100% rename from mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/vault/economy/EconomyService.kt rename to mcspring-api/mcspring-vault/src/main/kotlin/in/kyle/mcspring/vault/economy/EconomyService.kt diff --git a/mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/vault/economy/VaultEconomyService.kt b/mcspring-api/mcspring-vault/src/main/kotlin/in/kyle/mcspring/vault/economy/VaultEconomyService.kt similarity index 100% rename from mcspring-api/mcspring-vault/src/main/java/in/kyle/mcspring/vault/economy/VaultEconomyService.kt rename to mcspring-api/mcspring-vault/src/main/kotlin/in/kyle/mcspring/vault/economy/VaultEconomyService.kt diff --git a/mcspring-api/pom.xml b/mcspring-api/pom.xml deleted file mode 100644 index 1f85996..0000000 --- a/mcspring-api/pom.xml +++ /dev/null @@ -1,80 +0,0 @@ - - - - mcspring-parent - in.kyle.mcspring - 0.0.9 - - - pom - - - mcspring-subcommands - mcspring-base - mcspring-vault - mcspring-jar-loader - mcspring-test - - - - 1.3.72 - - - 4.0.0 - - mcspring-api - - - - - ${project.parent.groupId} - mcspring-parent - - - - - - - org.jetbrains.kotlin - kotlin-stdlib-jdk8 - ${kotlin.version} - - - org.jetbrains.kotlin - kotlin-test - ${kotlin.version} - test - - - - - - - org.jetbrains.kotlin - kotlin-maven-plugin - ${kotlin.version} - - - compile - compile - - compile - - - - test-compile - test-compile - - test-compile - - - - - 1.8 - - - - - diff --git a/mcspring-build/mcspring-archetype/build.gradle.kts b/mcspring-build/mcspring-archetype/build.gradle.kts new file mode 100644 index 0000000..5741968 --- /dev/null +++ b/mcspring-build/mcspring-archetype/build.gradle.kts @@ -0,0 +1,3 @@ +dependencies { + compile(project(":mcspring-build:mcspring-starter:mcspring-plugin-manager")) +} diff --git a/mcspring-build/mcspring-maven-plugin/build.gradle.kts b/mcspring-build/mcspring-maven-plugin/build.gradle.kts new file mode 100644 index 0000000..bbebf50 --- /dev/null +++ b/mcspring-build/mcspring-maven-plugin/build.gradle.kts @@ -0,0 +1,11 @@ +dependencies { + compile("org.yaml:snakeyaml:1.26") + compile("org.apache.maven:maven-plugin-api:3.3.9") + compile("org.apache.maven:maven-core:3.3.9") + compile("org.apache.maven:maven-project:3.0-alpha-2") + compile(project(":mcspring-api:mcspring-base")) + testCompile("org.apache.maven.plugin-testing:maven-plugin-testing-harness:3.3.0") + testCompile("org.apache.maven:maven-compat:3.6.3") + compileOnly("org.apache.maven.plugin-tools:maven-plugin-annotations:3.6.0") + compileOnly("org.projectlombok:lombok:1.18.12") +} diff --git a/mcspring-build/mcspring-plugin-layout/build.gradle.kts b/mcspring-build/mcspring-plugin-layout/build.gradle.kts new file mode 100644 index 0000000..72900cc --- /dev/null +++ b/mcspring-build/mcspring-plugin-layout/build.gradle.kts @@ -0,0 +1,4 @@ +dependencies { + compile("org.springframework.boot:spring-boot-loader-tools") + compile(project(":mcspring-api:mcspring-jar-loader")) +} diff --git a/mcspring-build/mcspring-plugin-layout/src/main/java/in/kyle/mcspring/layout/McSpringLayout.java b/mcspring-build/mcspring-plugin-layout/src/main/kotlin/in/kyle/mcspring/layout/McSpringLayout.java similarity index 100% rename from mcspring-build/mcspring-plugin-layout/src/main/java/in/kyle/mcspring/layout/McSpringLayout.java rename to mcspring-build/mcspring-plugin-layout/src/main/kotlin/in/kyle/mcspring/layout/McSpringLayout.java diff --git a/mcspring-build/mcspring-plugin-layout/src/main/java/in/kyle/mcspring/layout/McSpringLayoutFactory.java b/mcspring-build/mcspring-plugin-layout/src/main/kotlin/in/kyle/mcspring/layout/McSpringLayoutFactory.java similarity index 100% rename from mcspring-build/mcspring-plugin-layout/src/main/java/in/kyle/mcspring/layout/McSpringLayoutFactory.java rename to mcspring-build/mcspring-plugin-layout/src/main/kotlin/in/kyle/mcspring/layout/McSpringLayoutFactory.java diff --git a/mcspring-build/mcspring-plugin-manager/build.gradle.kts b/mcspring-build/mcspring-plugin-manager/build.gradle.kts new file mode 100644 index 0000000..da6e3dc --- /dev/null +++ b/mcspring-build/mcspring-plugin-manager/build.gradle.kts @@ -0,0 +1,5 @@ +dependencies { + compile(project(":mcspring-api:mcspring-subcommands")) + testCompile(project(":mcspring-api:mcspring-test")) + testCompile("org.springframework.boot:spring-boot-starter-test") +} diff --git a/mcspring-build/mcspring-plugin-manager/pom.xml b/mcspring-build/mcspring-plugin-manager/pom.xml deleted file mode 100644 index 2333bc6..0000000 --- a/mcspring-build/mcspring-plugin-manager/pom.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - mcspring-starter - in.kyle.mcspring - 0.0.9 - ../mcspring-starter - - - 4.0.0 - - mcspring-plugin-manager - ${project.artifactId} - Adds basic debugging commands and features - - - - in.kyle.mcspring - mcspring-subcommands - - - org.springframework.boot - spring-boot-starter-test - test - - - in.kyle.mcspring - mcspring-test - ${project.parent.version} - test - - - diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandAbout.kt b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandAbout.kt similarity index 100% rename from mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandAbout.kt rename to mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandAbout.kt diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandClassLoader.kt b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandClassLoader.kt similarity index 100% rename from mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandClassLoader.kt rename to mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandClassLoader.kt diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandGamemode.kt b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandGamemode.kt similarity index 100% rename from mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandGamemode.kt rename to mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandGamemode.kt diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandHeal.kt b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandHeal.kt similarity index 100% rename from mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandHeal.kt rename to mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandHeal.kt diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandOp.kt b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandOp.kt similarity index 100% rename from mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandOp.kt rename to mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandOp.kt diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandPlugin.kt b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandPlugin.kt similarity index 100% rename from mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandPlugin.kt rename to mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandPlugin.kt diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandSpeed.kt b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandSpeed.kt similarity index 100% rename from mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/commands/CommandSpeed.kt rename to mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandSpeed.kt diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/BukkitPluginUnloader.kt b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/BukkitPluginUnloader.kt similarity index 100% rename from mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/BukkitPluginUnloader.kt rename to mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/BukkitPluginUnloader.kt diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/LogFileController.kt b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/LogFileController.kt similarity index 100% rename from mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/LogFileController.kt rename to mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/LogFileController.kt diff --git a/mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/PluginController.kt b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/PluginController.kt similarity index 100% rename from mcspring-build/mcspring-plugin-manager/src/main/java/in/kyle/mcspring/manager/controller/PluginController.kt rename to mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/PluginController.kt diff --git a/mcspring-build/pom.xml b/mcspring-build/pom.xml deleted file mode 100644 index cbead1a..0000000 --- a/mcspring-build/pom.xml +++ /dev/null @@ -1,69 +0,0 @@ - - - - mcspring-parent - in.kyle.mcspring - 0.0.9 - - - pom - - - mcspring-plugin-layout - mcspring-archetype - mcspring-starter - mcspring-maven-plugin - - - - 1.3.72 - - - - - org.jetbrains.kotlin - kotlin-stdlib-jdk8 - ${kotlin.version} - - - org.jetbrains.kotlin - kotlin-test - ${kotlin.version} - test - - - - - - - org.jetbrains.kotlin - kotlin-maven-plugin - ${kotlin.version} - - - compile - compile - - compile - - - - test-compile - test-compile - - test-compile - - - - - 1.8 - - - - - - 4.0.0 - mcspring-build - diff --git a/mcspring-examples/pom.xml b/mcspring-examples/pom.xml deleted file mode 100644 index 946e408..0000000 --- a/mcspring-examples/pom.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - mcspring-starter - in.kyle.mcspring - 0.0.9 - ../mcspring-build/mcspring-starter - - - - simple-factions - simple-factions-addon - - - 4.0.0 - - pom - mcspring-examples - - - - - ${project.parent.groupId} - mcspring-starter - - - - diff --git a/mcspring-examples/simple-factions-addon/build.gradle.kts b/mcspring-examples/simple-factions-addon/build.gradle.kts new file mode 100644 index 0000000..74b92aa --- /dev/null +++ b/mcspring-examples/simple-factions-addon/build.gradle.kts @@ -0,0 +1,4 @@ +dependencies { + compileOnly(project(":mcspring-build:mcspring-starter:mcspring-examples:simple-factions")) + compileOnly("org.projectlombok:lombok:1.18.12") +} diff --git a/mcspring-examples/simple-factions-addon/pom.xml b/mcspring-examples/simple-factions-addon/pom.xml deleted file mode 100644 index 7e2ae6c..0000000 --- a/mcspring-examples/simple-factions-addon/pom.xml +++ /dev/null @@ -1,76 +0,0 @@ - - 4.0.0 - - - mcspring-examples - in.kyle.mcspring - 0.0.9 - - - simple-factions-addon - jar - 0.69 - - - UTF-8 - 1.15.1-R0.1-SNAPSHOT - 1.8 - 1.8 - true - - - - - in.kyle.mcspring - simple-factions - ${project.parent.version} - provided - - - org.projectlombok - lombok - - - - - - - in.kyle.mcspring - mcspring-maven-plugin - - - - generate-files - - - - - - org.codehaus.mojo - build-helper-maven-plugin - 3.0.0 - - - generate-resources - - add-source - add-resource - - - - ${project.build.directory}/generated-sources/mc-spring/ - - - - ${project.build.directory}/generated-sources/mc-spring/ - - - - - - - - - diff --git a/mcspring-examples/simple-factions-addon/src/main/java/test/other/stats/FactionsStats.java b/mcspring-examples/simple-factions-addon/src/main/java/test/other/stats/FactionsStats.java deleted file mode 100644 index b060b51..0000000 --- a/mcspring-examples/simple-factions-addon/src/main/java/test/other/stats/FactionsStats.java +++ /dev/null @@ -1,31 +0,0 @@ -package test.other.stats; - -import in.kyle.mcspring.annotation.PluginDepend; -import org.bukkit.event.EventHandler; -import org.bukkit.event.player.PlayerMoveEvent; -import in.kyle.mcspring.command.Command; - -import org.example.factions.api.FactionsApi; -import org.springframework.stereotype.Component; - -import lombok.RequiredArgsConstructor; - -@Component -@RequiredArgsConstructor -@PluginDepend(plugins = "factions") -//@SpringPlugin(name = "faction-stats", description = "Shows stats for factions") -class FactionsStats { - - private final FactionsApi factionsApi; - - @Command("stats") - String test() { -// return "test"; - return String.format("There are %d factions", factionsApi.getFactions().size()); - } - - @EventHandler - void move(PlayerMoveEvent e) { - System.out.println(e.getPlayer().getName() + " moved"); - } -} diff --git a/mcspring-examples/simple-factions-addon/src/main/kotlin/test/other/stats/FactionsStats.kt b/mcspring-examples/simple-factions-addon/src/main/kotlin/test/other/stats/FactionsStats.kt new file mode 100644 index 0000000..552a170 --- /dev/null +++ b/mcspring-examples/simple-factions-addon/src/main/kotlin/test/other/stats/FactionsStats.kt @@ -0,0 +1,25 @@ +package test.other.stats + +import lombok.RequiredArgsConstructor +import org.bukkit.event.EventHandler +import org.bukkit.event.player.PlayerMoveEvent +import org.example.factions.api.FactionsApi +import org.springframework.stereotype.Component + +@Component +@RequiredArgsConstructor +@PluginDepend(plugins = "factions") +internal class FactionsStats { + private val factionsApi: FactionsApi? = null + + @Command("stats") + fun test(): String { +// return "test"; + return String.format("There are %d factions", factionsApi!!.factions.size) + } + + @EventHandler + fun move(e: PlayerMoveEvent) { + println(e.getPlayer().getName().toString() + " moved") + } +} diff --git a/mcspring-examples/simple-factions/build.gradle.kts b/mcspring-examples/simple-factions/build.gradle.kts new file mode 100644 index 0000000..311e64a --- /dev/null +++ b/mcspring-examples/simple-factions/build.gradle.kts @@ -0,0 +1,4 @@ +dependencies { + compile(project(":mcspring-api:mcspring-subcommands")) + compileOnly("org.projectlombok:lombok:1.18.12") +} diff --git a/mcspring-examples/simple-factions/pom.xml b/mcspring-examples/simple-factions/pom.xml deleted file mode 100644 index 925a784..0000000 --- a/mcspring-examples/simple-factions/pom.xml +++ /dev/null @@ -1,31 +0,0 @@ - - 4.0.0 - - - mcspring-examples - in.kyle.mcspring - 0.0.9 - - - simple-factions - jar - - - 1.15.1-R0.1-SNAPSHOT - 1.8 - 1.8 - - - - - in.kyle.mcspring - mcspring-subcommands - - - org.projectlombok - lombok - - - diff --git a/mcspring-examples/simple-factions/src/main/java/org/example/factions/api/Faction.java b/mcspring-examples/simple-factions/src/main/kotlin/org/example/factions/api/Faction.java similarity index 100% rename from mcspring-examples/simple-factions/src/main/java/org/example/factions/api/Faction.java rename to mcspring-examples/simple-factions/src/main/kotlin/org/example/factions/api/Faction.java diff --git a/mcspring-examples/simple-factions/src/main/java/org/example/factions/api/FactionsApi.java b/mcspring-examples/simple-factions/src/main/kotlin/org/example/factions/api/FactionsApi.java similarity index 100% rename from mcspring-examples/simple-factions/src/main/java/org/example/factions/api/FactionsApi.java rename to mcspring-examples/simple-factions/src/main/kotlin/org/example/factions/api/FactionsApi.java diff --git a/mcspring-examples/simple-factions/src/main/java/org/example/factions/commands/FactionCommand.java b/mcspring-examples/simple-factions/src/main/kotlin/org/example/factions/commands/FactionCommand.java similarity index 100% rename from mcspring-examples/simple-factions/src/main/java/org/example/factions/commands/FactionCommand.java rename to mcspring-examples/simple-factions/src/main/kotlin/org/example/factions/commands/FactionCommand.java diff --git a/mcspring-examples/simple-factions/src/main/java/org/example/factions/controller/FactionsController.java b/mcspring-examples/simple-factions/src/main/kotlin/org/example/factions/controller/FactionsController.java similarity index 100% rename from mcspring-examples/simple-factions/src/main/java/org/example/factions/controller/FactionsController.java rename to mcspring-examples/simple-factions/src/main/kotlin/org/example/factions/controller/FactionsController.java diff --git a/pom.xml b/pom.xml deleted file mode 100644 index 005694a..0000000 --- a/pom.xml +++ /dev/null @@ -1,309 +0,0 @@ - - - 4.0.0 - - - mcspring-build - mcspring-api - - - in.kyle.mcspring - mcspring-parent - pom - 0.0.9 - mcspring - Adds Spring support to Bukkit plugins - https://github.com/kylepls/mc-spring - - - UTF-8 - 8 - ${java.version} - ${java.version} - 1.15.1-R0.1-SNAPSHOT - 2.2.4.RELEASE - 0.20.0 - - - - - MIT License - http://www.opensource.org/licenses/mit-license.php - repo - - - - - GitHub - https://github.com/kylepls/mcspring/issues - - - - - api.wiki - https://github.com/phillip-kruger/apiee/wiki - - - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - - - https://github.com/kylepls/mcspring - scm:git:git://github.com/kylepls/mcspring.git - scm:git:ssh://git@github.com:kylepls/mcspring.git - - - - - Kyle - mail@kyle.in - kylepls - - - Josh - joshuabetz12@gmail.com - Joshbpls - - - - - - spigot-repo - https://hub.spigotmc.org/nexus/content/repositories/snapshots/ - - - - - - jcenter - JCenter - https://jcenter.bintray.com/ - - - kotlinx - https://kotlin.bintray.com/kotlinx - - - - - - - org.projectlombok - lombok - 1.18.12 - provided - - - org.springframework.boot - spring-boot-dependencies - ${spring.version} - pom - import - - - in.kyle.mcspring - mcspring-base - ${project.parent.version} - - - junit - junit - 4.12 - test - - - org.spigotmc - spigot-api - ${spigot.version} - provided - - - net.md-5 - bungeecord-chat - - - - - - - - - - - release - - - release - - - - org.apache.maven.plugins - maven-source-plugin - - - org.apache.maven.plugins - maven-javadoc-plugin - - - org.sonatype.plugins - nexus-staging-maven-plugin - - - org.apache.maven.plugins - maven-gpg-plugin - - - - - - - - - - org.apache.maven.plugins - maven-enforcer-plugin - 3.0.0-M3 - - - - - - - - org.jetbrains.dokka - dokka-maven-plugin - - - io.github.javaeden.orchid - orchid-maven-plugin - - - - - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.8 - true - - ossrh - https://oss.sonatype.org/ - true - - - - org.apache.maven.plugins - maven-gpg-plugin - 1.6 - - - sign-artifacts - verify - - sign - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.8.1 - - true - false - - -AartifactId=${project.artifactId} - - - - - org.apache.maven.plugins - maven-source-plugin - 3.2.1 - - - attach-sources - - jar - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 3.1.1 - - - attach-javadocs - - jar - - - - - - org.jetbrains.dokka - dokka-maven-plugin - 0.10.1 - - - pre-site - - dokka - javadoc - - - - - html - true - false - - - - io.github.javaeden.orchid - orchid-maven-plugin - ${orchid.version} - - - io.github.javaeden.orchid - OrchidDocs - ${orchid.version} - - - io.github.javaeden.orchid - OrchidKotlindoc - ${orchid.version} - - - io.github.javaeden.orchid - OrchidPluginDocs - ${orchid.version} - - - - - Editorial - ${project.version} - https://kyle.github.io/project - - - - build - - - - - - diff --git a/src/orchid/resources/config.yml b/src/orchid/resources/config.yml index b474fff..c1b245e 100644 --- a/src/orchid/resources/config.yml +++ b/src/orchid/resources/config.yml @@ -16,12 +16,12 @@ Editorial: itemId: 'Changelog' - type: 'separator' title: 'API Docs' - - type: 'sourcedocPages' - moduleType: 'kotlindoc' + - type: 'kotlindocClasses' + moduleType: 'kotlindocClasses' node: 'classes' asSubmenu: true submenuTitle: 'Classes' - - type: 'sourcedocPages' + - type: 'kotlindocPackages' moduleType: 'kotlindoc' node: 'packages' asSubmenu: true @@ -29,10 +29,20 @@ Editorial: kotlindoc: sourceDirs: - - './../../../../app/src/main/kotlin' - sourcePages: - menu: - - type: 'sourcedocPageLinks' - moduleType: 'kotlindoc' - itemTitleType: 'SIGNATURE' - includeItems: true + - './../../../mcspring-api/mcspring-base/src/main/kotlin' + - './../../../mcspring-api/mcspring-subcommands/src/main/kotlin' + - './../../../mcspring-api/mcspring-vault/src/main/kotlin' + # modules: +# - name: 'mcspring-base' +# moduleGroup: 'group-one' +# sourceDirs: +# - './../../../../mcspring-api/mcspring-base/src/main/kotlin' +# - name: 'mcspring-subcommands' +# sourceDirs: +# - './../../../../../mcspring-api/mcspring-subcommands/src/main/kotlin' +# sourcePages: +# menu: +# - type: 'sourcedocPageLinks' +# moduleType: 'kotlindoc' +# itemTitleType: 'SIGNATURE' +# includeItems: true From c770f413c61b1525c492e720a80185d312b29aaf Mon Sep 17 00:00:00 2001 From: kylepls Date: Thu, 14 May 2020 19:18:02 -0600 Subject: [PATCH 22/54] add gradle files --- build.gradle.kts | 69 +++++++++++++++++++++++++++++++++++++++++++++ settings.gradle.kts | 22 +++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 build.gradle.kts create mode 100644 settings.gradle.kts diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..61670bc --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,69 @@ +import org.springframework.boot.gradle.plugin.SpringBootPlugin + +allprojects { + group = "in.kyle.mcspring" + version = "0.0.9" +} + +plugins { + id("io.spring.dependency-management") version "1.0.9.RELEASE" + id("org.springframework.boot") version "2.2.4.RELEASE" apply false + kotlin("jvm") version "1.3.72" + id("org.jetbrains.dokka") version "0.10.1" + id("com.eden.orchidPlugin") version "0.20.0" +} + +repositories { + jcenter() + maven { url = uri("https://kotlin.bintray.com/kotlinx") } +} + +dependencies { + orchidRuntime("io.github.javaeden.orchid:OrchidDocs:0.20.0") + orchidRuntime("io.github.javaeden.orchid:OrchidSourceDoc:0.20.0") + orchidRuntime("io.github.javaeden.orchid:OrchidKotlindoc:0.20.0") + orchidRuntime("io.github.javaeden.orchid:OrchidPluginDocs:0.20.0") +} + +subprojects { + apply(plugin = "io.spring.dependency-management") + apply(plugin = "kotlin") + + repositories { + jcenter() + mavenCentral() + maven { url = uri("http://repo.maven.apache.org/maven2") } + } + + dependencyManagement { + dependencies { + dependency("org.spigotmc:spigot-api:1.15.1-R0.1-SNAPSHOT") { + repositories { + maven { url = uri("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") } + maven { url = uri("https://oss.sonatype.org/content/repositories/snapshots") } + } + } + } + imports { + mavenBom(SpringBootPlugin.BOM_COORDINATES) + } + } + + dependencies { + implementation(kotlin("stdlib")) + } +} + +tasks.withType(org.jetbrains.dokka.gradle.DokkaTask::class) { + outputFormat = "html" + configuration { + externalDocumentationLink { + url = uri("https://hub.spigotmc.org/javadocs/bukkit/").toURL() + url = uri("http://milkbowl.github.io/VaultAPI/").toURL() + } + } +} + +orchid { + theme = "Editorial" +} diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..8b405d1 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,22 @@ +rootProject.name = "mcspring-parent" + +include(":mcspring-build:mcspring-plugin-layout") +include(":mcspring-build:mcspring-archetype") +include(":mcspring-build:mcspring-starter:mcspring-examples:simple-factions") +include(":mcspring-build:mcspring-starter:mcspring-examples:simple-factions-addon") +include(":mcspring-build:mcspring-starter:mcspring-examples") +include(":mcspring-build:mcspring-starter:mcspring-plugin-manager") +include(":mcspring-build:mcspring-starter") +include(":mcspring-build:mcspring-maven-plugin") +include(":mcspring-build") +include(":mcspring-api:mcspring-subcommands") +include(":mcspring-api:mcspring-base") +include(":mcspring-api:mcspring-vault") +include(":mcspring-api:mcspring-jar-loader") +include(":mcspring-api:mcspring-test") +include(":mcspring-api") + +project(":mcspring-build:mcspring-starter:mcspring-examples:simple-factions").projectDir = file("mcspring-examples/simple-factions") +project(":mcspring-build:mcspring-starter:mcspring-examples:simple-factions-addon").projectDir = file("mcspring-examples/simple-factions-addon") +project(":mcspring-build:mcspring-starter:mcspring-examples").projectDir = file("mcspring-examples") +project(":mcspring-build:mcspring-starter:mcspring-plugin-manager").projectDir = file("mcspring-build/mcspring-plugin-manager") From db3bb958608add05bc689db54a76e31a209b9bc2 Mon Sep 17 00:00:00 2001 From: kylepls Date: Sun, 7 Jun 2020 20:38:51 -0600 Subject: [PATCH 23/54] Remove subcommands api and replace with DSL --- build.gradle.kts | 67 +--- buildSrc/build.gradle.kts | 20 ++ .../src/main/kotlin/mcspring-build.gradle.kts | 43 +++ .../src/main/kotlin/mcspring-docs.gradle.kts | 29 ++ mcspring-api/mcspring-base/build.gradle.kts | 20 +- .../kotlin/in/kyle/mcspring/SpringPlugin.kt | 1 - .../in/kyle/mcspring/SpringSpigotSupport.kt | 5 +- .../mcspring/command/CommandController.kt | 32 -- .../kyle/mcspring/command/CommandResolver.kt | 9 - .../{ => execution}/SimpleMethodInjection.kt | 4 +- .../BukkitCommandRegistration.kt | 12 +- .../command/registration/CommandController.kt | 19 ++ .../CommandExecutorFactory.kt} | 29 +- .../{ => registration}/CommandRegistration.kt | 3 +- .../{ => registration}/CommandScanner.kt | 4 +- .../{ => resolution}/ParameterResolver.kt | 2 +- .../SimpleSpringParameterResolver.kt | 2 +- .../in/kyle/mcspring/util/SpringScanner.kt | 8 +- mcspring-api/mcspring-chat/README.md | 15 + mcspring-api/mcspring-chat/build.gradle.kts | 3 + .../in/kyle/mcspring/chat/ColorHelpers.kt | 54 +++ .../kotlin/in/kyle/mcspring/chat/Message.kt | 89 +++++ .../in/kyle/mcspring/chat/TestMessage.kt | 59 ++++ mcspring-api/mcspring-commands-dsl/README.md | 117 +++++++ .../mcspring-commands-dsl/build.gradle.kts | 4 + .../mcspring/commands/dsl/CommandBuilder.kt | 217 ++++++++++++ .../mcspring/commands/dsl/CommandModel.kt | 36 ++ .../mcspring/commands/dsl/CommandParsers.kt | 118 +++++++ .../mcspring/commands/dsl/CommandUtils.kt | 25 ++ .../dsl/mcspring/CommandDslRegistration.kt | 45 +++ .../commands/dsl/CommandTestSupport.kt | 31 ++ .../builders/TestBaseRequirementBuilder.kt | 62 ++++ .../dsl/builders/TestIntRequirementBuilder.kt | 20 ++ .../dsl/builders/TestOnRequirementBuilder.kt | 44 +++ .../builders/TestOptionRequirementBuilder.kt | 40 +++ .../builders/TestStringRequirementBuilder.kt | 47 +++ mcspring-api/mcspring-common/README.md | 3 + mcspring-api/mcspring-common/build.gradle.kts | 3 + .../main/kotlin/in/kyle/mcspring/common/.keep | 0 .../common/commands/CommandMapWrapper.kt | 18 + mcspring-api/mcspring-guis/README.md | 3 + mcspring-api/mcspring-guis/build.gradle.kts | 5 + .../main/kotlin/in/kyle/mcspring/guis/Gui.kt | 172 ++++++++++ .../in/kyle/mcspring/guis/chat/ChatGui.kt | 56 +++ .../in/kyle/mcspring/guis/hotbar/HotbarGui.kt | 144 ++++++++ .../mcspring/guis/invenotry/InventoryGui.kt | 170 +++++++++ .../guis/invenotry/InventoryTitleHelper.kt | 322 ++++++++++++++++++ .../in/kyle/mcspring/guis/item/ItemBuilder.kt | 98 ++++++ .../mcspring-jar-loader/build.gradle.kts | 2 +- .../mcspring-logging/build.gradle.kts | 14 + .../in/kyle/mcspring/logging/LogCall.kt | 12 + .../in/kyle/mcspring/logging/LogCallAspect.kt | 31 ++ .../mcspring/logging/LoggingConfiguration.kt | 25 ++ .../in/kyle/mcspring/logging/TestComponent.kt | 12 + .../src/main/resources/log4j2-mcspring.xml | 24 ++ .../src/main/resources/log4j2.xml | 18 + .../in/kyle/mcspring/logging/TestLog.kt | 32 ++ mcspring-api/mcspring-rx/README.md | 5 + mcspring-api/mcspring-rx/build.gradle.kts | 6 + .../in/kyle/mcspring/rx/EventSupport.kt | 67 ++++ .../in/kyle/mcspring/rx/SchedulerSupport.kt | 79 +++++ .../mcspring-subcommands/build.gradle.kts | 9 - .../subcommands/PluginCommandResolver.kt | 28 -- .../plugincommand/api/PluginCommand.kt | 161 --------- .../plugincommand/impl/PluginCommandBase.kt | 59 ---- .../impl/PluginCommandExecutors.kt | 76 ----- .../plugincommand/impl/PluginCommandImpl.kt | 38 --- .../impl/PluginCommandRequires.kt | 29 -- .../plugincommand/impl/PluginCommandWith.kt | 64 ---- .../impl/javasupport/HighIQExecutors.kt | 58 ---- .../PluginCommandExecutorsJavaSupport.kt | 121 ------- .../PluginCommandWithJavaSupport.kt | 21 -- .../subcommands/tab/TabCommandFactory.kt | 49 --- .../mcspring/subcommands/tab/TabDiscovery.kt | 46 --- .../kyle/mcspring/subcommands/TestConsole.kt | 30 -- .../subcommands/TestJavaPluginCommand.java | 56 --- .../mcspring/subcommands/TestPluginCommand.kt | 177 ---------- .../mcspring/subcommands/TestTabCompletion.kt | 90 ----- .../TestPluginCommandExecutors.kt | 58 ---- .../TestPluginCommandRequires.kt | 41 --- .../plugincommand/TestPluginCommandWith.kt | 103 ------ .../subcommands/tab/TestTabDiscovery.kt | 61 ---- mcspring-api/mcspring-test/build.gradle.kts | 13 +- mcspring-api/mcspring-test/pom.xml | 54 --- .../kyle/mcspring/test/SpringSpigotSupport.kt | 6 +- .../test/command/TestCommandExecutor.kt | 4 +- .../test/command/TestCommandRegistration.kt | 61 ++-- mcspring-api/mcspring-vault/build.gradle.kts | 5 +- .../mcspring-plugin-manager/build.gradle.kts | 5 +- .../mcspring/manager/commands/CommandAbout.kt | 23 +- .../manager/commands/CommandClassLoader.kt | 21 +- .../manager/commands/CommandGamemode.kt | 40 +-- .../mcspring/manager/commands/CommandHeal.kt | 31 +- .../mcspring/manager/commands/CommandOp.kt | 13 +- .../manager/commands/CommandPlugin.kt | 48 ++- .../manager/commands/CommandReload.kt | 25 ++ .../mcspring/manager/commands/CommandSpeed.kt | 22 +- .../commands/TestCommandClassLoader.kt | 19 -- .../manager/commands/TestCommandGamemode.kt | 35 -- .../manager/commands/TestCommandSpeed.kt | 34 -- .../simple-factions/build.gradle.kts | 2 +- .../factions/commands/FactionCommand.java | 2 +- settings.gradle.kts | 7 +- src/orchid/resources/changelog/1.0.0.md | 4 +- src/orchid/resources/wiki/commands-api.md | 70 ++++ src/orchid/resources/wiki/intro.md | 14 + src/orchid/resources/wiki/setup.md | 29 ++ src/orchid/resources/wiki/subcommands-api.md | 191 +++++++++++ src/orchid/resources/wiki/summary.md | 11 +- 109 files changed, 3024 insertions(+), 1796 deletions(-) create mode 100644 buildSrc/build.gradle.kts create mode 100644 buildSrc/src/main/kotlin/mcspring-build.gradle.kts create mode 100644 buildSrc/src/main/kotlin/mcspring-docs.gradle.kts delete mode 100644 mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/CommandController.kt delete mode 100644 mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/CommandResolver.kt rename mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/{ => execution}/SimpleMethodInjection.kt (94%) rename mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/{ => registration}/BukkitCommandRegistration.kt (68%) create mode 100644 mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/registration/CommandController.kt rename mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/{SimpleCommandFactory.kt => registration/CommandExecutorFactory.kt} (61%) rename mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/{ => registration}/CommandRegistration.kt (58%) rename mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/{ => registration}/CommandScanner.kt (83%) rename mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/{ => resolution}/ParameterResolver.kt (62%) rename mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/{ => resolution}/SimpleSpringParameterResolver.kt (91%) create mode 100644 mcspring-api/mcspring-chat/README.md create mode 100644 mcspring-api/mcspring-chat/build.gradle.kts create mode 100644 mcspring-api/mcspring-chat/src/main/kotlin/in/kyle/mcspring/chat/ColorHelpers.kt create mode 100644 mcspring-api/mcspring-chat/src/main/kotlin/in/kyle/mcspring/chat/Message.kt create mode 100644 mcspring-api/mcspring-chat/src/test/kotlin/in/kyle/mcspring/chat/TestMessage.kt create mode 100644 mcspring-api/mcspring-commands-dsl/README.md create mode 100644 mcspring-api/mcspring-commands-dsl/build.gradle.kts create mode 100644 mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandBuilder.kt create mode 100644 mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandModel.kt create mode 100644 mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandParsers.kt create mode 100644 mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandUtils.kt create mode 100644 mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/mcspring/CommandDslRegistration.kt create mode 100644 mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/CommandTestSupport.kt create mode 100644 mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestBaseRequirementBuilder.kt create mode 100644 mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestIntRequirementBuilder.kt create mode 100644 mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestOnRequirementBuilder.kt create mode 100644 mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestOptionRequirementBuilder.kt create mode 100644 mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestStringRequirementBuilder.kt create mode 100644 mcspring-api/mcspring-common/README.md create mode 100644 mcspring-api/mcspring-common/build.gradle.kts create mode 100644 mcspring-api/mcspring-common/src/main/kotlin/in/kyle/mcspring/common/.keep create mode 100644 mcspring-api/mcspring-common/src/main/kotlin/in/kyle/mcspring/common/commands/CommandMapWrapper.kt create mode 100644 mcspring-api/mcspring-guis/README.md create mode 100644 mcspring-api/mcspring-guis/build.gradle.kts create mode 100644 mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/Gui.kt create mode 100644 mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/chat/ChatGui.kt create mode 100644 mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/hotbar/HotbarGui.kt create mode 100644 mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/invenotry/InventoryGui.kt create mode 100644 mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/invenotry/InventoryTitleHelper.kt create mode 100644 mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/item/ItemBuilder.kt create mode 100644 mcspring-api/mcspring-logging/build.gradle.kts create mode 100644 mcspring-api/mcspring-logging/src/main/kotlin/in/kyle/mcspring/logging/LogCall.kt create mode 100644 mcspring-api/mcspring-logging/src/main/kotlin/in/kyle/mcspring/logging/LogCallAspect.kt create mode 100644 mcspring-api/mcspring-logging/src/main/kotlin/in/kyle/mcspring/logging/LoggingConfiguration.kt create mode 100644 mcspring-api/mcspring-logging/src/main/kotlin/in/kyle/mcspring/logging/TestComponent.kt create mode 100644 mcspring-api/mcspring-logging/src/main/resources/log4j2-mcspring.xml create mode 100644 mcspring-api/mcspring-logging/src/main/resources/log4j2.xml create mode 100644 mcspring-api/mcspring-logging/src/test/kotlin/in/kyle/mcspring/logging/TestLog.kt create mode 100644 mcspring-api/mcspring-rx/README.md create mode 100644 mcspring-api/mcspring-rx/build.gradle.kts create mode 100644 mcspring-api/mcspring-rx/src/main/kotlin/in/kyle/mcspring/rx/EventSupport.kt create mode 100644 mcspring-api/mcspring-rx/src/main/kotlin/in/kyle/mcspring/rx/SchedulerSupport.kt delete mode 100644 mcspring-api/mcspring-subcommands/build.gradle.kts delete mode 100644 mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/PluginCommandResolver.kt delete mode 100644 mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/api/PluginCommand.kt delete mode 100644 mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandBase.kt delete mode 100644 mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandExecutors.kt delete mode 100644 mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandImpl.kt delete mode 100644 mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandRequires.kt delete mode 100644 mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandWith.kt delete mode 100644 mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/javasupport/HighIQExecutors.kt delete mode 100644 mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/javasupport/PluginCommandExecutorsJavaSupport.kt delete mode 100644 mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/javasupport/PluginCommandWithJavaSupport.kt delete mode 100644 mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/tab/TabCommandFactory.kt delete mode 100644 mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/tab/TabDiscovery.kt delete mode 100644 mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestConsole.kt delete mode 100644 mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestJavaPluginCommand.java delete mode 100644 mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestPluginCommand.kt delete mode 100644 mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestTabCompletion.kt delete mode 100644 mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/plugincommand/TestPluginCommandExecutors.kt delete mode 100644 mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/plugincommand/TestPluginCommandRequires.kt delete mode 100644 mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/plugincommand/TestPluginCommandWith.kt delete mode 100644 mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/tab/TestTabDiscovery.kt delete mode 100644 mcspring-api/mcspring-test/pom.xml create mode 100644 mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandReload.kt delete mode 100644 mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandClassLoader.kt delete mode 100644 mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandGamemode.kt delete mode 100644 mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandSpeed.kt create mode 100644 src/orchid/resources/wiki/commands-api.md create mode 100644 src/orchid/resources/wiki/intro.md create mode 100644 src/orchid/resources/wiki/setup.md create mode 100644 src/orchid/resources/wiki/subcommands-api.md diff --git a/build.gradle.kts b/build.gradle.kts index 61670bc..2def065 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,69 +1,12 @@ -import org.springframework.boot.gradle.plugin.SpringBootPlugin - -allprojects { - group = "in.kyle.mcspring" - version = "0.0.9" -} - plugins { - id("io.spring.dependency-management") version "1.0.9.RELEASE" - id("org.springframework.boot") version "2.2.4.RELEASE" apply false - kotlin("jvm") version "1.3.72" - id("org.jetbrains.dokka") version "0.10.1" - id("com.eden.orchidPlugin") version "0.20.0" -} - -repositories { - jcenter() - maven { url = uri("https://kotlin.bintray.com/kotlinx") } + `mcspring-docs` } -dependencies { - orchidRuntime("io.github.javaeden.orchid:OrchidDocs:0.20.0") - orchidRuntime("io.github.javaeden.orchid:OrchidSourceDoc:0.20.0") - orchidRuntime("io.github.javaeden.orchid:OrchidKotlindoc:0.20.0") - orchidRuntime("io.github.javaeden.orchid:OrchidPluginDocs:0.20.0") +allprojects { + group = "in.kyle.mcspring" + version = "0.1.0" } subprojects { - apply(plugin = "io.spring.dependency-management") - apply(plugin = "kotlin") - - repositories { - jcenter() - mavenCentral() - maven { url = uri("http://repo.maven.apache.org/maven2") } - } - - dependencyManagement { - dependencies { - dependency("org.spigotmc:spigot-api:1.15.1-R0.1-SNAPSHOT") { - repositories { - maven { url = uri("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") } - maven { url = uri("https://oss.sonatype.org/content/repositories/snapshots") } - } - } - } - imports { - mavenBom(SpringBootPlugin.BOM_COORDINATES) - } - } - - dependencies { - implementation(kotlin("stdlib")) - } -} - -tasks.withType(org.jetbrains.dokka.gradle.DokkaTask::class) { - outputFormat = "html" - configuration { - externalDocumentationLink { - url = uri("https://hub.spigotmc.org/javadocs/bukkit/").toURL() - url = uri("http://milkbowl.github.io/VaultAPI/").toURL() - } - } -} - -orchid { - theme = "Editorial" + apply(plugin = "mcspring-build") } diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts new file mode 100644 index 0000000..e15e90d --- /dev/null +++ b/buildSrc/build.gradle.kts @@ -0,0 +1,20 @@ +plugins { + `kotlin-dsl` +} + +repositories { + jcenter() + mavenCentral() +} + +dependencies { + implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72") + implementation("io.spring.gradle:dependency-management-plugin:1.0.9.RELEASE") + implementation("org.springframework.boot:spring-boot-gradle-plugin:2.3.0.RELEASE") + implementation("org.jetbrains.dokka:dokka-gradle-plugin:0.10.1") + implementation("gradle.plugin.com.eden:orchidPlugin:0.20.0") { + repositories { + maven("https://plugins.gradle.org/m2/") + } + } +} diff --git a/buildSrc/src/main/kotlin/mcspring-build.gradle.kts b/buildSrc/src/main/kotlin/mcspring-build.gradle.kts new file mode 100644 index 0000000..92c887a --- /dev/null +++ b/buildSrc/src/main/kotlin/mcspring-build.gradle.kts @@ -0,0 +1,43 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + id("io.spring.dependency-management") +// id("org.springframework.boot") + kotlin("jvm") +} + +repositories { + jcenter() + mavenCentral() +} + +dependencyManagement { + dependencies { + dependency("org.spigotmc:spigot-api:1.15.2-R0.1-SNAPSHOT") { + repositories { + maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") + maven("https://oss.sonatype.org/content/repositories/snapshots") + } + } + } + imports { + mavenBom(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES) + } +} + +dependencies { + implementation(kotlin("stdlib")) + testImplementation("org.assertj:assertj-core:3.11.1") + testImplementation("org.amshove.kluent:kluent:1.61") + testImplementation("org.mockito:mockito-core:2.+") + testImplementation("org.junit.jupiter:junit-jupiter:5.6.2") +} + +tasks.test { + useJUnitPlatform() +} + +tasks.withType().configureEach { + kotlinOptions.suppressWarnings = true + kotlinOptions.jvmTarget = "1.8" +} diff --git a/buildSrc/src/main/kotlin/mcspring-docs.gradle.kts b/buildSrc/src/main/kotlin/mcspring-docs.gradle.kts new file mode 100644 index 0000000..8217ace --- /dev/null +++ b/buildSrc/src/main/kotlin/mcspring-docs.gradle.kts @@ -0,0 +1,29 @@ +plugins { + id("org.jetbrains.dokka") + id("com.eden.orchidPlugin") +} + +repositories { + jcenter() +} + +dependencies { + orchidRuntimeOnly("io.github.javaeden.orchid:OrchidDocs:0.20.0") + orchidRuntimeOnly("io.github.javaeden.orchid:OrchidSourceDoc:0.20.0") + orchidRuntimeOnly("io.github.javaeden.orchid:OrchidKotlindoc:0.20.0") + orchidRuntimeOnly("io.github.javaeden.orchid:OrchidPluginDocs:0.20.0") +} + +tasks.withType(org.jetbrains.dokka.gradle.DokkaTask::class) { + outputFormat = "html" + configuration { + externalDocumentationLink { + url = uri("https://hub.spigotmc.org/javadocs/bukkit/").toURL() + url = uri("http://milkbowl.github.io/VaultAPI/").toURL() + } + } +} + +orchid { + theme = "Editorial" +} diff --git a/mcspring-api/mcspring-base/build.gradle.kts b/mcspring-api/mcspring-base/build.gradle.kts index 0126974..71c146d 100644 --- a/mcspring-api/mcspring-base/build.gradle.kts +++ b/mcspring-api/mcspring-base/build.gradle.kts @@ -3,11 +3,17 @@ plugins { } dependencies { - compile(project(":mcspring-api:mcspring-jar-loader")) - compile("org.jetbrains.kotlin:kotlin-reflect:1.3.72") - compile("org.springframework.boot:spring-boot-loader") - compile("org.springframework.boot:spring-boot-starter") - testCompile("org.springframework.boot:spring-boot-starter-test") - compileOnly("org.apache.logging.log4j:log4j-core:2.12.1") - compileOnly("org.spigotmc:spigot-api") + api("org.apache.logging.log4j:log4j-core:2.12.1") + api("org.spigotmc:spigot-api") + api("org.springframework.boot:spring-boot-loader") + api("org.springframework.boot:spring-boot-starter") + + implementation(project(":mcspring-api:mcspring-common")) + implementation(project(":mcspring-api:mcspring-jar-loader")) + implementation("org.jetbrains.kotlin:kotlin-reflect:1.3.72") + implementation("org.springframework.boot:spring-boot-loader") + implementation("org.springframework.boot:spring-boot-starter") + + testImplementation("org.springframework.boot:spring-boot-starter-test") + testImplementation("org.spigotmc:spigot-api") } diff --git a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/SpringPlugin.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/SpringPlugin.kt index b74c156..32fb2f0 100644 --- a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/SpringPlugin.kt +++ b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/SpringPlugin.kt @@ -8,7 +8,6 @@ import org.springframework.boot.Banner import org.springframework.boot.builder.SpringApplicationBuilder import org.springframework.context.ConfigurableApplicationContext import org.springframework.core.io.DefaultResourceLoader -import java.util.* internal class SpringPlugin( private val plugin: Plugin diff --git a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/SpringSpigotSupport.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/SpringSpigotSupport.kt index 5dc84f7..57c46b9 100644 --- a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/SpringSpigotSupport.kt +++ b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/SpringSpigotSupport.kt @@ -14,15 +14,14 @@ import org.springframework.beans.factory.annotation.Value import org.springframework.context.annotation.Bean import org.springframework.context.annotation.ComponentScan import org.springframework.context.annotation.Configuration -import org.springframework.context.annotation.EnableAspectJAutoProxy import org.springframework.scheduling.annotation.EnableScheduling import java.util.logging.Logger @Configuration -@ComponentScan(basePackageClasses = [SpringPlugin::class]) +@ComponentScan(basePackageClasses = [SpringSpigotSupport::class]) @EnableScheduling -@EnableAspectJAutoProxy internal open class SpringSpigotSupport { + @Bean open fun plugin(@Value("\${spigot.plugin}") pluginName: String): Plugin { return Bukkit.getPluginManager().getPlugin(pluginName)!! diff --git a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/CommandController.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/CommandController.kt deleted file mode 100644 index e0b0321..0000000 --- a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/CommandController.kt +++ /dev/null @@ -1,32 +0,0 @@ -package `in`.kyle.mcspring.command - -import org.bukkit.Bukkit -import org.bukkit.command.Command -import org.bukkit.command.CommandMap -import org.bukkit.plugin.Plugin -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean -import org.springframework.context.annotation.Bean -import org.springframework.stereotype.Controller - -@Controller -@ConditionalOnBean(Plugin::class) -internal class CommandController( - private val plugin: Plugin -) { - private val commandMap = getCommandMap() - - fun registerCommand(command: Command) { - commandMap.register(command.label, plugin.name, command) - } - - private fun getCommandMap(): CommandMap { - val bukkitCommandMap = Bukkit.getServer().javaClass.getDeclaredField("commandMap") - bukkitCommandMap.isAccessible = true - return bukkitCommandMap[Bukkit.getServer()] as CommandMap - } - - @Bean - fun commandMap(): CommandMap { - return commandMap - } -} diff --git a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/CommandResolver.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/CommandResolver.kt deleted file mode 100644 index 763885a..0000000 --- a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/CommandResolver.kt +++ /dev/null @@ -1,9 +0,0 @@ -package `in`.kyle.mcspring.command - -import org.bukkit.command.CommandSender - -interface CommandResolver { - fun makeResolver(command: Command): ParameterResolver - - data class Command(val sender: CommandSender, val args: List, val label: String) -} diff --git a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/SimpleMethodInjection.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/execution/SimpleMethodInjection.kt similarity index 94% rename from mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/SimpleMethodInjection.kt rename to mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/execution/SimpleMethodInjection.kt index 0f40f35..9dd3336 100644 --- a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/SimpleMethodInjection.kt +++ b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/execution/SimpleMethodInjection.kt @@ -1,11 +1,11 @@ -package `in`.kyle.mcspring.command +package `in`.kyle.mcspring.command.execution +import `in`.kyle.mcspring.command.resolution.ParameterResolver import org.springframework.context.annotation.Lazy import org.springframework.stereotype.Component import kotlin.reflect.KFunction import kotlin.reflect.full.isSubclassOf import kotlin.reflect.jvm.isAccessible -import kotlin.reflect.jvm.javaType @Lazy @Component diff --git a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/BukkitCommandRegistration.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/registration/BukkitCommandRegistration.kt similarity index 68% rename from mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/BukkitCommandRegistration.kt rename to mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/registration/BukkitCommandRegistration.kt index d9bbec2..8aa5fd7 100644 --- a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/BukkitCommandRegistration.kt +++ b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/registration/BukkitCommandRegistration.kt @@ -1,20 +1,20 @@ -package `in`.kyle.mcspring.command +package `in`.kyle.mcspring.command.registration +import `in`.kyle.mcspring.command.Command import org.bukkit.plugin.Plugin import org.springframework.boot.autoconfigure.condition.ConditionalOnBean import org.springframework.stereotype.Component import java.lang.reflect.Method -import org.bukkit.command.Command as BukkitCommand @Component @ConditionalOnBean(Plugin::class) internal class BukkitCommandRegistration( private val controller: CommandController, - private val commandFactory: CommandFactory + private val commandExecutorFactory: CommandExecutorFactory ) : CommandRegistration { override fun register(command: Command, method: Method, obj: Any) { - val bukkitCommand = commandFactory.makeCommand(method, obj, command.value) + val bukkitCommand = commandExecutorFactory.makeCommand(method, obj, command.value) with(bukkitCommand) { aliases = command.aliases.toList() description = command.description @@ -24,8 +24,4 @@ internal class BukkitCommandRegistration( } controller.registerCommand(bukkitCommand) } - - internal interface CommandFactory { - fun makeCommand(method: Method, obj: Any, name: String): BukkitCommand - } } diff --git a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/registration/CommandController.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/registration/CommandController.kt new file mode 100644 index 0000000..0a665fc --- /dev/null +++ b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/registration/CommandController.kt @@ -0,0 +1,19 @@ +package `in`.kyle.mcspring.command.registration + +import `in`.kyle.mcspring.common.commands.CommandMapWrapper +import org.bukkit.command.Command +import org.bukkit.plugin.Plugin +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean +import org.springframework.context.annotation.Bean +import org.springframework.stereotype.Controller + +@Controller +@ConditionalOnBean(Plugin::class) +internal class CommandController( + private val plugin: Plugin +) { + fun registerCommand(command: Command) = CommandMapWrapper.registerCommand(plugin, command) + + @Bean + fun commandMap() = CommandMapWrapper.commandMap +} diff --git a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/SimpleCommandFactory.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/registration/CommandExecutorFactory.kt similarity index 61% rename from mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/SimpleCommandFactory.kt rename to mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/registration/CommandExecutorFactory.kt index f40554e..6931f39 100644 --- a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/SimpleCommandFactory.kt +++ b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/registration/CommandExecutorFactory.kt @@ -1,6 +1,6 @@ -package `in`.kyle.mcspring.command +package `in`.kyle.mcspring.command.registration -import `in`.kyle.mcspring.command.BukkitCommandRegistration.CommandFactory +import `in`.kyle.mcspring.command.execution.SimpleMethodInjection import org.bukkit.command.Command import org.bukkit.command.CommandExecutor import org.bukkit.command.CommandSender @@ -12,13 +12,12 @@ import java.lang.reflect.Method @Component @ConditionalOnBean(Plugin::class) -open class SimpleCommandFactory( +open class CommandExecutorFactory( private val injection: SimpleMethodInjection, - private val commandResolvers: Set, private val plugin: Plugin -) : CommandFactory { +) { - override fun makeCommand(method: Method, obj: Any, name: String): Command { + fun makeCommand(method: Method, obj: Any, name: String): Command { val constructor = PluginCommand::class.java.getDeclaredConstructor(String::class.java, Plugin::class.java) constructor.isAccessible = true val command = constructor.newInstance(name, plugin) @@ -30,21 +29,19 @@ open class SimpleCommandFactory( private fun makeExecutor(method: Method, obj: Any): CommandExecutor { return CommandExecutor { commandSender: CommandSender, bukkitCommand: Command, label: String, args: Array -> try { - val command = CommandResolver.Command(commandSender, args.toList(), label) val injectionResolvers = injection.makeResolvers(listOf(commandSender, args, label, bukkitCommand)) - val contextParameterResolvers = - commandResolvers.map { it.makeResolver(command) }.plus(injectionResolvers) +// val contextParameterResolvers = +// commandResolvers.map { it.makeResolver(command) }.plus(injectionResolvers) val types = method.parameterTypes.toList() - val parameters = injection.getParameters(types, contextParameterResolvers) - val result = method.invoke(obj, *parameters) - if (result !is Unit) { - commandSender.sendMessage(result.toString()) - } +// val parameters = injection.getParameters(types, contextParameterResolvers) +// val result = method.invoke(obj, *parameters) +// if (result !is Unit) { +// commandSender.sendMessage(result.toString()) +// } } catch (exception: RuntimeException) { - throw RuntimeException("Could not invoke method " + method.name, - exception) + throw RuntimeException("Could not invoke method ${method.name}", exception) } true } diff --git a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/CommandRegistration.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/registration/CommandRegistration.kt similarity index 58% rename from mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/CommandRegistration.kt rename to mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/registration/CommandRegistration.kt index 6d27a2f..705ce8e 100644 --- a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/CommandRegistration.kt +++ b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/registration/CommandRegistration.kt @@ -1,5 +1,6 @@ -package `in`.kyle.mcspring.command +package `in`.kyle.mcspring.command.registration +import `in`.kyle.mcspring.command.Command import java.lang.reflect.Method interface CommandRegistration { diff --git a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/CommandScanner.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/registration/CommandScanner.kt similarity index 83% rename from mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/CommandScanner.kt rename to mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/registration/CommandScanner.kt index 84a07e9..c91dc67 100644 --- a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/CommandScanner.kt +++ b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/registration/CommandScanner.kt @@ -1,5 +1,6 @@ -package `in`.kyle.mcspring.command +package `in`.kyle.mcspring.command.registration +import `in`.kyle.mcspring.command.Command import `in`.kyle.mcspring.util.SpringScanner import org.springframework.context.ApplicationContext import org.springframework.context.ApplicationContextAware @@ -17,6 +18,7 @@ internal class CommandScanner( override fun setApplicationContext(ctx: ApplicationContext) { val scan = scanner.scanMethods(Command::class.java) scan.filterKeys { it !in registeredCommands } + .filterKeys { it.returnType in arrayOf(Void::class.java, String::class.java) } .forEach { (key, value) -> val command = key.getAnnotation(Command::class.java) commandRegistration.register(command, key, value) diff --git a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/ParameterResolver.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/resolution/ParameterResolver.kt similarity index 62% rename from mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/ParameterResolver.kt rename to mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/resolution/ParameterResolver.kt index 44ac8a3..aa62af0 100644 --- a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/ParameterResolver.kt +++ b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/resolution/ParameterResolver.kt @@ -1,4 +1,4 @@ -package `in`.kyle.mcspring.command +package `in`.kyle.mcspring.command.resolution interface ParameterResolver { fun resolve(parameter: Class<*>): Any? diff --git a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/SimpleSpringParameterResolver.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/resolution/SimpleSpringParameterResolver.kt similarity index 91% rename from mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/SimpleSpringParameterResolver.kt rename to mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/resolution/SimpleSpringParameterResolver.kt index 9596b5d..0a927b8 100644 --- a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/SimpleSpringParameterResolver.kt +++ b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/resolution/SimpleSpringParameterResolver.kt @@ -1,4 +1,4 @@ -package `in`.kyle.mcspring.command +package `in`.kyle.mcspring.command.resolution import org.springframework.beans.factory.NoSuchBeanDefinitionException import org.springframework.context.ApplicationContext diff --git a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/util/SpringScanner.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/util/SpringScanner.kt index 240465f..e81889e 100644 --- a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/util/SpringScanner.kt +++ b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/util/SpringScanner.kt @@ -6,16 +6,16 @@ import org.springframework.stereotype.Component import java.lang.reflect.Method @Component -internal class SpringScanner( +class SpringScanner( private val context: ApplicationContext ) { - fun scanMethods(annotation: Class): Map { - val methods = mutableMapOf() + fun scanMethods(vararg annotations: Class): Map { + val methods = mutableMapOf() for (beanName in context.beanDefinitionNames) { val obj = context.getBean(beanName) for (method in getRealClass(obj).declaredMethods) { - if (method.isAnnotationPresent(annotation)) { + if (annotations.any { method.isAnnotationPresent(it) }) { methods[method] = obj } } diff --git a/mcspring-api/mcspring-chat/README.md b/mcspring-api/mcspring-chat/README.md new file mode 100644 index 0000000..0e32b36 --- /dev/null +++ b/mcspring-api/mcspring-chat/README.md @@ -0,0 +1,15 @@ +mcspring-chat +--- +Provides some basic wrappers around the TextComponent API + +### Usage + +```kotlin +player.sendMessage { + "hover for details".red() hover { + ("click to run ".gray()) + ("/test".yellow()) + } command "/test" +} +``` + +_This may be used independently of mcspring_ diff --git a/mcspring-api/mcspring-chat/build.gradle.kts b/mcspring-api/mcspring-chat/build.gradle.kts new file mode 100644 index 0000000..6de75a4 --- /dev/null +++ b/mcspring-api/mcspring-chat/build.gradle.kts @@ -0,0 +1,3 @@ +dependencies { + api("org.spigotmc:spigot-api") +} diff --git a/mcspring-api/mcspring-chat/src/main/kotlin/in/kyle/mcspring/chat/ColorHelpers.kt b/mcspring-api/mcspring-chat/src/main/kotlin/in/kyle/mcspring/chat/ColorHelpers.kt new file mode 100644 index 0000000..7c6c8e2 --- /dev/null +++ b/mcspring-api/mcspring-chat/src/main/kotlin/in/kyle/mcspring/chat/ColorHelpers.kt @@ -0,0 +1,54 @@ +package `in`.kyle.mcspring.chat + +import `in`.kyle.mcspring.chat.StringSupport.color +import `in`.kyle.mcspring.chat.TextComponentSupport.color +import net.md_5.bungee.api.chat.TextComponent +import org.bukkit.ChatColor.* + +fun String.translateColorCodes() = translateAlternateColorCodes('&', this) + +fun String.black() = this color BLACK +fun String.darkBlue() = this color DARK_BLUE +fun String.darkGreen() = this color DARK_GREEN +fun String.darkAqua() = this color DARK_AQUA +fun String.darkRed() = this color DARK_RED +fun String.darkPurple() = this color DARK_PURPLE +fun String.gold() = this color GOLD +fun String.gray() = this color GRAY +fun String.darkGray() = this color DARK_GRAY +fun String.blue() = this color BLUE +fun String.green() = this color GREEN +fun String.aqua() = this color AQUA +fun String.red() = this color RED +fun String.lightPurple() = this color LIGHT_PURPLE +fun String.yellow() = this color YELLOW +fun String.white() = this color WHITE +fun String.magic() = this color MAGIC +fun String.bold() = this color BOLD +fun String.strikethrough() = this color STRIKETHROUGH +fun String.underline() = this color UNDERLINE +fun String.italic() = this color ITALIC +fun String.reset() = this color RESET + +fun TextComponent.black() = this color BLACK +fun TextComponent.darkBlue() = this color DARK_BLUE +fun TextComponent.darkGreen() = this color DARK_GREEN +fun TextComponent.darkAqua() = this color DARK_AQUA +fun TextComponent.darkRed() = this color DARK_RED +fun TextComponent.darkPurple() = this color DARK_PURPLE +fun TextComponent.gold() = this color GOLD +fun TextComponent.gray() = this color GRAY +fun TextComponent.darkGray() = this color DARK_GRAY +fun TextComponent.blue() = this color BLUE +fun TextComponent.green() = this color GREEN +fun TextComponent.aqua() = this color AQUA +fun TextComponent.red() = this color RED +fun TextComponent.lightPurple() = this color LIGHT_PURPLE +fun TextComponent.yellow() = this color YELLOW +fun TextComponent.white() = this color WHITE +fun TextComponent.magic() = this color MAGIC +fun TextComponent.bold() = this color BOLD +fun TextComponent.strikethrough() = this color STRIKETHROUGH +fun TextComponent.underline() = this color UNDERLINE +fun TextComponent.italic() = this color ITALIC +fun TextComponent.reset() = this color RESET diff --git a/mcspring-api/mcspring-chat/src/main/kotlin/in/kyle/mcspring/chat/Message.kt b/mcspring-api/mcspring-chat/src/main/kotlin/in/kyle/mcspring/chat/Message.kt new file mode 100644 index 0000000..436a3dc --- /dev/null +++ b/mcspring-api/mcspring-chat/src/main/kotlin/in/kyle/mcspring/chat/Message.kt @@ -0,0 +1,89 @@ +package `in`.kyle.mcspring.chat + +import `in`.kyle.mcspring.chat.TextComponentSupport.color +import `in`.kyle.mcspring.chat.TextComponentSupport.command +import `in`.kyle.mcspring.chat.TextComponentSupport.hover +import `in`.kyle.mcspring.chat.TextComponentSupport.suggest +import net.md_5.bungee.api.chat.ClickEvent +import net.md_5.bungee.api.chat.HoverEvent +import net.md_5.bungee.api.chat.TextComponent +import org.bukkit.ChatColor +import org.bukkit.entity.Player + +object PlayerSupport { + fun Player.sendMessage(vararg components: TextComponent) { + spigot().sendMessage(*components) + } + + fun Player.sendMessage(lambda: () -> TextComponent) { + spigot().sendMessage(lambda()) + } +} + +object StringSupport { + + fun String.toTextComponent(): TextComponent { + val player: Player + return TextComponent(translateColorCodes()) + } + + infix fun String.color(color: ChatColor): TextComponent { + return toTextComponent() color color + } + + infix fun String.hover(hover: String): TextComponent { + return toTextComponent() hover hover + } + + infix fun String.command(command: String): TextComponent { + return toTextComponent() command command + } + + infix fun String.suggest(command: String): TextComponent { + return toTextComponent() suggest command + } + + operator fun String.plus(textComponent: TextComponent): TextComponent { + return TextComponent(toTextComponent(), textComponent) + } +} + +object TextComponentSupport { + infix fun TextComponent.color(color: ChatColor): TextComponent { + this.color = color.asBungee() + return this + } + + infix fun TextComponent.hover(hover: TextComponent): TextComponent { + this.hoverEvent = HoverEvent(HoverEvent.Action.SHOW_TEXT, arrayOf(hover)) + return this + } + + infix fun TextComponent.hover(lambda: () -> TextComponent): TextComponent { + return hover(lambda()) + } + + infix fun TextComponent.hover(hover: String): TextComponent { + val hoverComponent = TextComponent(hover) + this.hoverEvent = HoverEvent(HoverEvent.Action.SHOW_TEXT, arrayOf(hoverComponent)) + return this + } + + infix fun TextComponent.command(command: String): TextComponent { + this.clickEvent = ClickEvent(ClickEvent.Action.RUN_COMMAND, command) + return this + } + + infix fun TextComponent.suggest(command: String): TextComponent { + this.clickEvent = ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, command) + return this + } + + operator fun TextComponent.plus(text: String): TextComponent { + return TextComponent(this, TextComponent(text)) + } + + operator fun TextComponent.plus(text: TextComponent): TextComponent { + return TextComponent(this, text) + } +} diff --git a/mcspring-api/mcspring-chat/src/test/kotlin/in/kyle/mcspring/chat/TestMessage.kt b/mcspring-api/mcspring-chat/src/test/kotlin/in/kyle/mcspring/chat/TestMessage.kt new file mode 100644 index 0000000..4b86e71 --- /dev/null +++ b/mcspring-api/mcspring-chat/src/test/kotlin/in/kyle/mcspring/chat/TestMessage.kt @@ -0,0 +1,59 @@ +package `in`.kyle.mcspring.chat + +import `in`.kyle.mcspring.chat.StringSupport.color +import `in`.kyle.mcspring.chat.StringSupport.command +import `in`.kyle.mcspring.chat.StringSupport.hover +import `in`.kyle.mcspring.chat.StringSupport.suggest +import `in`.kyle.mcspring.chat.StringSupport.toTextComponent +import `in`.kyle.mcspring.chat.TextComponentSupport.plus +import net.md_5.bungee.api.ChatColor +import net.md_5.bungee.api.chat.ClickEvent +import net.md_5.bungee.api.chat.ClickEvent.Action.* +import net.md_5.bungee.api.chat.ComponentBuilder +import net.md_5.bungee.api.chat.HoverEvent +import net.md_5.bungee.api.chat.HoverEvent.Action.SHOW_TEXT +import net.md_5.bungee.api.chat.TextComponent +import org.amshove.kluent.shouldBeEqualTo +import org.bukkit.ChatColor.RED +import org.junit.jupiter.api.Test + +class TestMessage { + + @Test + fun testHover() { + "test" hover "hover" shouldBeEqualTo + TextComponent("test").apply { + hoverEvent = HoverEvent(SHOW_TEXT, arrayOf(TextComponent("hover"))) + } + } + + @Test + fun testColor() { + "test" color RED shouldBeEqualTo + ComponentBuilder("test").color(ChatColor.RED).create()[0] + } + + @Test + fun testPlus() { + val component = "hello ".toTextComponent() + "world" + // ofc the equality checking doesn't work... + component.toString() shouldBeEqualTo + TextComponent(TextComponent("hello "), TextComponent("world")).toString() + } + + @Test + fun testCommand() { + "test" command "/test" shouldBeEqualTo + TextComponent("test").apply { + clickEvent = ClickEvent(RUN_COMMAND, "/test") + } + } + + @Test + fun testSuggest() { + "test" suggest "/test" shouldBeEqualTo + TextComponent("test").apply { + clickEvent = ClickEvent(SUGGEST_COMMAND, "/test") + } + } +} diff --git a/mcspring-api/mcspring-commands-dsl/README.md b/mcspring-api/mcspring-commands-dsl/README.md new file mode 100644 index 0000000..ed89097 --- /dev/null +++ b/mcspring-api/mcspring-commands-dsl/README.md @@ -0,0 +1,117 @@ +mcspring-commands-dsl +--- +_A sane way to write commands_ + +### Project Goals +One of the worst parts of writing plugins is writing command parsers. + +One of the primary problems when writing parsers as a conditional tree is the tendency of depth + spiraling out of control. What could be a simple command could turn into a nested cataclysm of conditional branching. + +Here's some real code that I wrote not too long ago. The problems here are not worth listing out. +```kotlin +if (party.hasAdminPermissions(player)) { + if (args.size == 2) { // party size + val size = args[1].toIntOrNull() + if (size != null) { + if (size <= 12) { + if (size >= party.members.size) { + party.size = size + showPartyInfo(player, party) + } else { + player.sendMessage("Your party has too many members for that size. You must kick some players first. ($size < ${party.members.size}") + } + } else { + player.sendMessage("Max party size is 12") + } + } else { + player.sendMessage("Invalid party size: ${args[1]}") + } + } else { + player.sendMessage("Usage: /$label size ") + } +} else { + player.sendMessage("You must be a party admin to do this") +} +``` + +Flattening this mess is easy with some non-standard control flow. Next is to clean up the + redundant mess of if-return blocks. I started by commenting the type of task being performed by + each block. This will be useful later. + +```kotlin +// Condition +if (!party.hasAdminPermissions(player)) { + player.sendMessage("You must be a party admin to do this") + return +} + +// Usage message +if (args.size != 2) { // party size + player.sendMessage("Usage: /$label size ") + return +} + +// Argument parser +val size = args[1].toIntOrNull() +if (size == null) { + player.sendMessage("Invalid party size: ${args[1]}") + return +} + +// Argument condition +if (size > 12) { // TODO config + player.sendMessage("Max party size is 12") + return +} + +// Argument condition +if (size < party.members.size) { + player.sendMessage("Your party has too many members for that size. You must kick some players first. ($size < ${party.members.size}") + return +} + +// Command execution +party.size = size +showPartyInfo(player, party) +``` + +These statements are often highly repetitive in nature. Abstracting out the general concepts to a + DSL is more-or-less straightforward: + +```kotlin +require(party.hasAdminPermissions(sender as Player)) { + // execution + message("You must be a party admin to do this") + // Dead end +} + +val size by intArg { + parser { + lessThanEqual(12) { + // execution + message("Invalid party size ${args[1]}") + // Dead end + } + lessThanEqual(party.members.size) { + // execution + message("Your party has too many members for that size. You must kick some players first. (${args[1]} < ${party.members.size}") + // Dead end + } + } +} + +then { + // execution + party.size = size + showPartyInfo(player, party) +} +``` + +This approach has some neat advantages over a traditional command structure. First, there is a + clear structure to how the command is parsed. Second, this format natively lends itself to + automatic tab completion. That means that any command written in this DSL format is natively + tab-completable. + +Tab completions are handled by running the command DSL but ignoring execution sections (see + comments). diff --git a/mcspring-api/mcspring-commands-dsl/build.gradle.kts b/mcspring-api/mcspring-commands-dsl/build.gradle.kts new file mode 100644 index 0000000..d065866 --- /dev/null +++ b/mcspring-api/mcspring-commands-dsl/build.gradle.kts @@ -0,0 +1,4 @@ +dependencies { + implementation(project(":mcspring-api:mcspring-base")) + implementation(project(":mcspring-api:mcspring-common")) +} diff --git a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandBuilder.kt b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandBuilder.kt new file mode 100644 index 0000000..a7b0774 --- /dev/null +++ b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandBuilder.kt @@ -0,0 +1,217 @@ +package `in`.kyle.mcspring.commands.dsl + +import org.bukkit.command.CommandSender +import org.bukkit.command.ConsoleCommandSender +import org.bukkit.entity.Player + +@DslMarker +annotation class CommandParserBuilder + +class CommandMeta { + lateinit var name: String + var description: String = "" + var usageMessage: String = "" + var permission: String = "" + var permissionMessage: String = "" + var aliases: List = listOf() + lateinit var executor: CommandExecutor + + fun preRegister() { + require(this::name.isInitialized) {"Command name not set"} + require(this::executor.isInitialized) {"Command executor not set for command $name"} + } +} + +infix fun CommandMeta.executor(lambda: CommandBuilder.() -> Unit): CommandMeta { + executor = commandExecutor(lambda) + return this +} + +fun command(lambda: CommandMeta.() -> Unit) = CommandMeta().apply { lambda(this) } + +data class CommandContext( + val sender: CommandSender, + val label: String, + val args: List, + val tabCompletions: MutableList = mutableListOf(), + internal var argIndex: Int = 0, + internal val runExecutors: Boolean = true +) { + fun nextArg() = args[argIndex] +} + +data class CommandExecutor(val provider: (CommandContext) -> ParsedCommand) + +fun commandExecutor(lambda: CommandBuilder.() -> Unit) = + CommandExecutor { context: CommandContext -> + CommandBuilder(context).let { + try { + it.lambda() + } catch (e: ContextReciever.BreakParseException) { + } + it.build() + } + } + +@CommandParserBuilder +class CommandBuilder(context: CommandContext) : ContextReciever(context) { + + private val parsedArgs: MutableList> = mutableListOf() + + fun stringArg(lambda: ValueBuilder.() -> Unit = {}) = valueArg(lambda) { StringParser(context, it) } + fun intArg(lambda: ValueBuilder.() -> Unit) = valueArg(lambda) { IntParser(context, it) } + fun doubleArg(lambda: ValueBuilder.() -> Unit) = valueArg(lambda) { DoubleParser(context, it) } + fun booleanArg(lambda: ValueBuilder.() -> Unit) = valueArg(lambda) { BooleanParser(context, it) } + fun playerArg(lambda: ValueBuilder.() -> Unit = {}) = valueArg(lambda) { PlayerParser(context, it) } + + fun mapArg(lambda: ValueBuilder>.() -> Unit) = valueArg(lambda) { MapParser(context, it) } + + fun subcommand(lambda: SubcommandBuilder.() -> Unit) { + arg(SubcommandBuilder(context), lambda) + } + + fun require(predicate: () -> Boolean, lambda: ContextReciever.() -> Unit = {}) = require(predicate(), lambda) + + fun require(value: Boolean, lambda: ContextReciever.() -> Unit) { + if (!value) { + lambda(this) + complete() + } + } + + fun requirePlayer(lambda: ContextReciever.() -> Unit) = require({ sender is Player }, lambda) + + fun requireConsole(lambda: ContextReciever.() -> Unit) = require({ sender is ConsoleCommandSender }, lambda) + + private fun > valueArg( + lambda: ValueBuilder.() -> Unit, + parserSupplier: (String) -> P + ) = arg(ValueBuilder(context, parserSupplier), lambda) + + private fun > arg(builder: T, lambda: T.() -> Unit): Lazy { + context.tabCompletions.clear() + val arg = builder.apply(lambda).build() + parsedArgs.add(arg) + context.argIndex++ + return lazyOf(arg.returnValue) + } + + fun then(lambda: ContextReciever.() -> Unit) { + if (context.runExecutors) { + lambda(this) + complete() + } + } + + fun build(): ParsedCommand = ParsedCommand(parsedArgs) +} + +@CommandParserBuilder +abstract class ArgBuilder( + context: CommandContext +) : ContextReciever(context) { + + internal open var returnValue: R? = null + + open fun invalid(lambda: ContextReciever.(arg: String) -> Unit) { + if (returnValue == null && context.runExecutors) { + lambda(context.nextArg()) + complete() + } + } + + abstract fun build(): ValueArg +} + +class ValueBuilder>( + context: CommandContext, + private val parserSupplier: (String) -> T +) : ArgBuilder(context) { + + private var hasRunParser = false + + fun default(lambda: ContextReciever.() -> R?) { + if (returnValue == null) { + returnValue = lambda() + } + } + + fun parser(lambda: T.() -> Unit) { + hasRunParser = true + if (returnValue == null) { + val parser = parserSupplier(context.nextArg()) + parser.lambda() + returnValue = parser.returnValue + } + } + + fun missing(lambda: ContextReciever.() -> Unit) { + if (!hasNextArg() && context.runExecutors) { + lambda() + complete() + } + } + + override fun invalid(lambda: ContextReciever.(arg: String) -> Unit) { + runBaseParse() + super.invalid(lambda) + } + + private fun runBaseParse() { + if (!hasRunParser) { + parser {} + } + } + + override fun build(): ValueArg { + runBaseParse() + if (returnValue != null) { + return ValueArg(returnValue!!) + } else { + error("error parsing $context") + } + } +} + +class SubcommandBuilder(context: CommandContext) : ArgBuilder(context) { + + private val subCommands: MutableMap, ParsedCommand> = mutableMapOf() + + fun on(vararg values: String, commandExecutor: CommandExecutor) = on({ it in values }, values.toList(), commandExecutor) + fun on(vararg values: String, command: CommandBuilder.() -> Unit) { + on({ it in values }, values.toList(), commandExecutor(command)) + } + + fun on( + predicate: (String) -> Boolean, + tabCompletions: Iterable, + commandExecutor: CommandExecutor + ) { + context.tabCompletions.addAll(tabCompletions) + val argString = context.nextArg() + if (predicate(argString)) { + returnValue = argString + context.argIndex++ + subCommands[tabCompletions.toList()] = commandExecutor.provider(context) + complete() + } else { + val fakeContext = context.copy(runExecutors = false) + val parsed = commandExecutor.provider(fakeContext) + subCommands[tabCompletions.toList()] = parsed + } + } + + fun missing(lambda: SubcommandMissingBuilder.() -> Unit) { + if (!hasNextArg() && context.runExecutors) { + lambda(SubcommandMissingBuilder(subCommands, context)) + complete() + } + } + + override fun build() = SubCommandArg(subCommands, returnValue!!) + + class SubcommandMissingBuilder( + val subCommands: Map, ParsedCommand>, + context: CommandContext + ) : ContextReciever(context) +} diff --git a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandModel.kt b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandModel.kt new file mode 100644 index 0000000..fd21875 --- /dev/null +++ b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandModel.kt @@ -0,0 +1,36 @@ +package `in`.kyle.mcspring.commands.dsl + +import org.bukkit.ChatColor + +open class ContextReciever(val context: CommandContext) { + val sender = context.sender + val label = context.label + val args = context.args + + fun message(message: String) { + if (context.runExecutors) { + sender.sendMessage(ChatColor.translateAlternateColorCodes('&', message)) + } + } + + fun hasNextArg() = context.argIndex < context.args.size + + fun complete(): Nothing = throw BreakParseException() + + class BreakParseException : RuntimeException() +} + +data class ParsedCommand( + val args: List> +) + +open class ValueArg( + val returnValue: R +) + +class SubCommandArg( + val subCommands: Map, ParsedCommand>, + returnValue: String +): ValueArg(returnValue) + + diff --git a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandParsers.kt b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandParsers.kt new file mode 100644 index 0000000..0b3c2fe --- /dev/null +++ b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandParsers.kt @@ -0,0 +1,118 @@ +package `in`.kyle.mcspring.commands.dsl + +import org.bukkit.Bukkit +import org.bukkit.entity.Player + +class RunInvalidBlock : RuntimeException() + +typealias Error = (String) -> Unit + +@CommandParserBuilder +abstract class BaseParser( + context: CommandContext, + private val stringArg: String? +) : ContextReciever(context) { + + internal var returnValue: R? = null + + protected fun mapped(hints: Iterable = emptyList(), mapper: (String) -> R?) { + context.tabCompletions.addAll(hints) + if (returnValue == null) { + returnValue = stringArg?.let { mapper(it) } + } + } + + protected fun require( + hints: List = emptyList(), + predicate: (R) -> Boolean, + error: Error + ) { + context.tabCompletions.addAll(hints) + if (returnValue != null && !predicate(returnValue!!)) { + returnValue = null + throw RunInvalidBlock() + } + if (stringArg != null && returnValue == null && context.runExecutors) { + error(stringArg) + complete() + } + } +} + +class MapParser(context: CommandContext, stringArg: String) : BaseParser(context, stringArg) { + fun map(vararg pairs: Pair) = map(mapOf(*pairs)) + + fun map(map: Map) = mapped(map.keys) { map[it] } +} + +class StringParser(context: CommandContext, stringArg: String) : BaseParser(context, stringArg) { + init { + returnValue = stringArg + } + + fun anyOf(vararg string: String, error: Error) = anyOf(string.toList(), error) + fun anyOf(strings: List, error: Error) = require(strings, { it in strings }, error) + fun anyString() = mapped { it } + fun anyStringMatching(regex: Regex, error: Error) = require(predicate = { it.matches(regex) }, error = error) + fun anyInt(error: Error) = require(predicate = { it.toIntOrNull() != null }, error = error) + fun anyDouble(error: Error) = require(predicate = { it.toDoubleOrNull() != null }, error = error) + fun anyFloat(error: Error) = require(predicate = { it.toFloatOrNull() != null }, error = error) + fun anyByte(error: Error) = require(predicate = { it.toByteOrNull() != null }, error = error) + fun anyShort(error: Error) = require(predicate = { it.toShortOrNull() != null }, error = error) + fun anyBoolean(error: Error) = anyOf("true", "false", error = error) +} + +abstract class NumberParser(context: CommandContext, stringArg: String) : BaseParser(context, stringArg) + where T : Number, + T : Comparable { + + init { + returnValue = parse(stringArg) + } + + internal abstract fun toNumber(s: String): T? + internal abstract fun zero(): T + internal abstract fun parse(s: String): T? + + fun between(lower: T, upper: T, error: Error) = require(predicate = { it > lower && it < upper }, error = error) + fun greaterThan(value: T, error: Error) = require(predicate = { it > value }, error = error) + fun greaterThanEqual(value: T, error: Error) = require(predicate = { it >= value }, error = error) + fun lessThan(value: T, error: Error) = require(predicate = { it < value }, error = error) + fun lessThanEqual(value: T, error: Error) = require(predicate = { it <= value }, error = error) + fun positive(error: Error) = require(predicate = { it > zero() }, error = error) + fun nonZero(value: T, error: Error) = require(predicate = { it != zero() }, error = error) +} + +class IntParser(context: CommandContext, stringArg: String) : NumberParser(context, stringArg) { + override fun zero() = 0 + override fun toNumber(s: String): Int? = s.toIntOrNull() + fun even(error: Error) = require(predicate = { it % 2 == 0 && it != 0 }, error = error) + fun odd(error: Error) = require(predicate = { it % 2 == 1 && it != 0 }, error = error) + fun between(range: IntRange, error: Error) = require(predicate = { it in range }, error = error) + override fun parse(s: String) = s.toIntOrNull() +} + +class DoubleParser(context: CommandContext, stringArg: String) : NumberParser(context, stringArg) { + override fun zero() = 0.0 + override fun toNumber(s: String): Double? = s.toDoubleOrNull() + override fun parse(s: String) = s.toDoubleOrNull() +} + +class BooleanParser(context: CommandContext, stringArg: String) : BaseParser(context, stringArg) { + init { + val booleans = listOf("true", "false") + context.tabCompletions.addAll(booleans) + returnValue = stringArg.toBoolean().takeIf { stringArg in booleans } + } +} + +class PlayerParser( + context: CommandContext, + stringArg: String +) : BaseParser(context, stringArg) { + init { + val players = Bukkit.getOnlinePlayers() + context.tabCompletions.addAll(players.map { it.name }) + returnValue = Bukkit.getPlayer(stringArg) + } +} diff --git a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandUtils.kt b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandUtils.kt new file mode 100644 index 0000000..45defb5 --- /dev/null +++ b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandUtils.kt @@ -0,0 +1,25 @@ +package `in`.kyle.mcspring.commands.dsl + +import `in`.kyle.mcspring.common.commands.CommandMapWrapper +import org.bukkit.command.Command +import org.bukkit.command.CommandSender +import org.bukkit.plugin.Plugin + +object CommandUtils { + + fun register(plugin: Plugin, meta: CommandMeta) { + meta.preRegister() + CommandMapWrapper.registerCommand(plugin, object : Command(meta.name, meta.description, meta.usageMessage, meta.aliases) { + override fun execute( + sender: CommandSender, + commandLabel: String, + args: Array + ): Boolean { + val provider = meta.executor.provider + val context = CommandContext(sender, label, args.toList()) + provider(context) + return true + } + }) + } +} diff --git a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/mcspring/CommandDslRegistration.kt b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/mcspring/CommandDslRegistration.kt new file mode 100644 index 0000000..534f814 --- /dev/null +++ b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/mcspring/CommandDslRegistration.kt @@ -0,0 +1,45 @@ +package `in`.kyle.mcspring.commands.dsl.mcspring + +import `in`.kyle.mcspring.command.Command +import `in`.kyle.mcspring.commands.dsl.CommandExecutor +import `in`.kyle.mcspring.commands.dsl.CommandMeta +import `in`.kyle.mcspring.commands.dsl.CommandUtils +import `in`.kyle.mcspring.util.SpringScanner +import org.bukkit.plugin.Plugin +import org.springframework.context.ApplicationContext +import org.springframework.context.ApplicationContextAware +import org.springframework.stereotype.Component +import java.lang.reflect.Method + +@Component +open class CommandDslRegistration( + private val plugin: Plugin, + private val scanner: SpringScanner +) : ApplicationContextAware { + + private val registeredCommands: MutableSet = mutableSetOf() + + override fun setApplicationContext(ctx: ApplicationContext) { + scanner.scanMethods(Command::class.java).filterKeys { it !in registeredCommands } + .filterKeys { it.returnType == CommandExecutor::class } + .forEach { (key, value) -> + val command = key.getAnnotation(Command::class.java) + val meta = CommandMeta().apply { + name = command.value + description = command.description + usageMessage = command.usage + permission = command.permission + permissionMessage = command.permissionMessage + aliases = command.aliases.toMutableList() + executor = key.invoke(value) as CommandExecutor + } + CommandUtils.register(plugin, meta) + } + scanner.scanMethods().filterKeys { it !in registeredCommands } + .filterKeys { it.returnType == CommandMeta::class } + .forEach { (key, value) -> + val meta = key.invoke(value) as CommandMeta + CommandUtils.register(plugin, meta) + } + } +} diff --git a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/CommandTestSupport.kt b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/CommandTestSupport.kt new file mode 100644 index 0000000..194c5b2 --- /dev/null +++ b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/CommandTestSupport.kt @@ -0,0 +1,31 @@ +package `in`.kyle.mcspring.commands.dsl + +import org.bukkit.entity.Player +import org.mockito.ArgumentMatchers +import org.mockito.Mockito.doAnswer +import org.mockito.Mockito.mock + +object CommandTestSupport { + + fun runCommand(command: String, lambda: CommandBuilder.() -> Unit): String { + val (context, messages) = makeContext(command) + commandExecutor(lambda).provider(context) + return messages.joinToString("\n") + } + + fun makeContext(command: String): Pair> { + val (player, messages) = makeTestPlayer() + val args = command.split(" ").filter { it.isNotBlank() } + val context = CommandContext(player, args.getOrElse(0) { "" }, args.toMutableList()) + return Pair(context, messages) + } + + private fun makeTestPlayer(): Pair> { + val sender = mock(Player::class.java) + val messages = mutableListOf() + doAnswer { messages.add(it.getArgument(0)); null } + .`when`(sender) + .sendMessage(ArgumentMatchers.anyString()) + return Pair(sender, messages) + } +} diff --git a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestBaseRequirementBuilder.kt b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestBaseRequirementBuilder.kt new file mode 100644 index 0000000..486d4e8 --- /dev/null +++ b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestBaseRequirementBuilder.kt @@ -0,0 +1,62 @@ +package `in`.kyle.mcspring.commands.dsl.builders + +import `in`.kyle.mcspring.commands.dsl.CommandBuilder +import `in`.kyle.mcspring.commands.dsl.CommandTestSupport.makeContext +import `in`.kyle.mcspring.commands.dsl.commandExecutor +import org.amshove.kluent.shouldBeEqualTo +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.fail + +class TestBaseRequirementBuilder { + + @Test + fun testRequirePass() { + testCommand("test") { + require({ true }) { fail("should not run") } + } + } + + @Test + fun testRequireFail() { + testCommand("test") { + require({ false }) { message("should run") } + } shouldBeEqualTo "should run" + } + + @Test + fun testDefaultSet() { + testCommand("") { + val test by stringArg { + default { "test" } + default { "other" } + } + test shouldBeEqualTo "test" + } + } + + @Test + fun testMissing() { + testCommand("") { + val test by stringArg { + missing { message("should run") } + invalid { fail("should not run") } + } + } shouldBeEqualTo "should run" + } + + @Test + fun testInvalid() { + testCommand("test") { + val test by intArg { + missing { fail("should not run") } + invalid { message("should run") } + } + } shouldBeEqualTo "should run" + } + + private fun testCommand(commandString: String, lambda: CommandBuilder.() -> Unit): String { + val (context, messages) = makeContext(commandString) + commandExecutor(lambda).provider(context) + return messages.joinToString(" ") + } +} diff --git a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestIntRequirementBuilder.kt b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestIntRequirementBuilder.kt new file mode 100644 index 0000000..36a7beb --- /dev/null +++ b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestIntRequirementBuilder.kt @@ -0,0 +1,20 @@ +package `in`.kyle.mcspring.commands.dsl.builders + +import `in`.kyle.mcspring.commands.dsl.CommandTestSupport.runCommand +import org.amshove.kluent.shouldBeEqualTo +import org.junit.jupiter.api.Test +import org.assertj.core.api.Assertions.fail + +class TestIntRequirementBuilder { + + @Test + fun testInt() { + runCommand("123") { + val arg0 by intArg { + invalid { fail("should not run") } + missing { fail("should not run") } + } + arg0 shouldBeEqualTo 123 + } + } +} diff --git a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestOnRequirementBuilder.kt b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestOnRequirementBuilder.kt new file mode 100644 index 0000000..8cf0bcb --- /dev/null +++ b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestOnRequirementBuilder.kt @@ -0,0 +1,44 @@ +package `in`.kyle.mcspring.commands.dsl.builders + +import `in`.kyle.mcspring.commands.dsl.CommandTestSupport.runCommand +import org.amshove.kluent.shouldBeEqualTo +import org.assertj.core.api.Assertions.fail +import org.junit.jupiter.api.Test + +class TestOnRequirementBuilder { + + @Test + fun testSubcommandMissing() { + runCommand("") { + subcommand { + missing { message("should run") } + invalid { fail("should not run") } + } + fail("should not run") + } shouldBeEqualTo "should run" + } + + @Test + fun testSubcommandInvalid() { + runCommand("arg1") { + subcommand { + missing { fail("should not run") } + invalid { message("should run") } + } + fail("should not run") + } shouldBeEqualTo "should run" + } + + @Test + fun testSubcommandSimple() { + runCommand("sub1") { + subcommand { + on("sub1") { + context.argIndex shouldBeEqualTo 1 + } + missing { fail("should not run") } + invalid { fail("should not run") } + } + } + } +} diff --git a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestOptionRequirementBuilder.kt b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestOptionRequirementBuilder.kt new file mode 100644 index 0000000..aa7010e --- /dev/null +++ b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestOptionRequirementBuilder.kt @@ -0,0 +1,40 @@ +package `in`.kyle.mcspring.commands.dsl.builders + +class TestOptionRequirementBuilder { + +// @Test +// fun testOptionSimple() { +// runCommand("a") { +// with { +// options("a", mapping = true) +// invalid { fail("should not run") } +// missing { fail("should not run") } +// } shouldBe true +// context.isNotComplete() shouldBeEqualTo true +// } shouldBeEqualTo "" +// } +// +// @Test +// fun testOptionInvalid() { +// runCommand("asd") { +// with { +// options("a", mapping = true) +// invalid { message("should run") } +// missing { fail("should not run") } +// } shouldBe null +// context.complete shouldBeEqualTo true +// } shouldBeEqualTo "should run" +// } +// +// @Test +// fun testOptionMissing() { +// runCommand("") { +// with { +// options("a", mapping = true) +// invalid { fail("should not run") } +// missing { message("should run") } +// } shouldBe null +// context.complete shouldBeEqualTo true +// } shouldBeEqualTo "should run" +// } +} diff --git a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestStringRequirementBuilder.kt b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestStringRequirementBuilder.kt new file mode 100644 index 0000000..f45ebe7 --- /dev/null +++ b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestStringRequirementBuilder.kt @@ -0,0 +1,47 @@ +package `in`.kyle.mcspring.commands.dsl.builders + +import `in`.kyle.mcspring.commands.dsl.CommandTestSupport.runCommand +import org.amshove.kluent.shouldBeEqualTo +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.fail + +class TestStringRequirementBuilder { + + @Test + fun testSingleString() { + runCommand("test") { + val arg0 by stringArg { } + arg0 shouldBeEqualTo "test" + } + } + + @Test + fun testStringMissing() { + runCommand("") { + val arg0 by stringArg { + missing { message("missing arg") } + invalid { fail("should not run") } + } + fail("should not run") + } shouldBeEqualTo "missing arg" + } + + @Test + fun testStringMulti() { + runCommand("arg1 arg2") { + val arg0 by stringArg { + missing { fail("should not run") } + invalid { fail("should not run") } + } + + arg0 shouldBeEqualTo "arg1" + context.argIndex shouldBeEqualTo 1 + + val arg1 by stringArg { + missing { fail("should not run") } + invalid { fail("should not run") } + } + arg1 shouldBeEqualTo "arg2" + } + } +} diff --git a/mcspring-api/mcspring-common/README.md b/mcspring-api/mcspring-common/README.md new file mode 100644 index 0000000..900f66a --- /dev/null +++ b/mcspring-api/mcspring-common/README.md @@ -0,0 +1,3 @@ +mcspring-common +--- +This doesn't really do a lot. You can probably just ignore it. diff --git a/mcspring-api/mcspring-common/build.gradle.kts b/mcspring-api/mcspring-common/build.gradle.kts new file mode 100644 index 0000000..6de75a4 --- /dev/null +++ b/mcspring-api/mcspring-common/build.gradle.kts @@ -0,0 +1,3 @@ +dependencies { + api("org.spigotmc:spigot-api") +} diff --git a/mcspring-api/mcspring-common/src/main/kotlin/in/kyle/mcspring/common/.keep b/mcspring-api/mcspring-common/src/main/kotlin/in/kyle/mcspring/common/.keep new file mode 100644 index 0000000..e69de29 diff --git a/mcspring-api/mcspring-common/src/main/kotlin/in/kyle/mcspring/common/commands/CommandMapWrapper.kt b/mcspring-api/mcspring-common/src/main/kotlin/in/kyle/mcspring/common/commands/CommandMapWrapper.kt new file mode 100644 index 0000000..e67c1e7 --- /dev/null +++ b/mcspring-api/mcspring-common/src/main/kotlin/in/kyle/mcspring/common/commands/CommandMapWrapper.kt @@ -0,0 +1,18 @@ +package `in`.kyle.mcspring.common.commands + +import org.bukkit.Bukkit +import org.bukkit.command.Command +import org.bukkit.command.CommandMap +import org.bukkit.plugin.Plugin + +object CommandMapWrapper { + val commandMap by lazy { + val bukkitCommandMap = Bukkit.getServer().javaClass.getDeclaredField("commandMap") + bukkitCommandMap.isAccessible = true + bukkitCommandMap[Bukkit.getServer()] as CommandMap + } + + fun registerCommand(plugin: Plugin, command: Command) { + commandMap.register(command.label, plugin.name, command) + } +} diff --git a/mcspring-api/mcspring-guis/README.md b/mcspring-api/mcspring-guis/README.md new file mode 100644 index 0000000..17b5d7a --- /dev/null +++ b/mcspring-api/mcspring-guis/README.md @@ -0,0 +1,3 @@ +mcspring-guis +--- +Provides a functional GUI api. diff --git a/mcspring-api/mcspring-guis/build.gradle.kts b/mcspring-api/mcspring-guis/build.gradle.kts new file mode 100644 index 0000000..99b2209 --- /dev/null +++ b/mcspring-api/mcspring-guis/build.gradle.kts @@ -0,0 +1,5 @@ +dependencies { + api("org.spigotmc:spigot-api") + implementation(project(":mcspring-api:mcspring-chat")) + implementation(project(":mcspring-api:mcspring-rx")) +} diff --git a/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/Gui.kt b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/Gui.kt new file mode 100644 index 0000000..1001b0f --- /dev/null +++ b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/Gui.kt @@ -0,0 +1,172 @@ +package `in`.kyle.mcspring.guis + +import `in`.kyle.mcspring.guis.chat.ChatGui +import `in`.kyle.mcspring.guis.chat.ChatGuiDrawer +import `in`.kyle.mcspring.guis.chat.ChatGuiSetup +import `in`.kyle.mcspring.guis.hotbar.HotbarDrawer +import `in`.kyle.mcspring.guis.hotbar.HotbarGui +import `in`.kyle.mcspring.guis.hotbar.HotbarSetup +import `in`.kyle.mcspring.guis.invenotry.InventoryDrawer +import `in`.kyle.mcspring.guis.invenotry.InventoryGui +import `in`.kyle.mcspring.guis.invenotry.InventorySetup +import `in`.kyle.mcspring.guis.invenotry.InventorySize +import `in`.kyle.mcspring.rx.observeEvent +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.disposables.Disposable +import org.bukkit.entity.Player +import org.bukkit.event.player.PlayerQuitEvent +import org.bukkit.event.server.PluginDisableEvent +import org.bukkit.plugin.Plugin +import org.bukkit.plugin.java.JavaPlugin + +object Gui { + + val plugin = JavaPlugin.getProvidingPlugin(InventoryGui::class.java) + private val currentGuis = mutableMapOf>() + + fun inventory( + player: Player, + title: String, + size: InventorySize = InventorySize.ONE_LINE, + lambda: GuiBuilder.() -> Unit + ): Disposable { + val gui = InventoryGui( + size.slots, + title, + plugin, + player, + currentGuis[player] + ) + return setup(gui, lambda) + } + + fun hotbar(player: Player, lambda: GuiBuilder.() -> Unit): Disposable { + val gui = HotbarGui( + plugin, + player, + currentGuis[player] + ) + return setup(gui, lambda) + } + + fun chat(player: Player, lambda: GuiBuilder.() -> Unit): Disposable { + val gui = ChatGui(plugin, player, currentGuis[player]) + return setup(gui, lambda) + } + + @Suppress("UNCHECKED_CAST") + private fun setup( + gui: ClickableGui, + lambda: GuiBuilder.() -> Unit + ): Disposable { + val builder = GuiBuilder() + builder.lambda() + builder.setup?.apply { gui.setupLambdas.add(this) } + builder.redraw?.apply { gui.redrawLambdas.add(this) } + currentGuis[gui.player] = gui as ClickableGui + val sub = gui.open() + gui.globalSubscription.add(Disposable.fromRunnable { + if (!gui.isDisabled()) gui.disable() + currentGuis.remove(gui.player) + gui.parent?.apply { + enable() + currentGuis[player] = this + } + }) + return sub + } +} + +@Target(AnnotationTarget.CLASS) +annotation class DslMark + +@DslMark +class GuiBuilder { + internal var setup: (Setup.() -> Unit)? = null + internal var redraw: (Drawer.() -> Unit)? = null + + fun setup(lambda: Setup.() -> Unit) { + setup = lambda + } + + fun redraw(lambda: Drawer.() -> Unit) { + redraw = lambda + } +} + +abstract class ClickableGui( + val plugin: Plugin, + val player: Player, + val parent: ClickableGui? +) { + + lateinit var globalSubscription: CompositeDisposable + lateinit var listenerSubscription: CompositeDisposable + + val redrawLambdas = mutableSetOf Unit>() + val setupLambdas = mutableSetOf Unit>() + + protected abstract fun registerListeners(): CompositeDisposable + protected abstract fun makeDrawer(): Drawer + protected abstract fun makeSetup(): Setup + + abstract fun clear() + + open fun redraw() { + clear() + redrawLambdas.forEach { it(makeDrawer()) } + } + + open fun open(): Disposable { + require(!this::globalSubscription.isInitialized) { "GUI can only be opened once" } + require(parent?.isDisabled() + ?: true) { "Parent must be disabled/closed before a new GUI can be opened" } + globalSubscription = makeDefaultSubscription() + enable() + return globalSubscription + } + + fun isDisabled() = listenerSubscription.isDisposed + + open fun close() { + require(!globalSubscription.isDisposed) { "GUI already closed" } + globalSubscription.dispose() + } + + open fun enable() { + require(this::globalSubscription.isInitialized) { "GUI enable called before open" } + require(!this::listenerSubscription.isInitialized || listenerSubscription.isDisposed) { + "Cannot enable, already enabled" + } + listenerSubscription = registerListeners() + setupLambdas.forEach { it(makeSetup()) } + redraw() + } + + open fun disable() { + require(this::globalSubscription.isInitialized) { "GUI disable called before open" } + require(!listenerSubscription.isDisposed) { "Cannot disable, already disabled" } + listenerSubscription.dispose() + globalSubscription.remove(listenerSubscription) + clear() + } + + private fun makeDefaultSubscription(): CompositeDisposable { + val subscription = CompositeDisposable() + + subscription.add(plugin.observeEvent(PlayerQuitEvent::class) + .filter { it.player == player } + .subscribe { subscription.dispose() }) + + subscription.add(plugin.observeEvent(PluginDisableEvent::class) + .filter { it.plugin == plugin } + .subscribe { subscription.dispose() }) + + return subscription + } +} + +open class ClickContext(val gui: ClickableGui<*, *>) { + val listenerSubscription: CompositeDisposable = gui.listenerSubscription + val globalSubscription: CompositeDisposable = gui.globalSubscription +} diff --git a/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/chat/ChatGui.kt b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/chat/ChatGui.kt new file mode 100644 index 0000000..ccf6a4a --- /dev/null +++ b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/chat/ChatGui.kt @@ -0,0 +1,56 @@ +package `in`.kyle.mcspring.guis.chat + +import `in`.kyle.mcspring.guis.ClickContext +import `in`.kyle.mcspring.guis.ClickableGui +import `in`.kyle.mcspring.rx.observeEvent +import `in`.kyle.mcspring.rx.syncScheduler +import io.reactivex.rxjava3.disposables.CompositeDisposable +import org.bukkit.entity.Player +import org.bukkit.event.player.AsyncPlayerChatEvent +import org.bukkit.plugin.Plugin + +class ChatGui( + plugin: Plugin, + player: Player, + parent: ClickableGui? +) : ClickableGui(plugin, player, parent) { + + val actions = mutableListOf<(String) -> Unit>() + + override fun registerListeners(): CompositeDisposable { + val subscription = CompositeDisposable() + subscription.add(plugin.observeEvent(AsyncPlayerChatEvent::class) + .filter { it.player == player } + .observeOn(plugin.syncScheduler()) + .subscribeOn(plugin.syncScheduler()) + .subscribe { event -> + actions.toList().forEach { it(event.message) } + }) + return subscription + } + + override fun makeDrawer() = ChatGuiDrawer(this) + + override fun makeSetup() = ChatGuiSetup(this) + + override fun clear() { + actions.clear() + repeat(100) { + player.sendMessage(" ") + } + } +} + +open class ChatGuiDrawer(val chat: ChatGui) : ClickContext(chat) { + + fun message(lambda: (String) -> Unit) { + chat.actions.add(lambda) + } + + fun redraw() = chat.redraw() + fun close() = chat.close() + fun enable() = chat.enable() + fun disable() = chat.disable() +} + +class ChatGuiSetup(chat: ChatGui) : ChatGuiDrawer(chat) diff --git a/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/hotbar/HotbarGui.kt b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/hotbar/HotbarGui.kt new file mode 100644 index 0000000..81962dd --- /dev/null +++ b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/hotbar/HotbarGui.kt @@ -0,0 +1,144 @@ +package `in`.kyle.mcspring.guis.hotbar + +import `in`.kyle.mcspring.guis.ClickContext +import `in`.kyle.mcspring.guis.ClickableGui +import `in`.kyle.mcspring.guis.item.ItemBuilder +import `in`.kyle.mcspring.rx.observeEvent +import io.reactivex.rxjava3.disposables.CompositeDisposable +import org.bukkit.Material +import org.bukkit.entity.Player +import org.bukkit.event.Cancellable +import org.bukkit.event.block.Action.PHYSICAL +import org.bukkit.event.block.BlockBreakEvent +import org.bukkit.event.block.BlockPlaceEvent +import org.bukkit.event.inventory.InventoryClickEvent +import org.bukkit.event.inventory.InventoryCreativeEvent +import org.bukkit.event.inventory.InventoryType +import org.bukkit.event.player.PlayerDropItemEvent +import org.bukkit.event.player.PlayerInteractEvent +import org.bukkit.inventory.ItemStack +import org.bukkit.plugin.Plugin +import kotlin.collections.set + +open class HotbarDrawer(val hotbar: HotbarGui) { + + val player = hotbar.player + + fun button(slot: Int, lambda: HotbarButtonBuilder.() -> Unit) { + val builder = HotbarButtonBuilder() + builder.lambda() + if (builder.click != null) { + hotbar.actions[slot] = builder.click!! + } + player.inventory.setItem(slot, builder.item) + } + + fun redraw() = hotbar.redraw() + + fun close() = hotbar.close() + + fun enable() = hotbar.enable() + + fun disable() = hotbar.disable() +} + +class HotbarSetup(hotbar: HotbarGui) : HotbarDrawer(hotbar) { + val listenerSubscription = hotbar.listenerSubscription + val globalSubscription = hotbar.globalSubscription +} + +class HotbarGui( + plugin: Plugin, player: Player, parent: ClickableGui? +) : ClickableGui(plugin, player, parent) { + + val actions = mutableMapOf Unit>() + private var lastClickTime = -1L + + override fun clear() { + actions.clear() + for (i in 0..8) { + player.inventory.setItem(i, null) + } + player.updateInventory() + } + + override fun redraw() { + super.redraw() + player.updateInventory() + } + + private val clickEventHandler = { event: Cancellable -> + val slot = player.inventory.heldItemSlot + if (slot in actions) { + event.isCancelled = true + if (notSpamClick()) { + actions[slot]?.invoke(ClickContext(this)) + } + } + } + + override fun registerListeners(): CompositeDisposable { + val listeners = CompositeDisposable() + listeners.add(plugin.observeEvent(PlayerDropItemEvent::class) + .filter { it.player == player } + .subscribe(clickEventHandler)) + + listeners.add(plugin.observeEvent(InventoryCreativeEvent::class, InventoryClickEvent::class) + .filter { player == it.whoClicked } + .filter { it.clickedInventory?.type == InventoryType.PLAYER } + .subscribe { + it.isCancelled = true + player.setItemOnCursor(ItemStack(Material.AIR)) + player.updateInventory() + }) + + listeners.add(plugin.observeEvent(PlayerInteractEvent::class) + .filter { player == it.player } + .filter { it.action !== PHYSICAL } + .subscribe(clickEventHandler)) + + listeners.add(plugin.observeEvent(BlockPlaceEvent::class) + .filter { it.player == player } + .subscribe(clickEventHandler)) + + listeners.add(plugin.observeEvent(BlockBreakEvent::class) + .filter { it.player == player } + .subscribe(clickEventHandler)) + + return listeners + } + + override fun makeDrawer() = HotbarDrawer(this) + + override fun makeSetup() = HotbarSetup(this) + + private fun notSpamClick(): Boolean { + return if (lastClickTime == -1L) { + lastClickTime = System.currentTimeMillis() + true + } else { + val duration = System.currentTimeMillis() - lastClickTime + if (duration > 100) { + lastClickTime = System.currentTimeMillis() + true + } else { + false + } + } + } +} + +class HotbarButtonBuilder { + + internal lateinit var item: ItemStack + internal var click: (ClickContext.() -> Unit)? = null + + fun onClick(click: ClickContext.() -> Unit) { + this.click = click + } + + fun itemStack(lambda: ItemBuilder.() -> Unit) { + this.item = ItemBuilder.create(lambda) + } +} + diff --git a/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/invenotry/InventoryGui.kt b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/invenotry/InventoryGui.kt new file mode 100644 index 0000000..dbb1c40 --- /dev/null +++ b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/invenotry/InventoryGui.kt @@ -0,0 +1,170 @@ +package `in`.kyle.mcspring.guis.invenotry + +import `in`.kyle.mcspring.chat.translateColorCodes +import `in`.kyle.mcspring.guis.ClickContext +import `in`.kyle.mcspring.guis.ClickableGui +import `in`.kyle.mcspring.guis.item.ItemBuilder +import `in`.kyle.mcspring.rx.observeEvent +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.disposables.Disposable +import jdk.jshell.JShell +import org.bukkit.Bukkit +import org.bukkit.entity.Player +import org.bukkit.event.inventory.ClickType.* +import org.bukkit.event.inventory.InventoryClickEvent +import org.bukkit.event.inventory.InventoryDragEvent +import org.bukkit.event.inventory.InventoryEvent +import org.bukkit.inventory.Inventory +import org.bukkit.inventory.ItemStack +import org.bukkit.plugin.Plugin + +class InventorySetup(gui: InventoryGui) : InventoryDrawer(gui) { + val listenerSubscription: CompositeDisposable = gui.listenerSubscription + val globalSubscription: CompositeDisposable = gui.globalSubscription +} + +open class InventoryDrawer(val inventory: InventoryGui) { + val player = inventory.player + val plugin = inventory.plugin + val lastItemIndex: Int by lazy { inventory.bukkitInventory.size - 1 } + + fun button(x: Int, y: Int, lambda: InventoryItemBuilder.() -> Unit) { + require(x < 9 && x > -1) { "x is out of range! $x" } + button(x + y * 9, lambda) + } + + fun button(slot: Int, lambda: InventoryItemBuilder.() -> Unit) { + require(slot < inventory.bukkitInventory.size) { "Slot out of bounds $slot < ${inventory.bukkitInventory.size}" } + + val builder = InventoryItemBuilder() + builder.lambda() + + builder.click?.let { inventory.actions[slot] = it } + inventory.bukkitInventory.setItem(slot, builder.itemStack) + } + + fun redraw() = inventory.redraw() + fun close() = inventory.close() + fun title(lambda: () -> String) = inventory.title(lambda) + fun enable() = inventory.enable() + fun disable(closeInventory: Boolean = true) = inventory.disable(closeInventory) + fun closeInventoryOnDisable(boolean: Boolean) { inventory.closeInventoryOnDisable = boolean } +} + +class InventoryGui constructor( + val size: Int, + var title: String, + plugin: Plugin, + player: Player, + parent: ClickableGui? +) : ClickableGui(plugin, player, parent) { + + val actions = mutableMapOf Unit>() + + internal lateinit var bukkitInventory: Inventory + var closeInventoryOnDisable = true + + init { + setupLambdas.add { + if (player.openInventory.topInventory != bukkitInventory) { + player.openInventory(bukkitInventory) + } + } + } + + override fun close() { + require(player.openInventory.topInventory == bukkitInventory) { "Inventory already closed" } + super.close() + } + + fun title(title: () -> String) = + InventoryTitleHelper.sendInventoryTitle( + player, + title() + ) + + override fun open(): Disposable { + if (parent is InventoryGui) { + title { title.translateColorCodes() } + bukkitInventory = parent.bukkitInventory + } else { + bukkitInventory = Bukkit.createInventory(player, size, title.translateColorCodes()) + player.openInventory(bukkitInventory) + player.updateInventory() + } + return super.open() + } + + fun disable(closeInventory: Boolean) { + val temp = closeInventoryOnDisable + closeInventoryOnDisable = closeInventory + disable() + closeInventoryOnDisable = temp + } + + override fun disable() { + super.disable() + if (closeInventoryOnDisable) { + Bukkit.getScheduler().runTask(plugin, Runnable { player.closeInventory() }) + } + } + + override fun registerListeners(): CompositeDisposable { + val listeners = CompositeDisposable() + listeners.add(plugin.observeEvent(InventoryClickEvent::class) + .filter { isCorrectPlayer(it) } + .subscribe { + if (it.click in listOf(LEFT, RIGHT, MIDDLE)) { + actions[it.rawSlot]?.invoke(ClickContext(this)) + } + it.isCancelled = true + }) + + listeners.add(plugin.observeEvent(InventoryDragEvent::class) + .filter { event -> event.inventory == bukkitInventory } + .subscribe { it.isCancelled = true }) + return listeners + } + + private fun isCorrectPlayer(event: InventoryEvent): Boolean { + return event.inventory.holder != null + && event.inventory.holder == player + && this::bukkitInventory.isInitialized + && event.inventory == bukkitInventory + } + + override fun makeDrawer() = InventoryDrawer(this) + + override fun makeSetup() = InventorySetup(this) + + override fun clear() { + bukkitInventory.clear() + player.updateInventory() + } + +} + +enum class InventorySize { + ONE_LINE, TWO_LINE, THREE_LINE, FOUR_LINE, FIVE_LINE, SIX_LINE; + + val slots: Int = 9 * (ordinal + 1) + + companion object { + fun of(requiredSlots: Int) = values().first { it.slots >= requiredSlots } + } +} + +class InventoryItemBuilder { + + internal var itemStack: ItemStack? = null + internal var click: (ClickContext.() -> Unit)? = null + + fun itemStack(lambda: ItemBuilder.() -> Unit) { + this.itemStack = ItemBuilder.create(lambda) + } + + fun onClick(lambda: ClickContext.() -> Unit) { + click = lambda + } +} + diff --git a/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/invenotry/InventoryTitleHelper.kt b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/invenotry/InventoryTitleHelper.kt new file mode 100644 index 0000000..3aede52 --- /dev/null +++ b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/invenotry/InventoryTitleHelper.kt @@ -0,0 +1,322 @@ +@file:Suppress("LocalVariableName") + +package `in`.kyle.mcspring.guis.invenotry + +import com.google.common.base.Preconditions +import org.bukkit.Bukkit +import org.bukkit.entity.Player +import org.bukkit.event.inventory.InventoryType +import org.bukkit.inventory.Inventory +import java.lang.reflect.Constructor +import java.lang.reflect.Field +import java.lang.reflect.Method + +/** + * A inventory helper that allows you to change the title of the current + * opened inventory. + * https://gist.githubusercontent.com/Cybermaxke/7f0a315aea70c9d62535/raw/545b01be4234422e81b1ce0a9606083c261906ba/InventoryTitleHelper + */ +object InventoryTitleHelper { + // Methods + private var m_Player_GetHandle: Method? = null + private var m_PlayerConnection_sendPacket: Method? = null + private var m_CraftChatMessage_fromString: Method? = null + private var m_EntityPlayer_updateInventory: Method? = null + + // Fields + private var f_EntityPlayer_playerConnection: Field? = null + private var f_EntityPlayer_activeContainer: Field? = null + private var f_Container_windowId: Field? = null + + // Constructors + private var c_PacketOpenWindow: Constructor<*>? = null + + // The version of the server (nms version like v1_5_R3) + private var nms_version: String? = null + private var nms_package: String? = null + private var crb_package: String? = null + + /** + * Sends a new inventory title to the client. + * + * @param player the player + * @param title the new title + */ + fun sendInventoryTitle(player: Player, title: String) { + Preconditions.checkNotNull(player, "player") + try { + sendInventoryTitle0( + player, + title + ) + } catch (e: Exception) { + e.printStackTrace() + } + } + + private fun sendInventoryTitle0(player: Player, title: String) { + val inventory = player.openInventory.topInventory + if (m_Player_GetHandle == null) { + m_Player_GetHandle = player.javaClass.getMethod("getHandle") + } + val nms_EntityPlayer = m_Player_GetHandle!!.invoke(player) + if (f_EntityPlayer_playerConnection == null) { + f_EntityPlayer_playerConnection = + nms_EntityPlayer.javaClass.getField("playerConnection") + } + val nms_PlayerConnection = + f_EntityPlayer_playerConnection!![nms_EntityPlayer] + if (f_EntityPlayer_activeContainer == null) { + f_EntityPlayer_activeContainer = nms_EntityPlayer.javaClass.getField("activeContainer") + } + val nms_Container = f_EntityPlayer_activeContainer!![nms_EntityPlayer] + if (f_Container_windowId == null) { + f_Container_windowId = nms_Container.javaClass.getField("windowId") + } + val windowId = f_Container_windowId!!.getInt(nms_Container) + val version = nmsVersion + if (version!!.startsWith("v1_5_") || version.startsWith("v1_6_")) { + sendPacket15a16a17( + nms_PlayerConnection, + nms_EntityPlayer, + nms_Container, + windowId, + inventory, + title, + false + ) + } else if (version.startsWith("v1_7_")) { + sendPacket15a16a17( + nms_PlayerConnection, + nms_EntityPlayer, + nms_Container, + windowId, + inventory, + title, + true + ) + } else if (version == "v1_8_R1" || version == "v1_8_R2") { + sendPacket18( + nms_PlayerConnection, + nms_EntityPlayer, + nms_Container, + windowId, + inventory, + title + ) + } + } + + private fun sendPacket15a16a17( + nms_playerConnection: Any, + nms_EntityPlayer: Any, + nms_Container: Any, + windowId: Int, + inventory: Inventory, + title: String, + flag: Boolean + ) { + var title: String? = title + if (c_PacketOpenWindow == null) { + c_PacketOpenWindow = if (flag) { + findNmsClass("PacketPlayOutOpenWindow") + .getConstructor( + Int::class.javaPrimitiveType, + Int::class.javaPrimitiveType, + String::class.java, + Int::class.javaPrimitiveType, + Boolean::class.javaPrimitiveType + ) + } else { + findNmsClass("Packet100OpenWindow") + .getConstructor( + Int::class.javaPrimitiveType, + Int::class.javaPrimitiveType, + String::class.java, + Int::class.javaPrimitiveType, + Boolean::class.javaPrimitiveType + ) + } + } + val id: Int + val size: Int + when (inventory.type) { + InventoryType.ANVIL -> { + id = 8 + size = 9 + } + InventoryType.BEACON -> { + id = 7 + size = 1 + } + InventoryType.BREWING -> { + id = 5 + size = 4 + } + InventoryType.CRAFTING -> return + InventoryType.CREATIVE -> return + InventoryType.DISPENSER -> { + id = 3 + size = 9 + } + InventoryType.DROPPER -> { + id = 10 + size = 9 + } + InventoryType.ENCHANTING -> { + id = 4 + size = 9 + } + InventoryType.ENDER_CHEST, InventoryType.CHEST -> { + id = 0 + size = inventory.size + } + InventoryType.FURNACE -> { + id = 2 + size = 2 + } + InventoryType.HOPPER -> { + id = 9 + size = 5 + } + InventoryType.MERCHANT -> { + id = 6 + size = 3 + } + InventoryType.PLAYER -> return + InventoryType.WORKBENCH -> { + id = 1 + size = 9 + } + else -> return + } + if (title != null && title.length > 32) { + title = title.substring(0, 32) + } + if (m_EntityPlayer_updateInventory == null) { + m_EntityPlayer_updateInventory = + nms_EntityPlayer.javaClass.getMethod("updateInventory", + findNmsClass("Container") + ) + } + val packet = c_PacketOpenWindow!!.newInstance( + windowId, + id, + title ?: "", + size, + true + ) + sendPacket( + nms_playerConnection, + packet + ) + m_EntityPlayer_updateInventory!!.invoke(nms_EntityPlayer, nms_Container) + } + + private fun sendPacket18( + nms_playerConnection: Any, + nms_EntityPlayer: Any, + nms_Container: Any, + windowId: Int, + inventory: Inventory, + title: String + ) { + if (c_PacketOpenWindow == null) { + c_PacketOpenWindow = + findNmsClass("PacketPlayOutOpenWindow") + .getConstructor( + Int::class.javaPrimitiveType, + String::class.java, + findNmsClass("IChatBaseComponent"), + Int::class.javaPrimitiveType + ) + } + val id: String + var size = 0 + when (inventory.type) { + InventoryType.ANVIL -> id = "minecraft:anvil" + InventoryType.BEACON -> id = "minecraft:beacon" + InventoryType.BREWING -> id = "minecraft:brewing_stand" + InventoryType.CRAFTING -> return + InventoryType.CREATIVE -> return + InventoryType.DISPENSER -> id = "minecraft:dispenser" + InventoryType.DROPPER -> id = "minecraft:dropper" + InventoryType.ENCHANTING -> id = "minecraft:enchanting_table" + InventoryType.ENDER_CHEST, InventoryType.CHEST -> { + id = "minecraft:chest" + size = inventory.size + } + InventoryType.FURNACE -> id = "minecraft:furnace" + InventoryType.HOPPER -> id = "minecraft:hopper" + InventoryType.MERCHANT -> { + id = "minecraft:villager" + size = 3 + } + InventoryType.PLAYER -> return + InventoryType.WORKBENCH -> id = "minecraft:crafting_table" + else -> return + } + if (m_CraftChatMessage_fromString == null) { + m_CraftChatMessage_fromString = + findCrbClass("util.CraftChatMessage") + .getMethod("fromString", String::class.java) + } + if (m_EntityPlayer_updateInventory == null) { + m_EntityPlayer_updateInventory = + nms_EntityPlayer.javaClass.getMethod("updateInventory", + findNmsClass("Container") + ) + } + val nms_title = + (m_CraftChatMessage_fromString!!.invoke(null, title) as Array)[0] + val nms_packet = c_PacketOpenWindow!!.newInstance(windowId, id, nms_title, size) + sendPacket( + nms_playerConnection, + nms_packet + ) + m_EntityPlayer_updateInventory!!.invoke(nms_EntityPlayer, nms_Container) + } + + private fun sendPacket(playerConnection: Any, packet: Any) { + if (m_PlayerConnection_sendPacket == null) { + m_PlayerConnection_sendPacket = + playerConnection.javaClass.getMethod("sendPacket", + findNmsClass("Packet") + ) + } + m_PlayerConnection_sendPacket!!.invoke(playerConnection, packet) + } + + private val nmsVersion: String? + get() { + if (nms_version == null) { + nms_version = + Bukkit.getServer().javaClass.getPackage().name.replace("org.bukkit.craftbukkit.", "") + } + return nms_version + } + + private val nmsPackage: String? + get() { + if (nms_package == null) { + nms_package = "net.minecraft.server.$nmsVersion" + } + return nms_package + } + + private val crbPackage: String? + get() { + if (crb_package == null) { + crb_package = "org.bukkit.craftbukkit.$nmsVersion" + } + return crb_package + } + + private fun findNmsClass(name: String): Class<*> { + return Class.forName("$nmsPackage.$name") + } + + private fun findCrbClass(name: String): Class<*> { + return Class.forName("$crbPackage.$name") + } +} diff --git a/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/item/ItemBuilder.kt b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/item/ItemBuilder.kt new file mode 100644 index 0000000..7022064 --- /dev/null +++ b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/item/ItemBuilder.kt @@ -0,0 +1,98 @@ +package `in`.kyle.mcspring.guis.item + +import `in`.kyle.mcspring.chat.translateColorCodes +import org.bukkit.DyeColor +import org.bukkit.Material +import org.bukkit.inventory.ItemFlag +import org.bukkit.inventory.ItemStack +import org.bukkit.inventory.meta.SkullMeta +import org.bukkit.material.Dye + +class ItemBuilder { + + var material = Material.AIR + var amount = 1 + var name: String? = null + set(value) { + field = value?.translateColorCodes() + } + var skullOwner: String? = null + var lore = listOf() + set(lines) { + field = lines.map { it.translateColorCodes() }.toMutableList() + } + + var dyeColor: DyeColor? = null + + fun skullOwner(name: String) { + material = Material.PLAYER_HEAD + skullOwner = name + } + + fun lore(vararg lines: String) { + this.lore = lines.toList() + } + + fun build(): ItemStack { + var itemStack= if (dyeColor != null) { + createDyeItemStack() + } else { + ItemStack(material, amount) + } + tryAddItemName(itemStack) + tryAddSkullMeta(itemStack) + tryAddLore(itemStack) + itemStack = + resetItemFlags(itemStack) + return itemStack + } + + private fun tryAddLore(itemStack: ItemStack) { + if (lore.isNotEmpty()) { + val itemMeta = itemStack.itemMeta + itemMeta!!.lore = lore.map { it.translateColorCodes() } + itemStack.itemMeta = itemMeta + } + } + + private fun tryAddSkullMeta(itemStack: ItemStack) { + if (skullOwner != null) { + val skullMeta = itemStack.itemMeta as SkullMeta + skullMeta.owner = skullOwner!!.trim { it <= ' ' } + itemStack.itemMeta = skullMeta + } + } + + private fun tryAddItemName(itemStack: ItemStack) { + if (name != null) { + val itemMeta = itemStack.itemMeta + itemMeta!!.setDisplayName(name) + itemStack.itemMeta = itemMeta + } + } + + private fun createDyeItemStack(): ItemStack { + val itemStack: ItemStack + val dye = Dye(material) + dye.color = dyeColor + itemStack = dye.toItemStack(amount) + return itemStack + } + + companion object { + fun create(lambda: ItemBuilder.() -> Unit): ItemStack { + val builder = ItemBuilder() + builder.lambda() + return builder.build() + } + + private fun resetItemFlags(item: ItemStack): ItemStack { + if (item.hasItemMeta()) { + val itemMeta = item.itemMeta + itemMeta!!.addItemFlags(ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_POTION_EFFECTS) + item.itemMeta = itemMeta + } + return item + } + } +} diff --git a/mcspring-api/mcspring-jar-loader/build.gradle.kts b/mcspring-api/mcspring-jar-loader/build.gradle.kts index 062b32a..f95e930 100644 --- a/mcspring-api/mcspring-jar-loader/build.gradle.kts +++ b/mcspring-api/mcspring-jar-loader/build.gradle.kts @@ -1,3 +1,3 @@ dependencies { - compile("org.springframework.boot:spring-boot-loader") + implementation("org.springframework.boot:spring-boot-loader") } diff --git a/mcspring-api/mcspring-logging/build.gradle.kts b/mcspring-api/mcspring-logging/build.gradle.kts new file mode 100644 index 0000000..2e5d25f --- /dev/null +++ b/mcspring-api/mcspring-logging/build.gradle.kts @@ -0,0 +1,14 @@ +dependencies { + implementation("org.springframework.boot:spring-boot-starter-aop:") + implementation("org.springframework.boot:spring-boot-starter-log4j2:") + + testImplementation("org.springframework.boot:spring-boot-starter-test") + testImplementation("org.springframework.boot:spring-boot-starter-log4j2:") + testImplementation("org.springframework.boot:spring-boot-starter-aop:") +} + +configurations { + all { + exclude(group = "org.springframework.boot", module = "spring-boot-starter-logging") + } +} diff --git a/mcspring-api/mcspring-logging/src/main/kotlin/in/kyle/mcspring/logging/LogCall.kt b/mcspring-api/mcspring-logging/src/main/kotlin/in/kyle/mcspring/logging/LogCall.kt new file mode 100644 index 0000000..5a36dfa --- /dev/null +++ b/mcspring-api/mcspring-logging/src/main/kotlin/in/kyle/mcspring/logging/LogCall.kt @@ -0,0 +1,12 @@ +package `in`.kyle.mcspring.logging + +import java.lang.annotation.Inherited + +@Target(AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.RUNTIME) +@Inherited +annotation class LogCall( + val logger: String = "mcspring", + val logParameters: Boolean = false +) + diff --git a/mcspring-api/mcspring-logging/src/main/kotlin/in/kyle/mcspring/logging/LogCallAspect.kt b/mcspring-api/mcspring-logging/src/main/kotlin/in/kyle/mcspring/logging/LogCallAspect.kt new file mode 100644 index 0000000..88d0a58 --- /dev/null +++ b/mcspring-api/mcspring-logging/src/main/kotlin/in/kyle/mcspring/logging/LogCallAspect.kt @@ -0,0 +1,31 @@ +package `in`.kyle.mcspring.logging + +import org.apache.logging.log4j.LogManager +import org.aspectj.lang.JoinPoint +import org.aspectj.lang.annotation.Aspect +import org.aspectj.lang.annotation.Before +import org.aspectj.lang.reflect.MethodSignature +import org.springframework.stereotype.Service + +@Aspect +@Service +class LogCallAspect { + + @Before("@within(in.kyle.mcspring.logging.LogCall) || @annotation(in.kyle.mcspring.logging.LogCall)") + fun logCall(joinPoint: JoinPoint) { + println("swag") + val sig = joinPoint.signature as MethodSignature + val annotation = sig.method.getAnnotation(LogCall::class.java) + val logger = LogManager.getLogger(annotation.logger) + val parameters = if (annotation.logParameters) { + joinPoint.args.zip(sig.method.parameters).joinToString(", ") { "${it.second.name}=${it.first}" } + } else { + "" + } + logger.trace("Call ${sig.method.name} $parameters") + println("swag") + println("swag") + println("swag") + println("swag") + } +} diff --git a/mcspring-api/mcspring-logging/src/main/kotlin/in/kyle/mcspring/logging/LoggingConfiguration.kt b/mcspring-api/mcspring-logging/src/main/kotlin/in/kyle/mcspring/logging/LoggingConfiguration.kt new file mode 100644 index 0000000..da190db --- /dev/null +++ b/mcspring-api/mcspring-logging/src/main/kotlin/in/kyle/mcspring/logging/LoggingConfiguration.kt @@ -0,0 +1,25 @@ +package `in`.kyle.mcspring.logging + +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.context.annotation.EnableAspectJAutoProxy +import org.springframework.core.io.support.PathMatchingResourcePatternResolver + + +@Configuration +@EnableAspectJAutoProxy +open class LoggingConfiguration { + @Bean("mcspring-logger") + open fun logger(): Logger { + registerConfigurations() + return LoggerFactory.getLogger("mcspring") + } + + private fun registerConfigurations() { + val loader = PathMatchingResourcePatternResolver() + val resources = loader.getResources("classpath*:log4j2-*.xml") + System.setProperty("log4j.configurationFile", resources.joinToString(separator = ",") { it.filename!! }) + } +} diff --git a/mcspring-api/mcspring-logging/src/main/kotlin/in/kyle/mcspring/logging/TestComponent.kt b/mcspring-api/mcspring-logging/src/main/kotlin/in/kyle/mcspring/logging/TestComponent.kt new file mode 100644 index 0000000..dedfd7e --- /dev/null +++ b/mcspring-api/mcspring-logging/src/main/kotlin/in/kyle/mcspring/logging/TestComponent.kt @@ -0,0 +1,12 @@ +package `in`.kyle.mcspring.logging + +import org.springframework.stereotype.Component + +@Component +open class TestComponent { + + @LogCall + fun test() { + println("called") + } +} diff --git a/mcspring-api/mcspring-logging/src/main/resources/log4j2-mcspring.xml b/mcspring-api/mcspring-logging/src/main/resources/log4j2-mcspring.xml new file mode 100644 index 0000000..2a201e0 --- /dev/null +++ b/mcspring-api/mcspring-logging/src/main/resources/log4j2-mcspring.xml @@ -0,0 +1,24 @@ + + + + + + %d{yyyy-MM-dd HH:mm:ss} %5p : %m%n + + + + + + + + + + + + + + + + diff --git a/mcspring-api/mcspring-logging/src/main/resources/log4j2.xml b/mcspring-api/mcspring-logging/src/main/resources/log4j2.xml new file mode 100644 index 0000000..9c851fe --- /dev/null +++ b/mcspring-api/mcspring-logging/src/main/resources/log4j2.xml @@ -0,0 +1,18 @@ + + + + + + + [%-5level] %d{yyyy-MM-dd HH:mm:ss} [%t] %c{1} - %msg%n + > + + + + + + + + + + diff --git a/mcspring-api/mcspring-logging/src/test/kotlin/in/kyle/mcspring/logging/TestLog.kt b/mcspring-api/mcspring-logging/src/test/kotlin/in/kyle/mcspring/logging/TestLog.kt new file mode 100644 index 0000000..8238bb8 --- /dev/null +++ b/mcspring-api/mcspring-logging/src/test/kotlin/in/kyle/mcspring/logging/TestLog.kt @@ -0,0 +1,32 @@ +package `in`.kyle.mcspring.logging + +import org.junit.jupiter.api.Test +import org.slf4j.LoggerFactory +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.SpringBootConfiguration +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.context.ApplicationContext +import org.springframework.context.annotation.ComponentScan +import org.springframework.context.annotation.Import + +@SpringBootTest +@Import(LogCallAspect::class) +@ComponentScan(basePackageClasses = [LogCall::class]) +open class TestLog { + + @Autowired + lateinit var test: TestComponent + + @Autowired + lateinit var ctx: ApplicationContext + + @Test + fun test() { + test.test() + LoggerFactory.getLogger("mcspring").info("wtf") + } + + @SpringBootConfiguration + open class Config { + } +} diff --git a/mcspring-api/mcspring-rx/README.md b/mcspring-api/mcspring-rx/README.md new file mode 100644 index 0000000..a1f0937 --- /dev/null +++ b/mcspring-api/mcspring-rx/README.md @@ -0,0 +1,5 @@ +mcspring-rx +--- +Adds RXJava3 Support for Bukkit events and scheduled tasks + +_This may be used independently of mcspring_ diff --git a/mcspring-api/mcspring-rx/build.gradle.kts b/mcspring-api/mcspring-rx/build.gradle.kts new file mode 100644 index 0000000..0ba349d --- /dev/null +++ b/mcspring-api/mcspring-rx/build.gradle.kts @@ -0,0 +1,6 @@ +val rxJava = "3.0.4" +dependencies { + api("org.spigotmc:spigot-api") + api("io.reactivex.rxjava3:rxjava:$rxJava") + implementation("io.reactivex.rxjava3:rxjava:$rxJava") +} diff --git a/mcspring-api/mcspring-rx/src/main/kotlin/in/kyle/mcspring/rx/EventSupport.kt b/mcspring-api/mcspring-rx/src/main/kotlin/in/kyle/mcspring/rx/EventSupport.kt new file mode 100644 index 0000000..3604b01 --- /dev/null +++ b/mcspring-api/mcspring-rx/src/main/kotlin/in/kyle/mcspring/rx/EventSupport.kt @@ -0,0 +1,67 @@ +package `in`.kyle.mcspring.rx + +import io.reactivex.rxjava3.core.Observable +import io.reactivex.rxjava3.core.ObservableEmitter +import io.reactivex.rxjava3.disposables.Disposable +import org.bukkit.Bukkit +import org.bukkit.event.Event +import org.bukkit.event.EventPriority +import org.bukkit.event.HandlerList +import org.bukkit.event.Listener +import org.bukkit.event.server.PluginDisableEvent +import org.bukkit.plugin.EventExecutor +import org.bukkit.plugin.Plugin +import kotlin.reflect.KClass + +fun Plugin.observeEvent( + vararg classes: KClass, + priority: EventPriority = EventPriority.NORMAL, + ignoredCanceled: Boolean = false +): Observable { + val listener = object : Listener {} + return Observable.create { subscriber -> + val executor = createExecutor( + subscriber, + classes.toList() + ) + + val pluginManager = Bukkit.getPluginManager() + classes.forEach { + pluginManager.registerEvent(it.java, listener, priority, executor, this, ignoredCanceled) + } + + registerDisable(subscriber, this, listener) + subscriber.setDisposable(Disposable.fromRunnable { + HandlerList.unregisterAll(listener) + }) + } +} + +private fun createExecutor( + subscriber: ObservableEmitter, + classes: List> +): EventExecutor { + return EventExecutor { _, event -> + if (classes.all { it.java.isAssignableFrom(event::class.java) }) { + subscriber.onNext(event as T) + } + } +} + +private fun registerDisable( + subscriber: ObservableEmitter<*>, + plugin: Plugin, + listener: Listener +) { + Bukkit.getPluginManager().registerEvent( + PluginDisableEvent::class.java, + listener, + EventPriority.MONITOR, + { _, event -> + if ((event as PluginDisableEvent).plugin == plugin) { + subscriber.onComplete() + } + }, + plugin + ) +} diff --git a/mcspring-api/mcspring-rx/src/main/kotlin/in/kyle/mcspring/rx/SchedulerSupport.kt b/mcspring-api/mcspring-rx/src/main/kotlin/in/kyle/mcspring/rx/SchedulerSupport.kt new file mode 100644 index 0000000..746506a --- /dev/null +++ b/mcspring-api/mcspring-rx/src/main/kotlin/in/kyle/mcspring/rx/SchedulerSupport.kt @@ -0,0 +1,79 @@ +package `in`.kyle.mcspring.rx + +import io.reactivex.rxjava3.core.Scheduler +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.disposables.Disposable +import org.bukkit.plugin.Plugin +import org.bukkit.scheduler.BukkitTask +import java.util.concurrent.TimeUnit +import kotlin.math.roundToLong + +private val asyncSchedulers = mutableMapOf() +private val syncSchedulers = mutableMapOf() + +fun Plugin.asyncScheduler(): BukkitScheduler { + return asyncSchedulers.getOrPut(this) { + BukkitScheduler(this, true) + } +} + +fun Plugin.syncScheduler(): BukkitScheduler { + return syncSchedulers.getOrPut(this) { + BukkitScheduler(this, false) + } +} + +class BukkitScheduler(private val plugin: Plugin, private val async: Boolean) : Scheduler() { + + override fun createWorker() = BukkitWorker(plugin, async) + + class BukkitWorker(private val plugin: Plugin, private val async: Boolean) : Worker() { + + private val composite = CompositeDisposable() + + override fun isDisposed() = composite.isDisposed + + override fun schedule(run: Runnable, initialDelay: Long, unit: TimeUnit): Disposable { + val initialTicks = timeUnitToBukkitTicks(initialDelay, unit) + val bukkitTask = bukkitSchedule(run, initialTicks) + + return Disposable.fromRunnable { + bukkitTask.cancel() + }.apply { composite.add(this) } + } + + override fun dispose() = composite.dispose() + + override fun schedulePeriodically(run: Runnable, initialDelay: Long, period: Long, unit: TimeUnit): Disposable { + val initialTicks = timeUnitToBukkitTicks(initialDelay, unit) + val periodTicks = timeUnitToBukkitTicks(period, unit) + val bukkitTask = bukkitSchedulePeriodically(run, initialTicks, periodTicks) + + return Disposable.fromRunnable { + bukkitTask.cancel() + }.apply { composite.add(this) } + } + + private fun timeUnitToBukkitTicks(delayTime: Long, timeUnit: TimeUnit): Long { + return (timeUnit.toMillis(delayTime) * 0.02).roundToLong() + } + + private fun bukkitSchedulePeriodically(runnable: Runnable, delay: Long, period: Long): BukkitTask { + val scheduler = plugin.server.scheduler + return if (async) { + scheduler.runTaskTimerAsynchronously(plugin, runnable, delay, period) + } else { + scheduler.runTaskTimer(plugin, runnable, delay, period) + } + } + + private fun bukkitSchedule(runnable: Runnable, delay: Long): BukkitTask { + val scheduler = plugin.server.scheduler + return if (async) { + scheduler.runTaskLaterAsynchronously(plugin, runnable, delay) + } else { + scheduler.runTaskLater(plugin, runnable, delay) + } + } + } +} diff --git a/mcspring-api/mcspring-subcommands/build.gradle.kts b/mcspring-api/mcspring-subcommands/build.gradle.kts deleted file mode 100644 index e0d2a6f..0000000 --- a/mcspring-api/mcspring-subcommands/build.gradle.kts +++ /dev/null @@ -1,9 +0,0 @@ -plugins { - id("org.jetbrains.dokka") -} - -dependencies { - compile(project(":mcspring-api:mcspring-base")) - testCompile(project(":mcspring-api:mcspring-test")) - compileOnly("org.spigotmc:spigot-api") -} diff --git a/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/PluginCommandResolver.kt b/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/PluginCommandResolver.kt deleted file mode 100644 index 1b0168c..0000000 --- a/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/PluginCommandResolver.kt +++ /dev/null @@ -1,28 +0,0 @@ -package `in`.kyle.mcspring.subcommands - -import `in`.kyle.mcspring.command.CommandResolver -import `in`.kyle.mcspring.command.ParameterResolver -import `in`.kyle.mcspring.command.SimpleMethodInjection -import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand -import `in`.kyle.mcspring.subcommands.plugincommand.impl.PluginCommandImpl -import org.springframework.stereotype.Component - -@Component -internal class PluginCommandResolver( - private val injection: SimpleMethodInjection -) : CommandResolver { - - override fun makeResolver(command: CommandResolver.Command): ParameterResolver { - return object : ParameterResolver { - override fun resolve(parameter: Class<*>): Any? { - return if (parameter.isAssignableFrom(PluginCommand::class.java)) { - return makeCommand(command) - } else null - } - } - } - - private fun makeCommand(command: CommandResolver.Command): PluginCommandImpl { - return PluginCommandImpl(injection, command.sender, command.args.toMutableList(), true) - } -} diff --git a/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/api/PluginCommand.kt b/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/api/PluginCommand.kt deleted file mode 100644 index 4dda6e6..0000000 --- a/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/api/PluginCommand.kt +++ /dev/null @@ -1,161 +0,0 @@ -package `in`.kyle.mcspring.subcommands.plugincommand.api - -import `in`.kyle.mcspring.subcommands.plugincommand.impl.javasupport.HighIQExecutors -import org.bukkit.command.CommandSender -import kotlin.reflect.KFunction - -typealias Err = () -> String -typealias Err1 = (String) -> String - -const val defaultStageName = "UNKNOWN" - -interface PluginCommand { - - val sender: CommandSender - - fun otherwise(e: KFunction) - - fun otherwise(e: HighIQExecutors.O0) - - fun otherwise(e: HighIQExecutors.O1) - - fun otherwise(e: HighIQExecutors.O2) - - fun otherwise(e: HighIQExecutors.O3) - - fun otherwise(e: HighIQExecutors.O4) - - fun otherwise(e: HighIQExecutors.O5) - - fun otherwise(e: HighIQExecutors.O6) - - fun otherwise(message: String) - - fun otherwise(e: HighIQExecutors.E1) - - fun otherwise(e: HighIQExecutors.E2) - - fun otherwise(e: HighIQExecutors.E3) - - fun otherwise(e: HighIQExecutors.E4) - - fun otherwise(e: HighIQExecutors.E5) - - fun otherwise(e: HighIQExecutors.E6) - - fun on(command: String, e: KFunction) - - fun onAny(vararg command: String, e: KFunction) - - fun on(command: String, e: HighIQExecutors.O0) - - fun on(command: String, e: HighIQExecutors.O1) - - fun on(command: String, e: HighIQExecutors.O2) - - fun on(command: String, e: HighIQExecutors.O3) - - fun on(command: String, e: HighIQExecutors.O4) - - fun on(command: String, e: HighIQExecutors.O5) - - fun on(command: String, e: HighIQExecutors.O6) - - fun on(command: String, e: HighIQExecutors.E1) - - fun on(command: String, e: HighIQExecutors.E2) - - fun on(command: String, e: HighIQExecutors.E3) - - fun on(command: String, e: HighIQExecutors.E4) - - fun on(command: String, e: HighIQExecutors.E5) - - fun on(command: String, e: HighIQExecutors.E6) - - fun onInvalid(errorMessage: Err1) - - fun then(e: KFunction) - - fun then(e: HighIQExecutors.O0) - - fun then(e: HighIQExecutors.O1) - - fun then(e: HighIQExecutors.O2) - - fun then(e: HighIQExecutors.O3) - - fun then(e: HighIQExecutors.O4) - - fun then(e: HighIQExecutors.O5) - - fun then(e: HighIQExecutors.O6) - - fun then(e: HighIQExecutors.E1) - - fun then(e: HighIQExecutors.E2) - - fun then(e: HighIQExecutors.E3) - - fun then(e: HighIQExecutors.E4) - - fun then(e: HighIQExecutors.E5) - - fun then(e: HighIQExecutors.E6) - - fun requires(predicate: Boolean, errorMessage: Err) - - fun requiresPlayerSender(errorMessage: Err) - - fun requiresConsoleSender(errorMessage: Err) - - fun requiresPermission(permission: String, errorMessage: Err) - - fun requiresOp(errorMessage: Err) - - fun with(processor: (String) -> Any?, errorMessage: Err1 = { "" }, stageName: String = defaultStageName) - - fun with(processor: (String) -> Any?, errorMessage: Err1 = { "" }) - = with(processor, errorMessage, defaultStageName) - - fun withString() - - fun withSentence() - - fun withInt(errorMessage: Err1) - - fun withInt(errorMessage: String) - - fun withDouble(errorMessage: Err1) - - fun withDouble(errorMessage: String) - - fun withOfflinePlayer(errorMessage: Err1) - - fun withPlayer(errorMessage: Err1) - - fun withPlayer(errorMessage: String) - - fun withWorld(errorMessage: Err1) - - fun withWorld(errorMessage: String) - - fun withXYZInt(errorMessage: Err1) - - fun withXYZInt(errorMessage: String) - - fun withMap(options: Map, errorMessage: Err1, stageName: String = defaultStageName) - - fun withMap(options: Map, errorMessage: Err1) - = withMap(options, errorMessage, defaultStageName) - - fun withMap(options: Map, errorMessage: String) - - fun withAny(options: Collection, errorMessage: Err1, stageName: String = defaultStageName) - - fun withAny(options: Collection, errorMessage: Err1) - = withAny(options, errorMessage, defaultStageName) - - fun withAny(options: Collection, errorMessage: String) - -} diff --git a/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandBase.kt b/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandBase.kt deleted file mode 100644 index 406695c..0000000 --- a/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandBase.kt +++ /dev/null @@ -1,59 +0,0 @@ -package `in`.kyle.mcspring.subcommands.plugincommand.impl - -import org.bukkit.ChatColor -import org.bukkit.command.CommandSender - -interface PluginCommandBase { - - val injections: MutableList - val parts: MutableList - val sender: CommandSender - val completions: MutableList - val runExecutors: Boolean - - var child: PluginCommandBase? - var state: State - - fun nextPart(): String? - - fun dirtiesState(requiredStates: Array = arrayOf(State.CLEAN), - resultingState: State = State.COMPLETED, - action: () -> Unit) - - fun sendMessage(message: String) { - if (message.isNotBlank()) { - val colored = ChatColor.translateAlternateColorCodes('&', message) - colored.lines().forEach { sender.sendMessage(it) } - } - } - - fun addCompletion(completion: String, type: String) { - var stage = if (completions.size != 0) completions.last() else addCompletionStage(type, type) - if (stage.type != type) { - stage = addCompletionStage(type, type) - } - stage.completions.add(completion) - } - - fun addCompletionStage(name: String, type: String): CompletionStage { - val stage = CompletionStage(name, type) - completions.add(stage) - return stage - } - - fun execute(e: () -> Unit) { - if (runExecutors) { - e() - } - } - - data class CompletionStage(val name: String, val type: String) { - val completions: MutableList = mutableListOf() - } - - enum class State { - CLEAN, - MISSING_ARG, - COMPLETED - } -} diff --git a/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandExecutors.kt b/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandExecutors.kt deleted file mode 100644 index a42ec73..0000000 --- a/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandExecutors.kt +++ /dev/null @@ -1,76 +0,0 @@ -package `in`.kyle.mcspring.subcommands.plugincommand.impl - -import `in`.kyle.mcspring.command.SimpleMethodInjection -import `in`.kyle.mcspring.subcommands.plugincommand.impl.PluginCommandBase.State -import `in`.kyle.mcspring.subcommands.plugincommand.api.Err1 -import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand -import kotlin.reflect.KFunction -import kotlin.reflect.full.createType -import kotlin.reflect.full.isSubtypeOf -import kotlin.reflect.jvm.javaType - -interface PluginCommandExecutors : PluginCommandBase, PluginCommand { - - val injection: SimpleMethodInjection - - override fun otherwise(e: KFunction) { - dirtiesState(requiredStates = arrayOf(State.CLEAN, State.MISSING_ARG)) { - execute { runWithKotlinContext(e) } - } - } - - override fun on(command: String, e: KFunction) { - addCompletion(command, "on") - onPartCondition({ it.equals(command) }, e) - } - - override fun onAny(vararg command: String, e: KFunction) = command.forEach { on(it, e) } - - override fun onInvalid(errorMessage: Err1) { - if (nextPart() != null) { - dirtiesState { - execute { sendMessage(errorMessage(parts[0])) } - } - } - } - - fun onPartCondition(condition: (String?) -> Boolean, e: KFunction) { - if (condition(nextPart())) { - dirtiesState { - val receivesPluginCommand = e.parameters.any { - it.type.isSubtypeOf(PluginCommand::class.createType()) - } - if (receivesPluginCommand) { - runWithKotlinContext(e, sendOutput = false) - } else { - execute { runWithKotlinContext(e) } - } - } - } - } - - override fun then(e: KFunction) = dirtiesState { execute { runWithKotlinContext(e) } } - - fun runWithContext(e: KFunction, types: List>, sendOutput: Boolean = true) { - val nextExecutor = makeNextExecutor() - child = nextExecutor - val out = injection.callWithInjection(e, types, injections.plus(nextExecutor).plus(sender)) - if (out !is Unit && sendOutput) { - sendMessage(out.toString()) - } - } - - private fun runWithKotlinContext(e: KFunction, sendOutput: Boolean = true) { - val types = e.parameters.map { it.type.javaType as Class<*> } - return runWithContext(e, types, sendOutput) - } - - private fun makeNextExecutor(): PluginCommandImpl? { - return if (parts.isNotEmpty()) { - PluginCommandImpl(injection, sender, parts, runExecutors).apply { - injections.addAll(this.injections) - parts.removeAt(0) - } - } else null - } -} diff --git a/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandImpl.kt b/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandImpl.kt deleted file mode 100644 index be47c4b..0000000 --- a/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandImpl.kt +++ /dev/null @@ -1,38 +0,0 @@ -package `in`.kyle.mcspring.subcommands.plugincommand.impl - -import `in`.kyle.mcspring.command.SimpleMethodInjection -import `in`.kyle.mcspring.subcommands.plugincommand.impl.PluginCommandBase.CompletionStage -import `in`.kyle.mcspring.subcommands.plugincommand.impl.PluginCommandBase.State -import `in`.kyle.mcspring.subcommands.plugincommand.impl.javasupport.PluginCommandExecutorsJavaSupport -import `in`.kyle.mcspring.subcommands.plugincommand.impl.javasupport.PluginCommandWithJavaSupport -import org.bukkit.command.CommandSender - -class PluginCommandImpl( - override val injection: SimpleMethodInjection, - override val sender: CommandSender, - override val parts: MutableList, - override val runExecutors: Boolean -) : PluginCommandBase, PluginCommandWith, PluginCommandRequires, PluginCommandExecutors, - PluginCommandExecutorsJavaSupport, PluginCommandWithJavaSupport { - - override val injections = mutableListOf() - override var state: State = State.CLEAN - override var child: PluginCommandBase? = null - override val completions: MutableList = mutableListOf() - - override fun dirtiesState(requiredStates: Array, - resultingState: State, - action: () -> Unit) { - if (state in requiredStates) { - action() - state = resultingState - } - } - - override fun nextPart(): String? { - val part = parts.getOrNull(0).takeIf { state == State.CLEAN } - assert(part?.isNotBlank() ?: true) - return part - } -} - diff --git a/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandRequires.kt b/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandRequires.kt deleted file mode 100644 index 81bc5af..0000000 --- a/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandRequires.kt +++ /dev/null @@ -1,29 +0,0 @@ -package `in`.kyle.mcspring.subcommands.plugincommand.impl - -import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand -import org.bukkit.command.ConsoleCommandSender -import org.bukkit.entity.Player - -internal interface PluginCommandRequires : PluginCommandBase, PluginCommand { - - override fun requires(predicate: Boolean, errorMessage: () -> String) { - if (!predicate) { - dirtiesState { sendMessage(errorMessage()) } - } - } - - override fun requiresPlayerSender(errorMessage: () -> String) { - requires(sender is Player, errorMessage) - } - - override fun requiresConsoleSender(errorMessage: () -> String) { - requires(sender is ConsoleCommandSender, errorMessage) - } - - override fun requiresPermission(permission: String, errorMessage: () -> String) { - requires(sender.hasPermission(permission), errorMessage) - } - - override fun requiresOp(errorMessage: () -> String) = requires(sender.isOp, errorMessage) - -} diff --git a/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandWith.kt b/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandWith.kt deleted file mode 100644 index 05c0b96..0000000 --- a/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/PluginCommandWith.kt +++ /dev/null @@ -1,64 +0,0 @@ -package `in`.kyle.mcspring.subcommands.plugincommand.impl - -import `in`.kyle.mcspring.subcommands.plugincommand.api.Err1 -import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand -import `in`.kyle.mcspring.subcommands.plugincommand.impl.PluginCommandBase.State -import org.bukkit.Bukkit - -interface PluginCommandWith : PluginCommandBase, PluginCommand { - - override fun withString() = with({ it }, stageName = "string") - - override fun withSentence() { - addCompletionStage("sentence", "with") - if (nextPart() != null) { - injections.add(parts.joinToString(" ")) - parts.clear() - } - } - - override fun withInt(errorMessage: Err1) = with({ it.toIntOrNull() }, errorMessage, "int") - - override fun withDouble(errorMessage: Err1) = with({ it.toDoubleOrNull() }, - errorMessage, "double") - - @Suppress("DEPRECATION") - override fun withOfflinePlayer(errorMessage: Err1) { - with({ Bukkit.getOfflinePlayer(it) }, errorMessage, "offline player") - } - - override fun withPlayer(errorMessage: Err1) { - withMap(Bukkit.getOnlinePlayers().associateBy { it.name }, errorMessage, "player") - } - - override fun withWorld(errorMessage: Err1) { - withMap(Bukkit.getWorlds().associateBy { it.name }, errorMessage, "world") - } - - override fun withXYZInt(errorMessage: Err1) = repeat(3) { withInt(errorMessage) } - - override fun withMap(options: Map, errorMessage: Err1, stageName: String) { - with({ options[it] }, errorMessage, stageName) - options.keys.forEach { addCompletion(it, "with") } - } - - override fun withAny(options: Collection, errorMessage: Err1, stageName: String) { - withMap(options.associateBy { it }, errorMessage, stageName) - } - - override fun with(processor: (String) -> Any?, errorMessage: Err1, stageName: String) { - addCompletionStage(stageName, "with") - val part = nextPart() - if (part != null) { - val parsed = processor(part) - if (parsed != null) { - parts.removeAt(0) - injections.add(parsed) - } else { - dirtiesState { sendMessage(errorMessage(part)) } - } - } else { - dirtiesState(resultingState = State.MISSING_ARG) {} - } - } -} diff --git a/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/javasupport/HighIQExecutors.kt b/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/javasupport/HighIQExecutors.kt deleted file mode 100644 index 04188d0..0000000 --- a/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/javasupport/HighIQExecutors.kt +++ /dev/null @@ -1,58 +0,0 @@ -package `in`.kyle.mcspring.subcommands.plugincommand.impl.javasupport - -import java.io.Serializable - -interface HighIQExecutors { - - interface E1 : HighIQExecutors, Serializable { - fun handle(a1: A) - } - - interface E2 : HighIQExecutors, Serializable { - fun handle(a: A, b: B) - } - - interface E3 : HighIQExecutors, Serializable { - fun handle(a: A, b: B, c: C) - } - - interface E4 : HighIQExecutors, Serializable { - fun handle(a: A, b: B, c: C, d: D) - } - - interface E5 : HighIQExecutors, Serializable { - fun handle(a: A, b: B, c: C, d: D, e: E) - } - - interface E6 : HighIQExecutors, Serializable { - fun handle(a: A, b: B, c: C, d: D, e: E, f: F) - } - - interface O0 : HighIQExecutors, Serializable { - fun handle(): Any? - } - - interface O1 : HighIQExecutors, Serializable { - fun handle(a1: A): Any? - } - - interface O2 : HighIQExecutors, Serializable { - fun handle(a: A, b: B): Any? - } - - interface O3 : HighIQExecutors, Serializable { - fun handle(a: A, b: B, c: C): Any? - } - - interface O4 : HighIQExecutors, Serializable { - fun handle(a: A, b: B, c: C, d: D): Any? - } - - interface O5 : HighIQExecutors, Serializable { - fun handle(a: A, b: B, c: C, d: D, e: E): Any? - } - - interface O6 : HighIQExecutors, Serializable { - fun handle(a: A, b: B, c: C, d: D, e: E, f: F): Any? - } -} diff --git a/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/javasupport/PluginCommandExecutorsJavaSupport.kt b/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/javasupport/PluginCommandExecutorsJavaSupport.kt deleted file mode 100644 index a464e3c..0000000 --- a/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/javasupport/PluginCommandExecutorsJavaSupport.kt +++ /dev/null @@ -1,121 +0,0 @@ -package `in`.kyle.mcspring.subcommands.plugincommand.impl.javasupport - -import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand -import `in`.kyle.mcspring.subcommands.plugincommand.impl.PluginCommandBase.State -import `in`.kyle.mcspring.subcommands.plugincommand.impl.PluginCommandExecutors -import `in`.kyle.mcspring.subcommands.plugincommand.impl.javasupport.HighIQExecutors.* -import java.lang.invoke.SerializedLambda -import java.lang.reflect.Method -import kotlin.reflect.KFunction - -@Suppress("UNCHECKED_CAST") -interface PluginCommandExecutorsJavaSupport : PluginCommandExecutors { - - override fun on(command: String, e: O0) = this.on(command, e::handle as KFunction) - - override fun on(command: String, e: O1) = this.on(command, e::handle as KFunction) - - override fun on(command: String, e: O2) = this.on(command, e::handle as KFunction) - - override fun on(command: String, e: O3) = this.on(command, e::handle as KFunction) - - override fun on(command: String, e: O4) = this.on(command, e::handle as KFunction) - - override fun on(command: String, e: O5) = this.on(command, e::handle as KFunction) - - override fun on(command: String, e: O6) = this.on(command, e::handle as KFunction) - - override fun on(command: String, e: E1) = this.on(command, e::handle as KFunction) - - override fun on(command: String, e: E2) = this.on(command, e::handle as KFunction) - - override fun on(command: String, e: E3) = this.on(command, e::handle as KFunction) - - override fun on(command: String, e: E4) = this.on(command, e::handle as KFunction) - - override fun on(command: String, e: E5) = this.on(command, e::handle as KFunction) - - override fun on(command: String, e: E6) = this.on(command, e::handle as KFunction) - - override fun then(e: O0) = this.then(e, e::handle as KFunction) - - override fun then(e: O1) = this.then(e, e::handle as KFunction) - - override fun then(e: O2) = this.then(e, e::handle as KFunction) - - override fun then(e: O3) = this.then(e, e::handle as KFunction) - - override fun then(e: O4) = this.then(e, e::handle as KFunction) - - override fun then(e: O5) = this.then(e, e::handle as KFunction) - - override fun then(e: O6) = this.then(e, e::handle as KFunction) - - override fun then(e: E1) = this.then(e, e::handle as KFunction) - - override fun then(e: E2) = this.then(e, e::handle as KFunction) - - override fun then(e: E3) = this.then(e, e::handle as KFunction) - - override fun then(e: E4) = this.then(e, e::handle as KFunction) - - override fun then(e: E5) = this.then(e, e::handle as KFunction) - - override fun then(e: E6) = this.then(e, e::handle as KFunction) - - override fun otherwise(e: O0) = this.then(e, e::handle as KFunction) - - override fun otherwise(e: O1) = this.then(e, e::handle as KFunction) - - override fun otherwise(e: O2) = this.then(e, e::handle as KFunction) - - override fun otherwise(e: O3) = this.then(e, e::handle as KFunction) - - override fun otherwise(e: O4) = this.then(e, e::handle as KFunction) - - override fun otherwise(e: O5) = this.then(e, e::handle as KFunction) - - override fun otherwise(e: O6) = this.then(e, e::handle as KFunction) - - override fun otherwise(e: E1) = this.then(e, e::handle as KFunction) - - override fun otherwise(e: E2) = this.then(e, e::handle as KFunction) - - override fun otherwise(e: E3) = this.then(e, e::handle as KFunction) - - override fun otherwise(e: E4) = this.then(e, e::handle as KFunction) - - override fun otherwise(e: E5) = this.then(e, e::handle as KFunction) - - override fun otherwise(e: E6) = this.then(e, e::handle as KFunction) - - override fun otherwise(message: String) { - dirtiesState(requiredStates = arrayOf(State.CLEAN, State.MISSING_ARG)) { sendMessage(message) } - } - - private fun then(e: HighIQExecutors, function: KFunction) { - dirtiesState { - execute { - val types: List> = getMethod(e).parameterTypes.toList() - val sendOutput = types.any { PluginCommand::class.java.isAssignableFrom(it) } - super.runWithContext(function, types, sendOutput) - } - } - } - - companion object { - private fun getMethod(e: HighIQExecutors): Method { - val argCount = e::class.java.declaredMethods.find { it.name == "handle" }!!.parameterCount - val writeReplace = e::class.java.getDeclaredMethod("writeReplace") - writeReplace.isAccessible = true - val sl = writeReplace.invoke(e) as SerializedLambda - val methodName = sl.implMethodName - val clazz = Class.forName(sl.implClass.replace("/", ".")) - - return clazz.methods.plus(clazz.declaredMethods) - .filter { it.name == methodName }.first { it.parameters.size == argCount } - ?: throw RuntimeException("Method not found") - } - } -} - diff --git a/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/javasupport/PluginCommandWithJavaSupport.kt b/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/javasupport/PluginCommandWithJavaSupport.kt deleted file mode 100644 index 482b629..0000000 --- a/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/plugincommand/impl/javasupport/PluginCommandWithJavaSupport.kt +++ /dev/null @@ -1,21 +0,0 @@ -package `in`.kyle.mcspring.subcommands.plugincommand.impl.javasupport - -import `in`.kyle.mcspring.subcommands.plugincommand.impl.PluginCommandWith - -interface PluginCommandWithJavaSupport : PluginCommandWith { - - override fun withInt(errorMessage: String) = super.withInt { errorMessage } - - override fun withDouble(errorMessage: String) = super.withDouble { errorMessage } - - override fun withPlayer(errorMessage: String) = super.withPlayer { errorMessage } - - override fun withWorld(errorMessage: String) = super.withWorld { errorMessage } - - override fun withXYZInt(errorMessage: String) = super.withXYZInt { errorMessage } - - override fun withMap(options: Map, errorMessage: String) = withMap(options) { errorMessage } - - override fun withAny(options: Collection, errorMessage: String) = withAny(options) { errorMessage } - -} diff --git a/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/tab/TabCommandFactory.kt b/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/tab/TabCommandFactory.kt deleted file mode 100644 index db276b4..0000000 --- a/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/tab/TabCommandFactory.kt +++ /dev/null @@ -1,49 +0,0 @@ -package `in`.kyle.mcspring.subcommands.tab - -import `in`.kyle.mcspring.command.CommandResolver -import `in`.kyle.mcspring.command.SimpleCommandFactory -import `in`.kyle.mcspring.command.SimpleMethodInjection -import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand -import org.bukkit.command.CommandSender -import org.bukkit.command.TabCompleter -import org.bukkit.plugin.Plugin -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean -import org.springframework.context.annotation.Primary -import org.springframework.stereotype.Component -import java.lang.reflect.Method -import org.bukkit.command.Command as BukkitCommand -import org.bukkit.command.PluginCommand as BukkitPluginCommand - -@Primary -@Component -@ConditionalOnBean(Plugin::class) -internal class TabCommandFactory( - injection: SimpleMethodInjection, - commandResolvers: Set, - val plugin: Plugin, - private val tabDiscovery: TabDiscovery -) : SimpleCommandFactory(injection, commandResolvers, plugin) { - - override fun makeCommand(method: Method, obj: Any, name: String): BukkitCommand { - val command = super.makeCommand(method, obj, name) as BukkitPluginCommand - if (method.parameterCount == 1 - && method.parameters[0].type.isAssignableFrom(PluginCommand::class.java)) { - command.tabCompleter = makeTabCompleter(method, obj) - } - return command - } - - private fun methodToConsumer(method: Method, obj: Any): (PluginCommand) -> Unit { - return { - method.isAccessible = true - method.invoke(obj, it) - } - } - - private fun makeTabCompleter(method: Method, obj: Any): TabCompleter { - return TabCompleter { sender: CommandSender, _: BukkitCommand, _: String, strings: Array -> - val consumer = methodToConsumer(method, obj) - tabDiscovery.getCompletions(sender, strings.joinToString(" "), consumer) - } - } -} diff --git a/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/tab/TabDiscovery.kt b/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/tab/TabDiscovery.kt deleted file mode 100644 index e4cbb96..0000000 --- a/mcspring-api/mcspring-subcommands/src/main/kotlin/in/kyle/mcspring/subcommands/tab/TabDiscovery.kt +++ /dev/null @@ -1,46 +0,0 @@ -package `in`.kyle.mcspring.subcommands.tab - -import `in`.kyle.mcspring.command.SimpleMethodInjection -import `in`.kyle.mcspring.subcommands.plugincommand.impl.PluginCommandBase.State -import `in`.kyle.mcspring.subcommands.plugincommand.impl.PluginCommandImpl -import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand -import org.bukkit.command.CommandSender -import org.springframework.stereotype.Component - -@Component -class TabDiscovery( - private val injection: SimpleMethodInjection -) { - fun getCompletions(sender: CommandSender, - commandString: String, - consumer: (PluginCommand) -> Unit): List { - val (parts, prefix) = getCommandParts(commandString) - - val command = PluginCommandImpl(injection, sender, parts, false) - consumer(command) - - val completionCandidates = getCompletions(command) - return completionCandidates.filter { it.startsWith(prefix) } - } - - private fun getCommandParts(command: String): Pair, String> { - val parts = command.split(" ").filter { it.isNotBlank() }.toMutableList() - - val prefix = if (!command.endsWith(" ") && command.isNotBlank()) { - parts.removeAt(parts.size - 1) - } else { - "" - } - return Pair(parts, prefix) - } - - private fun getCompletions(command: PluginCommandImpl): List { - return if (command.child != null) { - getCompletions(command.child as PluginCommandImpl) - } else if (command.state == State.MISSING_ARG || command.state == State.CLEAN) { - command.completions.flatMap { it.completions } - } else { - emptyList() - } - } -} diff --git a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestConsole.kt b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestConsole.kt deleted file mode 100644 index 1ecb9e7..0000000 --- a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestConsole.kt +++ /dev/null @@ -1,30 +0,0 @@ -package `in`.kyle.mcspring.subcommands - -import `in`.kyle.mcspring.command.SimpleMethodInjection -import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand -import `in`.kyle.mcspring.subcommands.plugincommand.impl.PluginCommandImpl -import org.bukkit.command.CommandSender -import org.bukkit.entity.Player -import org.mockito.Mockito.mock - -object TestConsole { - - fun runCommand(commandString: String, - consumer: (PluginCommand) -> Unit, - runExecutors: Boolean = true, - sender: CommandSender = mock(Player::class.java)): PluginCommandImpl { - val command = makeCommand(commandString, sender, runExecutors) - consumer(command) - return command - } - - fun makeCommand(command: String, - player: CommandSender = mock(Player::class.java), - runExecutors: Boolean = true): - PluginCommandImpl { - return PluginCommandImpl(SimpleMethodInjection(listOf()), - player, - command.split(" ").filter { it.isNotBlank() }.toMutableList(), - runExecutors) - } -} diff --git a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestJavaPluginCommand.java b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestJavaPluginCommand.java deleted file mode 100644 index 55eff0f..0000000 --- a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestJavaPluginCommand.java +++ /dev/null @@ -1,56 +0,0 @@ -package in.kyle.mcspring.subcommands; - -import org.bukkit.entity.Player; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import in.kyle.mcspring.command.SimpleMethodInjection; -import in.kyle.mcspring.subcommands.plugincommand.impl.PluginCommandImpl; -import in.kyle.mcspring.subcommands.plugincommand.api.PluginCommand; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -class TestJavaPluginCommand { - - Player sender; - List messages; - - @BeforeEach - void setup() { - messages = new ArrayList<>(); - sender = mock(Player.class); - doAnswer(invocationOnMock -> { - messages.add(invocationOnMock.getArgument(0)); - return null; - }).when(sender).sendMessage(anyString()); - } - - @Test - public void testSimple() { - class Test { - void parse(PluginCommand command) { - command.withString(); - command.then(this::parse2); - } - - String parse2(PluginCommand command, String parsedPart) { - assertThat(command).isNotNull(); - return String.format("part is %s", parsedPart); - } - } - - PluginCommandImpl pluginCommand = - new PluginCommandImpl(new SimpleMethodInjection(Collections.emptyList()), - sender, - new ArrayList<>(Arrays.asList("test-arg", "another-arg")), - true); - new Test().parse(pluginCommand); - assertThat(messages).containsExactly("part is test-arg"); - } -} diff --git a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestPluginCommand.kt b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestPluginCommand.kt deleted file mode 100644 index 5a8ed32..0000000 --- a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestPluginCommand.kt +++ /dev/null @@ -1,177 +0,0 @@ -package `in`.kyle.mcspring.subcommands - -import `in`.kyle.mcspring.subcommands.TestConsole.runCommand -import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand -import org.assertj.core.api.Assertions.assertThat -import org.bukkit.command.CommandSender -import org.bukkit.entity.Player -import org.junit.jupiter.api.Test -import org.mockito.Mockito.* - -internal class TestPluginCommand { - - @Test - fun testDirectExecutor() { - class Test { - fun exec1(command: PluginCommand) = command.on("subcommand1", this::handler1) - fun exec2(command: PluginCommand) { - command.withString() - command.on("subcommand2", this::handler2) - } - - fun handler1() = "handler1" - fun handler2(string: String) = "handler2: $string" - } - - val (sender, outputMessages) = makePlayer() - runCommand("subcommand1", Test()::exec1, true, sender) - assertThat(outputMessages).containsExactly("handler1") - outputMessages.clear() - - runCommand("test-string subcommand2", Test()::exec2, sender = sender) - assertThat(outputMessages).containsExactly("handler2: test-string") - } - - @Test - fun testSenderArg() { - class Test { - fun root(command: PluginCommand) = command.then(this::exec) - - @Suppress("UNUSED_PARAMETER") - fun exec(sender: CommandSender) = "Hello World" - } - - val (sender, outputMessages) = makePlayer() - runCommand("", Test()::root, sender = sender) - assertThat(outputMessages).containsExactly("Hello World") - } - - // @Test - // void testIf() { - // class Test { - // void root(PluginCommand command) { - // command.re(ServerOperator::isOp, this::exec); - // } - // - // void exec(CommandSender sender) { - // sender.sendMessage("Works"); - // } - // } - // Test test = new Test(); - // run(sender, "", test::root); - // assertThat(outputMessages).isEmpty(); - // - // sender.setOp(true); - // run(sender, "", test::root); - // assertThat(outputMessages).containsExactly("Works"); - // } - - @Test - fun testCommandSingleSentenceArg() { - class Test { - fun root(command: PluginCommand) { - command.withSentence() - command.then(this::exec) - } - - fun exec(sentence: String) = sentence - } - - val (sender, outputMessages) = makePlayer() - runCommand("Hello to you world", Test()::root, sender = sender) - assertThat(outputMessages).containsExactly("Hello to you world") - } - - @Test - fun testCommandIntArgs() { - class Test { - fun root(command: PluginCommand) { - command.withXYZInt { "error" } - command.then(this::exec) - } - - fun exec(x: Int, y: Int, z: Int): Boolean { - assertThat(x).isEqualTo(1) - assertThat(y).isEqualTo(2) - assertThat(z).isEqualTo(3) - return true - } - } - - val (sender, outputMessages) = makePlayer() - runCommand("1 2 3", Test()::root, sender = sender) - assertThat(outputMessages).containsExactly("true") - } - - @Test - fun testCommandBranching() { - class Test { - fun root(command: PluginCommand) = command.on("a", this::a) - - private fun a(command: PluginCommand) { - command.on("b", this::b) - command.on("c", this::c) - } - - private fun b(command: PluginCommand) = command.then(this::exec) - - @Suppress("UNUSED_PARAMETER") - private fun c(command: PluginCommand) { - } - - private fun exec() = "Works" - } - - val (sender, outputMessages) = makePlayer() - runCommand("a b", Test()::root, sender = sender) - assertThat(outputMessages).containsExactly("Works") - outputMessages.clear() - - runCommand("a c", Test()::root, sender = sender) - assertThat(outputMessages).isEmpty() - } - - @Test - fun testOtherwise() { - class Test { - fun root(command: PluginCommand) { - command.on("a", this::a) - command.otherwise("no subcommand at root") - } - - fun a(command: PluginCommand) { - command.withInt { "error" } - command.otherwise("should run if int passed or missing arg") - } - } - - val (sender, outputMessages) = makePlayer() - runCommand("", Test()::root, sender = sender) - assertThat(outputMessages).containsExactly("no subcommand at root") - outputMessages.clear() - - runCommand("a", Test()::root, sender = sender) - assertThat(outputMessages).containsExactly("should run if int passed or missing arg") - outputMessages.clear() - runCommand("a 2", Test()::root, sender = sender) - assertThat(outputMessages).containsExactly("should run if int passed or missing arg") - } - - @Test - fun testWithError() { - class Test { - fun root(command: PluginCommand) = command.withInt { "$it is not an int" } - } - - val (sender, outputMessages) = makePlayer() - runCommand("swag", Test()::root, sender = sender) - assertThat(outputMessages).containsExactly("swag is not an int") - } - - private fun makePlayer(): Pair> { - val sender = mock(Player::class.java) - val messages = mutableListOf() - doAnswer { messages.add(it.getArgument(0)) }.`when`(sender).sendMessage(anyString()) - return Pair(sender, messages) - } -} diff --git a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestTabCompletion.kt b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestTabCompletion.kt deleted file mode 100644 index a90f359..0000000 --- a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/TestTabCompletion.kt +++ /dev/null @@ -1,90 +0,0 @@ -package `in`.kyle.mcspring.subcommands - -import `in`.kyle.mcspring.subcommands.TestConsole.runCommand -import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand -import org.assertj.core.api.Assertions.assertThat -import org.assertj.core.api.Assertions.fail -import org.junit.jupiter.api.Test - -internal class TestTabCompletion { - - @Test - fun testWithString() { - class Test { - fun base(command: PluginCommand) { - command.withString() - command.withString() - } - } - - val command = runCommand("hello world", Test()::base, runExecutors = false) - - assertThat(command.completions).hasSize(2) - assertThat(command.completions.first().completions).isEmpty() - assertThat(command.completions.last().completions).isEmpty() - } - - @Test - fun testThenNoExecute() { - class Test { - fun base(command: PluginCommand) { - command.then(this::fail) - } - - fun fail(): Unit = fail("Should not run") - } - - runCommand("", Test()::base, runExecutors = false) - } - - @Test - fun testOnRecurse() { - class Test { - fun base(command: PluginCommand) { - command.on("subcommand-1", this::sub1) - command.on("other-1", this::fail) - command.on("other-2", this::fail) - } - - fun sub1(command: PluginCommand) { - command.on("subcommand-2", this::fail) - } - - fun fail(): Unit = fail("Should not run") - } - - val command = runCommand("subcommand-1 subcommand-2", Test()::base, runExecutors = false) - assertThat(command.completions).hasSize(1) - assertThat(command.completions[0].completions).hasSize(3) - assertThat(command.child).isNotNull() - assertThat(command.child!!.completions).hasSize(1) - } - - @Test - fun testMultiOn() { - class Test { - fun base(command: PluginCommand) { - command.on("a", this::fail) - command.on("b", this::fail) - command.on("c", this::fail) - } - - fun fail(): Unit = fail() - } - - val command = runCommand("", Test()::base, runExecutors = false) - assertThat(command.completions).hasSize(1) - assertThat(command.completions[0].completions).containsExactly("a", "b", "c") - } - - @Test - fun testWithAny() { - class Test { - fun base(command: PluginCommand) = command.withAny(listOf("a", "b", "c")) { "" } - } - - val command = runCommand("", Test()::base, runExecutors = false) - assertThat(command.completions).hasSize(1) - assertThat(command.completions[0].completions).containsExactly("a", "b", "c") - } -} diff --git a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/plugincommand/TestPluginCommandExecutors.kt b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/plugincommand/TestPluginCommandExecutors.kt deleted file mode 100644 index a468cd5..0000000 --- a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/plugincommand/TestPluginCommandExecutors.kt +++ /dev/null @@ -1,58 +0,0 @@ -package `in`.kyle.mcspring.subcommands.plugincommand - -import `in`.kyle.mcspring.subcommands.TestConsole.makeCommand -import `in`.kyle.mcspring.subcommands.plugincommand.impl.PluginCommandBase.State -import org.assertj.core.api.Assertions.assertThat -import org.bukkit.entity.Player -import org.junit.jupiter.api.Test -import org.mockito.ArgumentMatchers.anyString -import org.mockito.Mockito.* - -class TestPluginCommandExecutors { - - @Test - fun testOtherwise() { - val player = mock(Player::class.java) - val command = makeCommand("", player) - command.otherwise("output") - verify(player, only()).sendMessage("output") - } - - @Test - fun testOn() { - val player = mock(Player::class.java) - val command = makeCommand("optionB", player) - command.on("optionA", TestClass()::fail) - command.on("optionB", TestClass()::execute) - verify(player, only()).sendMessage("output") - } - - @Test - fun testOnMissingPart() { - val player = mock(Player::class.java) - val command = makeCommand("", player) - command.on("optionA", TestClass()::fail) - command.on("optionB", TestClass()::fail) - verify(player, never()).sendMessage(anyString()) - assertThat(command.state).isEqualTo(State.CLEAN) - } - - @Test - fun testOnInvalid() { - val player = mock(Player::class.java) - val command = makeCommand("invalidOption", player) - - command.on("optionA", TestClass()::fail) - command.on("optionB", TestClass()::fail) - assertThat(command.state).isEqualTo(State.CLEAN) - command.onInvalid { "no valid option $it" } - assertThat(command.state).isEqualTo(State.COMPLETED) - - verify(player, only()).sendMessage("no valid option invalidOption") - } - - class TestClass { - fun fail(): Any = fail() - fun execute() = "output" - } -} diff --git a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/plugincommand/TestPluginCommandRequires.kt b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/plugincommand/TestPluginCommandRequires.kt deleted file mode 100644 index 1f5a92b..0000000 --- a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/plugincommand/TestPluginCommandRequires.kt +++ /dev/null @@ -1,41 +0,0 @@ -package `in`.kyle.mcspring.subcommands.plugincommand - -import `in`.kyle.mcspring.subcommands.TestConsole.makeCommand -import `in`.kyle.mcspring.subcommands.plugincommand.impl.PluginCommandBase.State -import org.assertj.core.api.Assertions.assertThat -import org.bukkit.command.ConsoleCommandSender -import org.bukkit.entity.Player -import org.junit.jupiter.api.Test -import org.mockito.ArgumentMatchers.anyString -import org.mockito.Mockito.* - -class TestPluginCommandRequires { - @Test - fun testRequires() { - val player = mock(Player::class.java) - val command = makeCommand("test", player) - command.requires(true) {"error"} - verify(player, never()).sendMessage(anyString()) - - command.requires(false) {"error"} - verify(player, only()).sendMessage("error") - } - - @Test - fun testRequiresPlayerSender() { - val player = mock(Player::class.java) - val command = makeCommand("test", player) - command.requiresPlayerSender { "error" } - verify(player, never()).sendMessage(anyString()) - assertThat(command.state).isEqualTo(State.CLEAN) - } - - @Test - fun testRequiresPlayerSenderFail() { - val console = mock(ConsoleCommandSender::class.java) - val command = makeCommand("test", console) - command.requiresPlayerSender { "error" } - verify(console, only()).sendMessage("error") - assertThat(command.state).isEqualTo(State.COMPLETED) - } -} diff --git a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/plugincommand/TestPluginCommandWith.kt b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/plugincommand/TestPluginCommandWith.kt deleted file mode 100644 index ff41ccc..0000000 --- a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/plugincommand/TestPluginCommandWith.kt +++ /dev/null @@ -1,103 +0,0 @@ -package `in`.kyle.mcspring.subcommands.plugincommand - -import `in`.kyle.mcspring.subcommands.TestConsole.makeCommand -import `in`.kyle.mcspring.subcommands.plugincommand.impl.PluginCommandBase.State -import org.assertj.core.api.Assertions.assertThat -import org.bukkit.entity.Player -import org.junit.jupiter.api.Test -import org.mockito.Mockito.* - -internal class TestPluginCommandWith { - - @Test - fun testWithString() { - val command = makeCommand("one two three") - command.withString() - assertThat(command.injections).containsExactly("one") - assertThat(command.parts).containsExactly("two", "three") - } - - @Test - fun testWithSentence() { - val command = makeCommand("one two three") - command.withSentence() - assertThat(command.injections).containsExactly("one two three") - assertThat(command.parts).isEmpty() - } - - @Test - fun testWithInt() { - val command = makeCommand("123") - command.withInt { "" } - assertThat(command.injections).containsExactly(123) - assertThat(command.parts).isEmpty() - } - - @Test - fun testWithIntInvalid() { - val player = mock(Player::class.java) - val command = makeCommand("onetwothree", player) - command.withInt { "invalid int $it" } - assertThat(command.injections).isEmpty() - verify(player, only()).sendMessage("invalid int onetwothree") - } - - @Test - fun testWithDouble() { - val command = makeCommand("123.456") - command.withDouble { "" } - assertThat(command.injections).containsExactly(123.456) - assertThat(command.parts).isEmpty() - } - - @Test - fun testWithDoubleInvalid() { - val player = mock(Player::class.java) - val command = makeCommand("onetwothree.fourfivesix", player) - command.withDouble { "invalid double $it" } - assertThat(command.injections).isEmpty() - verify(player, only()).sendMessage("invalid double onetwothree.fourfivesix") - } - - @Test - fun testWithMap() { - val options = mapOf("optionA" to 'a', "optionB" to 'b', "optionC" to 'c') - val command = makeCommand("optionB") - command.withMap(options) { "invalid option $it" } - assertThat(command.injections).containsExactly('b') - assertThat(command.parts).isEmpty() - } - - @Test - fun testWithAny() { - val options = listOf("optionA", "optionB", "optionC") - val command = makeCommand("optionB") - command.withAny(options) { "invalid option $it" } - assertThat(command.injections).containsExactly("optionB") - assertThat(command.parts).isEmpty() - } - - @Test - fun testWith() { - var command = makeCommand("option") - command.with({ "output" }) - assertThat(command.injections).containsExactly("output") - assertThat(command.parts).isEmpty() - assertThat(command.state).isEqualTo(State.CLEAN) - - command = makeCommand("option") - command.with({ null }) - assertThat(command.injections).isEmpty() - assertThat(command.parts).containsExactly("option") - assertThat(command.state).isEqualTo(State.COMPLETED) - } - - @Test - fun testWithNoOption() { - val command = makeCommand("") - command.with({ "output" }) - assertThat(command.injections).isEmpty() - assertThat(command.parts).isEmpty() - assertThat(command.state).isEqualTo(State.MISSING_ARG) - } -} diff --git a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/tab/TestTabDiscovery.kt b/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/tab/TestTabDiscovery.kt deleted file mode 100644 index 3f14649..0000000 --- a/mcspring-api/mcspring-subcommands/src/test/java/in/kyle/mcspring/subcommands/tab/TestTabDiscovery.kt +++ /dev/null @@ -1,61 +0,0 @@ -package `in`.kyle.mcspring.subcommands.tab - -import `in`.kyle.mcspring.command.SimpleMethodInjection -import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand -import `in`.kyle.mcspring.test.MCSpringTest -import org.assertj.core.api.Assertions -import org.assertj.core.api.Assertions.assertThat -import org.assertj.core.api.Assertions.fail -import org.bukkit.entity.Player -import org.junit.jupiter.api.Test -import org.mockito.Mockito.mock - -internal class TestTabDiscovery { - - private val tabDiscovery = TabDiscovery(SimpleMethodInjection(emptyList())) - - class TestCommandSinglePart { - fun base(command: PluginCommand) { - command.withAny(listOf("sub1", "sub2", "other3"), "err") - } - } - - class TestCommandMultiPart { - fun base(command: PluginCommand) { - command.on("sub1", this::error) - command.on("sub2", this::error) - command.on("other3", this::other3) - } - - private fun other3(command: PluginCommand) { - command.withAny(listOf("other1", "other2"), "fail") - } - - private fun error(): Unit = fail("fail"); - } - - @Test - fun testFullCompletions() { - getCompletions("", TestCommandSinglePart()::base).also { - assertThat(it).containsExactly("sub1", "sub2", "other3") - } - } - - @Test - fun testPartialCompletions() { - getCompletions("sub", TestCommandSinglePart()::base).also { - assertThat(it).containsExactly("sub1", "sub2") - } - } - - @Test - fun testCompletionsMultiPart() { - getCompletions("other3 ", TestCommandMultiPart()::base).also { - assertThat(it).containsExactly("other1", "other2") - } - } - - private fun getCompletions(commandString: String, consumer: (PluginCommand) -> Unit): List { - return tabDiscovery.getCompletions(mock(Player::class.java), commandString, consumer) - } -} diff --git a/mcspring-api/mcspring-test/build.gradle.kts b/mcspring-api/mcspring-test/build.gradle.kts index 23a65d3..0761443 100644 --- a/mcspring-api/mcspring-test/build.gradle.kts +++ b/mcspring-api/mcspring-test/build.gradle.kts @@ -1,8 +1,9 @@ dependencies { - compile(project(":mcspring-api:mcspring-base")) - compile("org.springframework.boot:spring-boot-test") - compile("org.springframework.boot:spring-boot-starter-test") - compile("org.assertj:assertj-core:3.15.0") - compile("org.junit.jupiter:junit-jupiter-engine:5.3.2") - compileOnly("org.spigotmc:spigot-api") + api("org.springframework.boot:spring-boot-test") + api("org.springframework.boot:spring-boot-starter-test") + api(project(":mcspring-api:mcspring-base")) + implementation("org.springframework.boot:spring-boot-test") + implementation("org.springframework.boot:spring-boot-starter-test") + implementation("org.assertj:assertj-core:3.15.0") + implementation("org.junit.jupiter:junit-jupiter-engine:5.3.2") } diff --git a/mcspring-api/mcspring-test/pom.xml b/mcspring-api/mcspring-test/pom.xml deleted file mode 100644 index 5835a29..0000000 --- a/mcspring-api/mcspring-test/pom.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - mcspring-api - in.kyle.mcspring - 0.0.9 - - 4.0.0 - - mcspring-test - - - - org.spigotmc - spigot-api - - - in.kyle.mcspring - mcspring-base - ${project.parent.version} - - - org.springframework.boot - spring-boot-test - - - org.assertj - assertj-core - 3.15.0 - - - org.springframework.boot - spring-boot-starter-test - ${spring.version} - - - junit - junit - - - org.junit.vintage - junit-vintage-engine - - - - - org.junit.jupiter - junit-jupiter-engine - 5.3.2 - - - diff --git a/mcspring-api/mcspring-test/src/main/kotlin/in/kyle/mcspring/test/SpringSpigotSupport.kt b/mcspring-api/mcspring-test/src/main/kotlin/in/kyle/mcspring/test/SpringSpigotSupport.kt index 7a9ae95..db92429 100644 --- a/mcspring-api/mcspring-test/src/main/kotlin/in/kyle/mcspring/test/SpringSpigotSupport.kt +++ b/mcspring-api/mcspring-test/src/main/kotlin/in/kyle/mcspring/test/SpringSpigotSupport.kt @@ -1,11 +1,13 @@ package `in`.kyle.mcspring.test -import `in`.kyle.mcspring.SpringPlugin import org.springframework.boot.SpringBootConfiguration import org.springframework.boot.autoconfigure.EnableAutoConfiguration import org.springframework.context.annotation.ComponentScan @SpringBootConfiguration @EnableAutoConfiguration -@ComponentScan(basePackageClasses = [SpringSpigotSupport::class, SpringPlugin::class]) +@ComponentScan( + basePackageClasses = [SpringSpigotSupport::class], + basePackages = ["in.kyle.mcspring"] +) internal open class SpringSpigotSupport diff --git a/mcspring-api/mcspring-test/src/main/kotlin/in/kyle/mcspring/test/command/TestCommandExecutor.kt b/mcspring-api/mcspring-test/src/main/kotlin/in/kyle/mcspring/test/command/TestCommandExecutor.kt index 170e5aa..2e8b154 100644 --- a/mcspring-api/mcspring-test/src/main/kotlin/in/kyle/mcspring/test/command/TestCommandExecutor.kt +++ b/mcspring-api/mcspring-test/src/main/kotlin/in/kyle/mcspring/test/command/TestCommandExecutor.kt @@ -9,7 +9,7 @@ import org.springframework.stereotype.Component @Component class TestCommandExecutor( - private val registration: TestCommandRegistration +// private val registration: TestCommandRegistration ) { fun makeTestPlayer(): Pair> { @@ -31,7 +31,7 @@ class TestCommandExecutor( if (parts.isNotEmpty()) { val label = parts[0] val args = parts.subList(1, parts.size) - registration.run(label, sender, label, args.toTypedArray()) +// registration.run(label, sender, label, args.toTypedArray()) } else { throw RuntimeException("Empty command") } diff --git a/mcspring-api/mcspring-test/src/main/kotlin/in/kyle/mcspring/test/command/TestCommandRegistration.kt b/mcspring-api/mcspring-test/src/main/kotlin/in/kyle/mcspring/test/command/TestCommandRegistration.kt index fac88fa..7697eb3 100644 --- a/mcspring-api/mcspring-test/src/main/kotlin/in/kyle/mcspring/test/command/TestCommandRegistration.kt +++ b/mcspring-api/mcspring-test/src/main/kotlin/in/kyle/mcspring/test/command/TestCommandRegistration.kt @@ -1,53 +1,50 @@ package `in`.kyle.mcspring.test.command import `in`.kyle.mcspring.command.Command -import `in`.kyle.mcspring.command.CommandRegistration -import `in`.kyle.mcspring.command.CommandResolver -import `in`.kyle.mcspring.command.SimpleMethodInjection +import `in`.kyle.mcspring.command.registration.CommandRegistration +import `in`.kyle.mcspring.command.execution.SimpleMethodInjection import org.bukkit.command.CommandSender import org.springframework.stereotype.Component import java.lang.reflect.Method import java.util.* import kotlin.reflect.KFunction -import kotlin.reflect.jvm.javaMethod -import kotlin.reflect.jvm.kotlinFunction typealias CommandSig = (CommandSender, String, Array) -> Unit @Component class TestCommandRegistration( - private val injection: SimpleMethodInjection, - private val commandResolvers: Set + private val injection: SimpleMethodInjection +// private val commandResolvers: Set ) : CommandRegistration { private val commandExecutors: MutableMap = HashMap() override fun register(command: Command, method: Method, obj: Any) { - @Suppress("UNCHECKED_CAST") - val executor = makeExecutor(method.kotlinFunction as KFunction, obj) - command.aliases.plus(command.value).forEach { commandExecutors[it] = executor } +// @Suppress("UNCHECKED_CAST") +// val executor = makeExecutor(method.kotlinFunction as KFunction, obj) +// command.aliases.plus(command.value).forEach { commandExecutors[it] = executor } } - private fun makeExecutor(e: KFunction, obj: Any): CommandSig { - return { sender: CommandSender, label: String, args: Array -> - val temp = CommandResolver.Command(sender, args.toList(), label) - val miscResolvers = injection.makeResolvers(listOf(sender)) - val contextResolvers = commandResolvers.map { it.makeResolver(temp) } - - val parameterTypes = e.javaMethod!!.parameterTypes.toList() - val parameters = injection.getParameters(parameterTypes, contextResolvers.plus(miscResolvers)) - val result = e.call(obj, *parameters) - if (result !is Unit) { - sender.sendMessage(result.toString()) - } - } - } - - fun run(command: String, sender: CommandSender, label: String, args: Array) { - if (command in commandExecutors) { - commandExecutors[command]!!(sender, label, args) - } else { - throw RuntimeException("Command $command is not registered. Make sure to @Import it") - } - } +// private fun makeExecutor(e: KFunction, obj: Any): CommandSig { +// return { sender: CommandSender, label: String, args: Array -> +// val temp = CommandResolver.Command(sender, args.toList(), label) +// val miscResolvers = injection.makeResolvers(listOf(sender)) +// val contextResolvers = commandResolvers.map { it.makeResolver(temp) } +// +// val parameterTypes = e.javaMethod!!.parameterTypes.toList() +// val parameters = injection.getParameters(parameterTypes, contextResolvers.plus(miscResolvers)) +// val result = e.call(obj, *parameters) +// if (result !is Unit) { +// sender.sendMessage(result.toString()) +// } +// } +// } +// +// fun run(command: String, sender: CommandSender, label: String, args: Array) { +// if (command in commandExecutors) { +// commandExecutors[command]!!(sender, label, args) +// } else { +// throw RuntimeException("Command $command is not registered. Make sure to @Import it") +// } +// } } diff --git a/mcspring-api/mcspring-vault/build.gradle.kts b/mcspring-api/mcspring-vault/build.gradle.kts index a1e332b..ea55dc5 100644 --- a/mcspring-api/mcspring-vault/build.gradle.kts +++ b/mcspring-api/mcspring-vault/build.gradle.kts @@ -7,9 +7,8 @@ repositories { } dependencies { - compile(project(":mcspring-api:mcspring-base")) - compileOnly("org.spigotmc:spigot-api") - compileOnly("com.github.MilkBowl:VaultAPI:1.7") { + api(project(":mcspring-api:mcspring-base")) + api("com.github.MilkBowl:VaultAPI:1.7") { exclude(group = "org.bukkit", module = "bukkit") } } diff --git a/mcspring-build/mcspring-plugin-manager/build.gradle.kts b/mcspring-build/mcspring-plugin-manager/build.gradle.kts index da6e3dc..cb6b1da 100644 --- a/mcspring-build/mcspring-plugin-manager/build.gradle.kts +++ b/mcspring-build/mcspring-plugin-manager/build.gradle.kts @@ -1,5 +1,4 @@ dependencies { - compile(project(":mcspring-api:mcspring-subcommands")) - testCompile(project(":mcspring-api:mcspring-test")) - testCompile("org.springframework.boot:spring-boot-starter-test") + api(project(":mcspring-api:mcspring-commands-dsl")) + testImplementation(project(":mcspring-api:mcspring-test")) } diff --git a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandAbout.kt b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandAbout.kt index 16d62c2..4c266b1 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandAbout.kt +++ b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandAbout.kt @@ -1,20 +1,29 @@ package `in`.kyle.mcspring.manager.commands import `in`.kyle.mcspring.command.Command +import `in`.kyle.mcspring.commands.dsl.commandExecutor import org.bukkit.Server import org.springframework.boot.info.BuildProperties import org.springframework.core.SpringVersion import org.springframework.stereotype.Component @Component -internal class CommandAbout { +internal class CommandAbout( + private val properties: BuildProperties, + private val server: Server +) { @Command(value = "about", description = "Provides information about current library versions in use") - fun about(properties: BuildProperties, server: Server): String = """ - Plugin Name: ${properties.name} - Plugin Version: ${properties.version} - Spring Version: ${SpringVersion.getVersion()} - Bukkit Version: ${server.bukkitVersion} - """.trimIndent() + fun about() = commandExecutor { + then { + val aboutString = """ + Plugin Name: ${properties.name} + Plugin Version: ${properties.version} + Spring Version: ${SpringVersion.getVersion()} + Bukkit Version: ${server.bukkitVersion} + """.trimIndent() + message(aboutString) + } + } } diff --git a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandClassLoader.kt b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandClassLoader.kt index 4328700..b0c0bbb 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandClassLoader.kt +++ b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandClassLoader.kt @@ -1,7 +1,7 @@ package `in`.kyle.mcspring.manager.commands import `in`.kyle.mcspring.command.Command -import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand +import `in`.kyle.mcspring.commands.dsl.commandExecutor import org.springframework.stereotype.Component @Component @@ -9,13 +9,18 @@ internal class CommandClassLoader { @Command( value = "classloader", + aliases = ["cl"], description = "Show ClassLoader information for a specific class", usage = "/classloader " ) - fun classLoader(command: PluginCommand) { - command.withString() - command.then(this::executeClassLoader) - command.otherwise("Usage: /classloader ") + fun classLoader() = commandExecutor { + val className by stringArg { + missing { + message("Usage: /$label ") + } + } + + then { message(executeClassLoader(className)) } } private fun executeClassLoader(clazz: String): String { @@ -23,8 +28,8 @@ internal class CommandClassLoader { val classLoader = aClass.classLoader.toString() val protectionDomain = aClass.protectionDomain.codeSource.location.toString() return """ - ClassLoader: $classLoader - Domain: $protectionDomain - """.trimIndent() + ClassLoader: $classLoader + Domain: $protectionDomain + """.trimIndent() } } diff --git a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandGamemode.kt b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandGamemode.kt index a120760..438cf47 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandGamemode.kt +++ b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandGamemode.kt @@ -1,11 +1,13 @@ package `in`.kyle.mcspring.manager.commands import `in`.kyle.mcspring.command.Command -import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand +import `in`.kyle.mcspring.commands.dsl.commandExecutor import org.bukkit.GameMode import org.bukkit.entity.Player import org.springframework.stereotype.Component +import kotlin.contracts.ExperimentalContracts +@ExperimentalContracts @Component internal class CommandGamemode { @@ -15,29 +17,29 @@ internal class CommandGamemode { description = "Set your game mode", usage = "/gamemode " ) - fun gamemode(command: PluginCommand) { - command.requiresPlayerSender { "Only players can run this command." } - - val gamemodes = mutableMapOf() - GameMode.values().forEach { - @Suppress("DEPRECATION") - gamemodes[it.value.toString()] = it - gamemodes[it.name.toLowerCase()] = it + fun gamemode() = commandExecutor { + requirePlayer { message("Only players can run this command.") } + + val gameMode by mapArg { + parser { + map(GameMode.values().associateBy { it.name.toLowerCase() }) + map(GameMode.values().associateBy { it.value.toString() }) + } + missing { message("Usage: /$label ") } + invalid { message("Invalid game mode $it") } } - command.withMap(gamemodes) { "$it is not a valid game mode" } - command.then(this::gamemodeExecutor) - command.otherwise("Usage: /gamemode ") + then { message(gamemodeExecutor(sender as Player, gameMode)) } + } + + private fun gamemodeExecutor(target: Player, gameMode: GameMode): String { + target.gameMode = gameMode + return "Game mode set to ${gameMode.name.toLowerCase()}" } @Command(value = "gmc", description = "Set your game mode to creative") - fun gmc(sender: Player): String = gamemodeExecutor(sender, GameMode.CREATIVE) + fun gmc() = commandExecutor { then { message(gamemodeExecutor(sender as Player, GameMode.CREATIVE)) } } @Command(value = "gms", description = "Set your game mode to survival") - fun gms(sender: Player): String = gamemodeExecutor(sender, GameMode.SURVIVAL) - - private fun gamemodeExecutor(sender: Player, gameMode: GameMode): String { - sender.gameMode = gameMode - return "Game mode set to ${gameMode.name.toLowerCase()}" - } + fun gms() = commandExecutor { then { message(gamemodeExecutor(sender as Player, GameMode.SURVIVAL)) } } } diff --git a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandHeal.kt b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandHeal.kt index 8e20a6f..addef8b 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandHeal.kt +++ b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandHeal.kt @@ -1,7 +1,8 @@ package `in`.kyle.mcspring.manager.commands import `in`.kyle.mcspring.command.Command -import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand +import `in`.kyle.mcspring.commands.dsl.commandExecutor +import `in`.kyle.mcspring.commands.dsl.otherwise import org.bukkit.entity.Player import org.springframework.stereotype.Component @@ -13,16 +14,28 @@ internal class CommandHeal { description = "Heal yourself or another player", usage = "/heal ?" ) - fun heal(command: PluginCommand) { - command.requiresPlayerSender { "Sender must be a player" } - command.withPlayer { "Player $it not found" } - command.then(this::executeHeal) - command.otherwise(this::executeHeal) + fun heal() = commandExecutor { + requirePlayer { message("Sender must be a player") } + + val target by playerArg { + default { sender as Player } + invalid { message("Target player $it not found") } + } + + val health by doubleArg { + default { 20.0 } + parser { + between(0.0, 20.0) otherwise { message("Heal value must be between 0 and 20") } + } + invalid { message("Invalid health amount $it") } + } + + then { message(executeHeal(target, health)) } } - private fun executeHeal(target: Player): String { + private fun executeHeal(target: Player, health: Double): String { @Suppress("DEPRECATION") - target.health = target.maxHealth - return "Healed ${target.name}" + target.health += health + return "Healed ${target.name} by $health" } } diff --git a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandOp.kt b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandOp.kt index 45c2e13..d8f9d98 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandOp.kt +++ b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandOp.kt @@ -1,8 +1,9 @@ package `in`.kyle.mcspring.manager.commands import `in`.kyle.mcspring.command.Command -import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand +import `in`.kyle.mcspring.commands.dsl.commandExecutor import org.bukkit.command.CommandSender +import org.bukkit.entity.Player import org.springframework.stereotype.Component @Component @@ -13,10 +14,12 @@ internal class CommandOp { description = "Toggle yourself or another players OP status", usage = "/op ?" ) - fun op(command: PluginCommand) { - command.withPlayer { "Player $it not found" } - command.then(this::toggleOp) - command.otherwise(this::toggleOp) + fun op() = commandExecutor { + val target by playerArg { + default { sender as? Player } + invalid { message("Player $it not found") } + } + then { message(toggleOp(target)) } } private fun toggleOp(target: CommandSender): String { diff --git a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandPlugin.kt b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandPlugin.kt index 29e3576..7634868 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandPlugin.kt +++ b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandPlugin.kt @@ -2,7 +2,7 @@ package `in`.kyle.mcspring.manager.commands import `in`.kyle.mcspring.command.Command import `in`.kyle.mcspring.manager.controller.PluginController -import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand +import `in`.kyle.mcspring.commands.dsl.commandExecutor import org.bukkit.plugin.Plugin import org.springframework.boot.autoconfigure.condition.ConditionalOnBean import org.springframework.stereotype.Component @@ -10,9 +10,7 @@ import java.nio.file.Path @Component @ConditionalOnBean(Plugin::class) -internal class CommandPlugin( - private val pluginController: PluginController -) { +internal class CommandPlugin(private val pluginController: PluginController) { @Command( value = "plugin", @@ -20,24 +18,38 @@ internal class CommandPlugin( description = "Load/unload/reload a specific plugin", usage = "/plugin " ) - fun plugin(command: PluginCommand) { - command.on("load", this::load) - command.on("unload", this::unload) - command.on("list", this::executeListPlugins) - command.otherwise("Usage: /plugin ") + fun plugin() = commandExecutor { + subcommand { + on("load", commandExecutor = load()) + on("unload", commandExecutor = unload()) + on("list") { then { message(executeListPlugins()) } } + + missing { + val subs = subCommands.keys.joinToString(separator = "|") { it.first() } + message("Usage: $label <$subs>") + } + } } - private fun load(command: PluginCommand) { - command.withMap(pluginController.loadablePlugins) { "Plugin $it not found or is already loaded" } - command.then(this::executeLoad) - command.otherwise("Usage: /plugin load ") + private fun load() = commandExecutor { + val path by mapArg { + parser { + map(pluginController.loadablePlugins) + } + invalid { message("Plugin $it not found or it is already loaded") } + } + then { executeLoad(path) } } - private fun unload(command: PluginCommand) { - val plugins = pluginController.plugins.associateBy({ it.name }, { it }) - command.withMap(plugins) { "Plugin $it is not loaded" } - command.then(this::executeDisable) - command.otherwise("Usage: /plugin unload ") + private fun unload() = commandExecutor { + val plugin by mapArg { + parser { + map(pluginController.plugins.associateBy({ it.name }, { it })) + } + invalid { message("Plugin $it is not loaded") } + missing { message("Usage: $label ${args[0]} ") } + } + then { message(executeDisable(plugin)) } } private fun executeListPlugins(): String { diff --git a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandReload.kt b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandReload.kt new file mode 100644 index 0000000..cab5dc1 --- /dev/null +++ b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandReload.kt @@ -0,0 +1,25 @@ +package `in`.kyle.mcspring.manager.commands + +import `in`.kyle.mcspring.command.Command +import `in`.kyle.mcspring.commands.dsl.commandExecutor +import org.bukkit.Bukkit +import org.bukkit.plugin.Plugin +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean +import org.springframework.stereotype.Component + +@Component +@ConditionalOnBean(Plugin::class) +class CommandReload { + + @Command( + value = "reload", + aliases = ["rl"], + description = "Reload the server" + ) + fun reload() = commandExecutor { + then { + sender.sendMessage("Reloading the server...") + Bukkit.getServer().reload() + } + } +} diff --git a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandSpeed.kt b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandSpeed.kt index 3d3b5fc..b58fc6b 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandSpeed.kt +++ b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandSpeed.kt @@ -1,7 +1,8 @@ package `in`.kyle.mcspring.manager.commands import `in`.kyle.mcspring.command.Command -import `in`.kyle.mcspring.subcommands.plugincommand.api.PluginCommand +import `in`.kyle.mcspring.commands.dsl.commandExecutor +import `in`.kyle.mcspring.commands.dsl.otherwise import org.bukkit.entity.Player import org.springframework.stereotype.Component @@ -13,11 +14,20 @@ internal class CommandSpeed { description = "Set your movement and fly speed", usage = "/speed " ) - fun speed(command: PluginCommand) { - command.requiresPlayerSender { "Sender must be a player" } - command.withDouble("Speed value must be an integer") - command.then(this::speedExecutor) - command.otherwise("Usage: /speed ") + fun speed() = commandExecutor { + requirePlayer { message("Sender must be a player") } + val player = sender as Player + val speed by doubleArg { + parser { + between(0.0, 10.0) otherwise { message("Speed must be between 0 and 10") } + } + invalid { message("Speed $it is not a valid speed") } + missing { + message("Fly Speed = ${player.flySpeed * 10}") + message("Walk Speed = ${player.walkSpeed * 10}") + } + } + then { message(speedExecutor(player, speed / 10)) } } private fun speedExecutor(sender: Player, speed: Double): String { diff --git a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandClassLoader.kt b/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandClassLoader.kt deleted file mode 100644 index 53de286..0000000 --- a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandClassLoader.kt +++ /dev/null @@ -1,19 +0,0 @@ -package `in`.kyle.mcspring.manager.commands - -import `in`.kyle.mcspring.test.MCSpringTest -import `in`.kyle.mcspring.test.command.TestCommandExecutor -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test -import org.springframework.beans.factory.annotation.Autowired - -@MCSpringTest -internal class TestCommandClassLoader { - - @Test - fun testClassLoader(@Autowired commandExecutor: TestCommandExecutor) { - val output = commandExecutor.run("classloader " + javaClass.name) - assertThat(output).hasSize(2) - assertThat(output[0]).isEqualTo("ClassLoader: ${javaClass.classLoader}") - assertThat(output[1]).isEqualTo("Domain: ${javaClass.protectionDomain.codeSource.location}") - } -} diff --git a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandGamemode.kt b/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandGamemode.kt deleted file mode 100644 index 120d29f..0000000 --- a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandGamemode.kt +++ /dev/null @@ -1,35 +0,0 @@ -package `in`.kyle.mcspring.manager.commands - -import `in`.kyle.mcspring.test.MCSpringTest -import `in`.kyle.mcspring.test.command.TestCommandExecutor -import org.assertj.core.api.Assertions.assertThat -import org.bukkit.GameMode -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.CsvSource -import org.mockito.Mockito.times -import org.mockito.Mockito.verify -import org.springframework.beans.factory.annotation.Autowired - -@MCSpringTest -class TestCommandGamemode { - - @Autowired - lateinit var executor: TestCommandExecutor - - @ParameterizedTest - @CsvSource( - "gmc, CREATIVE", - "gms, SURVIVAL", - "gm creative, CREATIVE", - "gm survival, SURVIVAL", - "gm 1, CREATIVE", - "gm 0, SURVIVAL" - ) - fun testGamemodes(command: String, targetGameMode: GameMode) { - val (sender, messages) = executor.makeTestPlayer() - executor.run(sender, command) - assertThat(messages).first().asString() - .isEqualTo("Game mode set to ${targetGameMode.name.toLowerCase()}") - verify(sender, times(1)).gameMode = targetGameMode - } -} diff --git a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandSpeed.kt b/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandSpeed.kt deleted file mode 100644 index bdd060a..0000000 --- a/mcspring-build/mcspring-plugin-manager/src/test/java/in/kyle/mcspring/manager/commands/TestCommandSpeed.kt +++ /dev/null @@ -1,34 +0,0 @@ -package `in`.kyle.mcspring.manager.commands - -import `in`.kyle.mcspring.test.MCSpringTest -import `in`.kyle.mcspring.test.command.TestCommandExecutor -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test -import org.mockito.ArgumentMatchers.anyFloat -import org.mockito.Mockito.* -import org.springframework.beans.factory.annotation.Autowired - -@MCSpringTest -internal class TestCommandSpeed() { - - @Autowired - lateinit var executor: TestCommandExecutor - - @Test - fun testSpeed() { - val (sender, messages) = executor.makeTestPlayer() - executor.run(sender, "speed 10") - assertThat(messages).first().asString().matches("Speed set to [^ ]+") - verify(sender, times(1)).walkSpeed = 10F - verify(sender, times(1)).flySpeed = 10F - } - - @Test - fun testSpeedUsage() { - val (sender, messages) = executor.makeTestPlayer() - executor.run(sender, "speed") - assertThat(messages).first().asString().matches("Usage: .+") - verify(sender, never()).walkSpeed = anyFloat() - verify(sender, never()).flySpeed = anyFloat() - } -} diff --git a/mcspring-examples/simple-factions/build.gradle.kts b/mcspring-examples/simple-factions/build.gradle.kts index 311e64a..80bc545 100644 --- a/mcspring-examples/simple-factions/build.gradle.kts +++ b/mcspring-examples/simple-factions/build.gradle.kts @@ -1,4 +1,4 @@ dependencies { - compile(project(":mcspring-api:mcspring-subcommands")) + implementation(project(":mcspring-api:mcspring-commands-dsl")) compileOnly("org.projectlombok:lombok:1.18.12") } diff --git a/mcspring-examples/simple-factions/src/main/kotlin/org/example/factions/commands/FactionCommand.java b/mcspring-examples/simple-factions/src/main/kotlin/org/example/factions/commands/FactionCommand.java index e0f0958..0c53924 100644 --- a/mcspring-examples/simple-factions/src/main/kotlin/org/example/factions/commands/FactionCommand.java +++ b/mcspring-examples/simple-factions/src/main/kotlin/org/example/factions/commands/FactionCommand.java @@ -10,7 +10,7 @@ import java.util.stream.Collectors; import in.kyle.mcspring.command.Command; -import in.kyle.mcspring.subcommands.plugincommand.api.PluginCommand; +import in.kyle.mcspring.commandDsl.plugincommand.api.PluginCommand; import lombok.RequiredArgsConstructor; @Component diff --git a/settings.gradle.kts b/settings.gradle.kts index 8b405d1..8d4b6fe 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -9,11 +9,16 @@ include(":mcspring-build:mcspring-starter:mcspring-plugin-manager") include(":mcspring-build:mcspring-starter") include(":mcspring-build:mcspring-maven-plugin") include(":mcspring-build") -include(":mcspring-api:mcspring-subcommands") +include(":mcspring-api:mcspring-commands-dsl") include(":mcspring-api:mcspring-base") include(":mcspring-api:mcspring-vault") include(":mcspring-api:mcspring-jar-loader") include(":mcspring-api:mcspring-test") +include(":mcspring-api:mcspring-rx") +include(":mcspring-api:mcspring-guis") +include(":mcspring-api:mcspring-chat") +include(":mcspring-api:mcspring-logging") +include(":mcspring-api:mcspring-common") include(":mcspring-api") project(":mcspring-build:mcspring-starter:mcspring-examples:simple-factions").projectDir = file("mcspring-examples/simple-factions") diff --git a/src/orchid/resources/changelog/1.0.0.md b/src/orchid/resources/changelog/1.0.0.md index 12de9a0..fc1a599 100644 --- a/src/orchid/resources/changelog/1.0.0.md +++ b/src/orchid/resources/changelog/1.0.0.md @@ -1 +1,3 @@ -new stuff lol +* Moved over to Kotlin +* Moved over to Gradle +* Revamped subcommand/tab handling diff --git a/src/orchid/resources/wiki/commands-api.md b/src/orchid/resources/wiki/commands-api.md new file mode 100644 index 0000000..04be492 --- /dev/null +++ b/src/orchid/resources/wiki/commands-api.md @@ -0,0 +1,70 @@ +## The Basics +Commands can be a pain in the ass to write. We tried our best to mitigate most of that pain. + +Annotating any method with `@Command(String)` inside a Spring bean will automatically register that command upon the plugin loading. If the method returns a value, that value will be converted to a string and sent to the CommandSender. There are also three command-specific injections: +* `Player` - The player that sent the command +* `String` - The label of the command +* `String[]` - The args array of the command + +These can be injected in any order or not injected at all if you don't need them. + +Here is an example command, we'll walk through what it actually does: + +```java +@Command("test-command") +public String test(String label, Plugin plugin, Player player) { + return String.format("The command label was '%s'\nThe plugin was '%s'\nThe sender was '%s'", label, plugin.getName(), player.getName(); +} +``` + +This command is run by typing `/test-command` in the console. `String label` corresponds to the label of the command. In this case, it's just `test-command`. `Plugin plugin` corresponds to the plugin the command is registered to. `Plugin` is defined as a Spring bean by mcspring so it can be automatically injected in this method. `Player player` corresponds to the player that sent the command (e.g.: the sender). + +The return type of this command is a String. So the output of this method is then sent to the command sender. + +_It should also be noted that this method may be public/package-protected/protected depending on your preference. It doesn't matter._ + +## Things To Watch Out For +There are some gimmicks to writing these commands. We're trying our best to get these polished out. + +### Do not duplicate `@Command` annotations + +Consider the following example: + +```java +@Command("test") +public String test(CommandSender sender) { + return "Only players can run this"; +} + +@Command("test") +public String test(Player player) { + ... +} +``` + +This will register just 1 command method. + +Instead do: + +```java +@Command("test") +public String test(CommandSender sender) { + if (sender instanceof Player) { + ... + } else { + return "Only players can execute this command"; + } +} +``` + +### The command injection only injects typed beans + +Beans that are referenced by `@Qualifier` and the like are not distinguished. Therefore you should avoid using named beans with command-method injection. + +**NON-WORKING EXAMPLE** +```java +@Command("test") +public String test(@Qualifier("my-bean-1") Api api1, @Qualifier("my-bean-2") Api api2) { + // api1 and api2 will not be injected properly +} +``` diff --git a/src/orchid/resources/wiki/intro.md b/src/orchid/resources/wiki/intro.md new file mode 100644 index 0000000..0d5512b --- /dev/null +++ b/src/orchid/resources/wiki/intro.md @@ -0,0 +1,14 @@ +Long story short, don't make anything extend JavaPlugin and don't create a plugin.yml. + +--- + +### Extending JavaPlugin +You don't need a main class nor do you need to extend JavaPlugin. I know what you're thinking, "what about onEnable/onDisable?" We have you covered. +Instead of using the `onEnable` and `onDisable` methods use the `@PostConstract` and `@PreDestroy` annotations on any method anywhere in the project. You can make as many of these methods as you like. I suggest you create one on every Spring bean that needs something setup (e.g.: connecting to Mongo/MySQL). + +### The plugin.yml +Don't bother with creating `plugin.yml` either, mcspring will take care of that. + +If you need to add a dependency to the `plugin.yml` instead use the `@PluginDepends` annotation somewhere in your project and it will be automatically added for you. + +If you need to set the plugin version, name, or description add the `@SpringPlugin` annotation to a class somewhere inside your plugin. diff --git a/src/orchid/resources/wiki/setup.md b/src/orchid/resources/wiki/setup.md new file mode 100644 index 0000000..edb0045 --- /dev/null +++ b/src/orchid/resources/wiki/setup.md @@ -0,0 +1,29 @@ +I tried to make this as easy as possible. If you're using IntelliJ just do the following. + +1. Create a new project +2. In the create dialog select `Maven -> create from archetype` +3. Click `Add archetype` on the right-hand side +4. Type in the following: + + 1. group: `in.kyle.mcspring` + 2. artifact: `mcspring-starter` + 3. version: `0.0.7` <-- Change this to the latest release over [here](https://github.com/kylepls/mcspring/releases) if I forget to update it. + +Then a project will be created for you with a project-specific Spigot folder already set up for you. + +Next: + +1. Run a Maven install to create the required files +2. Create a new JAR run configuration +3. Select the downloaded `spigot.jar` in the `spigot` folder as the target +4. Change the run environment to the `spigot` folder +5. Add the following **VM flag** `-DIReallyKnowWhatIAmDoingISwear` +6. Add the following **Program Argument** `--nogui` + +![](https://i.imgur.com/waoHVTz.png) + +7. To update the plugin on the server, just run a maven install and restart the server. + +--- + +Now that you're all set up, head over [here](https://github.com/kylepls/mcspring/wiki/The-Commands-API) and read about how to define commands in mcspring. Also, head over [here](https://github.com/kylepls/mcspring/wiki/The-plugin.yml-and-Extending-JavaPlugin) to read about what to do now that you don't have a main class or plugin.yml. diff --git a/src/orchid/resources/wiki/subcommands-api.md b/src/orchid/resources/wiki/subcommands-api.md new file mode 100644 index 0000000..41fcbcc --- /dev/null +++ b/src/orchid/resources/wiki/subcommands-api.md @@ -0,0 +1,191 @@ +## Getting Setup +Writing sub-commands is #1 on my list of things that are tedious and annoying. How about we change that? + +mcspring has an optional dependency called `mcspring-subcommands`. If it is not already; you can add it to your project with the following dependency. **MAKE SURE TO REPLACE THE VERSION WITH YOUR MCSPRING VERSION** + +```xml + + in.kyle.mcspring + mcspring-subcommands + REPLACE WITH X.X.X + +``` + +Now we're ready to begin. + +## The Basics + +Sub-commands are defined in two parts. Firstly, there is the structure of the command. This defines how the command is read in and performs validation on the input. Secondly, there is the executor. This takes in the parsed values and does something on the server. If it's not clear, don't worry, just keep reading and the examples will do the talking. + +For this example, we will make a plot command to manage a plot plugin. This will look something like the following: +* `/plot ` +* `/plot create` +* `/plot add ` +* `/plot tp ` + +### The root method +Firstly, you will start with an `@Command` annotated method. This will have a return type of `void` and a parameter of type `PluginCommand`. + +```java +@Command("plot") +void plot(PluginCommand command) { +} +``` + +Now we want to define our first three sub-commands. These are the `` parts. A sub-command can be thought of as its own command in mcspring. We will then string up these individual commands to compose the full command. + +The `PluginCommand#on(String, Consumer)` method allows the creation of these sub-commands. For a consumer, I suggest you define a new method in the same class to keep things organized. + +```java +@Command("plot") +void plot(PluginCommand command) { + command.on("create", this::plotCreate); + command.on("add", this::plotAdd); + command.on("tp", this::plotTp); +} + +void plotCreate(PluginCommand command) { +} + +void plotAdd(PluginCommand command) { +} + +void plotTp(PluginCommand command) { +} +``` + +Now we treat `plotCreate` as its own command and start over. There is no additional parsing to be done so we can now create that plot for the user. If permissions checking, database lookups, etc.. have to be done; it should be in the execute method that we will soon define. + +The `PluginCommand#then(...)` method allows you to inject all parsed parameters into another method an run it. This is where your "work" should be done. + +```java +@Command("plot") +void plot(PluginCommand command) { + command.on("create", this::plotCreate); + command.on("add", this::plotAdd); + command.on("tp", this::plotTp); +} + +void plotCreate(PluginCommand command) { + command.then(this::plotCreateExecutor); +} + +void plotAdd(PluginCommand command) { + command.then(this::plotAddExecutor); +} + +void plotTp(PluginCommand command) { + command.then(this::plotTpExecutor); +} + +void plotCreateExecutor(Player sender) { + // create the plot for the sender +} + +void plotAddExecutor(Player sender) { + // add the player to the plot +} + +void plotTpExecutor(Player sender) { + // tp the player to the plot +} +``` + +Now we need to parse out the actual information in the commands. The `with...` methods allow you to parse individual arguments of the command and inject them into the executor method. These `with` calls should be put before the `execute` call. The `with...` parameters are injected into the method before anything else **IN ORDER**. + +```java +@Command("plot") +void plot(PluginCommand command) { + command.on("create", this::plotCreate); + command.on("add", this::plotAdd); + command.on("tp", this::plotTp); +} + +void plotCreate(PluginCommand command) { + command.then(this::plotCreateExecutor); +} + +void plotAdd(PluginCommand command) { + command.withPlayer("Argument was not a player"); + command.then(this::plotAddExecutor); +} + +void plotTp(PluginCommand command) { + command.withInt("Argument was not an integer"); // parse x + command.withInt("Argument was not an integer"); // parse y + command.then(this::plotTpExecutor); +} + +void plotCreateExecutor(Player sender) { + // create the plot for the sender +} + +void plotAddExecutor(Player target, Player sender) { + // add the player to the plot +} +``` + +### Some final touches +We actually are already done. Just a few more things to make it better. + +Here are a few issues: +* In the event a player runs `/plot asdflksjadf` nothing will happen. +* In the event a player runs `/plot` nothing will happen. +* In the event a player runs `/plot add` nothing will happen. +* In the event a player runs `/plot tp` nothing will happen. + +We need to add fallbacks for these cases. + +For an invalid sub-command being passed; we use `onInvalid`. The `Function` argument is called with the players sub-command if no valid sub-command was yet called. This should be called after all the `on` and `with` methods. + +For no sub-command being passed; we use `otherwise`. As the name notes, this runs if nothing yet has happened. It is the ultimate fallback. This should be called **LAST**. + +```java +@Command("plot") +void plot(PluginCommand command) { + command.on("create", this::plotCreate); + command.on("add", this::plotAdd); + command.on("tp", this::plotTp); + command.onInvalid(s -> String.format("%s is not a valid sub-command", s)); + command.otherwise("Usage: /plot "); +} + +void plotCreate(PluginCommand command) { + command.then(this::plotCreateExecutor); +} + +void plotAdd(PluginCommand command) { + command.withPlayer("Argument was not a player"); + command.then(this::plotAddExecutor); + command.otherwise("Usage: /plot add "); +} + +void plotTp(PluginCommand command) { + command.withInt("Argument was not an integer"); // parse x + command.withInt("Argument was not an integer"); // parse y + command.then(this::plotTpExecutor); + command.otherwise("Usage: /plot tp "); +} + +void plotCreateExecutor(Player sender) { + // create the plot for the sender +} + +void plotAddExecutor(Player target, Player sender) { + // add the player to the plot +} + +void plotTpExecutor(Player sender, int x, int y) { + // tp the player to the plot +} +``` + +## But Wait... There's More. + +Good job on making it this far. You've surely read a lot already so I'll make this last part short. + +Tab completion is automatically calculated for every command using the sub-command API. This is done by calling the command method with a dummy object that records the valid options for a command and then sending that to the player. That is why it is **SUPER IMPORTANT** that you **NEVER** put expensive command logic or anything other than `PluginCommand` calls inside the parsing methods. + +--- + +Holy smokes, you read all of that? Well if you have not yet read enough head over [here](https://github.com/kylepls/mcspring/wiki/The-Sub-Commands-API) to learn how to create sub-commands with tab completions. diff --git a/src/orchid/resources/wiki/summary.md b/src/orchid/resources/wiki/summary.md index 54e757a..e6e3eb6 100644 --- a/src/orchid/resources/wiki/summary.md +++ b/src/orchid/resources/wiki/summary.md @@ -1,6 +1,5 @@ -- [Installation](installation.md) -- [Basic Configuration](configuration.md) -- [Features](features.md) - - [Feature One](features/one.md) - - [Feature Two](features/two.md) -- [Extending](extending.md) +- [Setup](setup.md) +- [The plugin.yml and the main class](intro.md) +- Commands + - [Basic Commands API](commands-api.md) + - [Sub-commands API](subcommands-api.md) From 324d784ef02a1aa27c28a9668f71ccfb3902caf5 Mon Sep 17 00:00:00 2001 From: kylepls Date: Sat, 13 Jun 2020 23:09:18 -0600 Subject: [PATCH 24/54] Start plugin load testing --- .gitignore | 2 +- buildSrc/build.gradle.kts | 21 +- .../kotlin/in/kyle/mcspring/BuildPlugin.kt | 139 +++++++++++ .../in/kyle/mcspring/McSpringExtension.kt | 26 +++ .../in/kyle/mcspring/tasks/BuildPluginYml.kt | 66 ++++++ .../in/kyle/mcspring/tasks/DownloadJar.kt | 32 +++ .../in/kyle/mcspring/tasks/SetupSpigot.kt | 35 +++ .../src/main/kotlin/mcspring-build.gradle.kts | 5 +- buildSrc/src/main/resources/README.md | 14 ++ .../src/main/resources}/bukkit.yml | 2 +- .../src/main/resources}/eula.txt | 0 .../src/main/resources}/server.properties | 0 .../src/main/resources}/spigot.yml | 12 - gradle/wrapper/gradle-wrapper.jar | Bin 55190 -> 58910 bytes gradle/wrapper/gradle-wrapper.properties | 5 +- gradlew | 53 +++-- gradlew.bat | 22 +- mcspring-api/mcspring-base/README.md | 3 + mcspring-api/mcspring-base/build.gradle.kts | 21 +- .../mcspring/javaplugin/SpringJavaPlugin.java | 52 +++++ .../kotlin/in/kyle/mcspring/SpringLoader.kt | 80 +++++++ .../kotlin/in/kyle/mcspring/SpringPlugin.kt | 67 ------ .../in/kyle/mcspring/SpringSpigotSupport.kt | 2 +- .../kyle/mcspring/annotation/PluginDepend.kt | 2 +- .../mcspring/annotation/SoftPluginDepend.kt | 8 + .../kyle/mcspring/annotation/SpringPlugin.kt | 16 ++ .../execution/SimpleMethodInjection.kt | 56 ----- .../registration/BukkitCommandRegistration.kt | 27 --- .../command/registration/CommandController.kt | 19 -- .../registration/CommandExecutorFactory.kt | 49 ---- .../registration/CommandRegistration.kt | 8 - .../command/registration/CommandScanner.kt | 28 --- .../command/resolution/ParameterResolver.kt | 5 - .../SimpleSpringParameterResolver.kt | 18 -- .../mcspring/event/EventHandlerSupport.kt | 11 +- .../in/kyle/mcspring/event/EventService.kt | 1 + .../in/kyle/mcspring/util/SpringScanner.kt | 4 +- .../main/resources/META-INF/spring.factories | 1 - .../kotlin/in/kyle/mcspring/chat/Message.kt | 8 +- .../mcspring-commands-dsl/build.gradle.kts | 3 +- .../mcspring/commands/dsl/CommandParsers.kt | 14 +- .../mcspring/commands/dsl/CommandUtils.kt | 32 ++- .../commands/dsl/mcspring}/Command.kt | 2 +- .../dsl/mcspring/CommandDslRegistration.kt | 21 +- .../commands/dsl/util}/CommandMapWrapper.kt | 2 +- mcspring-api/mcspring-common/README.md | 3 - mcspring-api/mcspring-common/build.gradle.kts | 3 - .../main/kotlin/in/kyle/mcspring/common/.keep | 0 .../mcspring-jar-loader/build.gradle.kts | 3 - .../boot/loader/mcspring/McSpringLoader.kt | 18 -- mcspring-api/mcspring-test/build.gradle.kts | 9 - .../in/kyle/mcspring/test/MCSpringTest.kt | 15 -- .../kyle/mcspring/test/SpringSpigotSupport.kt | 13 -- .../test/command/TestCommandExecutor.kt | 39 ---- .../test/command/TestCommandRegistration.kt | 50 ---- .../mcspring-archetype/build.gradle.kts | 3 - mcspring-build/mcspring-archetype/pom.xml | 76 ------ .../META-INF/maven/archetype-metadata.xml | 33 --- .../resources/archetype-resources/pom.xml | 34 --- .../archetype-resources/spigot/README.md | 16 -- .../src/main/java/ExampleCommand.java | 30 --- .../mcspring-maven-plugin/README.md | 14 -- .../mcspring-maven-plugin/build.gradle.kts | 11 - mcspring-build/mcspring-maven-plugin/pom.xml | 87 ------- .../autogenerator/GenerateFilesMojo.java | 118 ---------- .../PluginMainClassGenerator.java | 52 ----- .../autogenerator/PluginYamlAttributes.java | 66 ------ .../ProjectDependencyResolver.java | 49 ---- .../scan/JarFileDependencyScanner.java | 59 ----- .../scan/PluginDependAnnotationScanner.java | 11 - .../scan/ProjectDependencyScanner.java | 46 ---- .../scan/ProjectSourceAnnotationScanner.java | 69 ------ .../util/ClassLoadingUtility.java | 30 --- .../autogenerator/util/FileUtility.java | 23 -- .../util/MainClassUtilities.java | 21 -- .../src/main/resources/MainTemplate.java | 35 --- .../mcspring/autogenerator/TestGenerator.java | 19 -- .../basic-test/basic-test-plugin-config.xml | 10 - .../mcspring-plugin-layout/build.gradle.kts | 4 - mcspring-build/mcspring-plugin-layout/pom.xml | 48 ---- .../kyle/mcspring/layout/McSpringLayout.java | 43 ---- .../layout/McSpringLayoutFactory.java | 18 -- .../mcspring-plugin-manager/build.gradle.kts | 16 +- .../mcspring/manager/McSpringPluginManager.kt | 8 + .../mcspring/manager/commands/CommandAbout.kt | 2 +- .../manager/commands/CommandClassLoader.kt | 2 +- .../manager/commands/CommandGamemode.kt | 2 +- .../mcspring/manager/commands/CommandHeal.kt | 5 +- .../mcspring/manager/commands/CommandOp.kt | 2 +- .../manager/commands/CommandPlugin.kt | 2 +- .../manager/commands/CommandReload.kt | 2 +- .../mcspring/manager/commands/CommandSpeed.kt | 5 +- mcspring-build/mcspring-starter/pom.xml | 216 ------------------ settings.gradle.kts | 20 +- 94 files changed, 661 insertions(+), 1793 deletions(-) create mode 100644 buildSrc/src/main/kotlin/in/kyle/mcspring/BuildPlugin.kt create mode 100644 buildSrc/src/main/kotlin/in/kyle/mcspring/McSpringExtension.kt create mode 100644 buildSrc/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt create mode 100644 buildSrc/src/main/kotlin/in/kyle/mcspring/tasks/DownloadJar.kt create mode 100644 buildSrc/src/main/kotlin/in/kyle/mcspring/tasks/SetupSpigot.kt create mode 100644 buildSrc/src/main/resources/README.md rename {mcspring-build/mcspring-archetype/src/main/resources/archetype-resources/spigot => buildSrc/src/main/resources}/bukkit.yml (94%) rename {mcspring-build/mcspring-archetype/src/main/resources/archetype-resources/spigot => buildSrc/src/main/resources}/eula.txt (100%) rename {mcspring-build/mcspring-archetype/src/main/resources/archetype-resources/spigot => buildSrc/src/main/resources}/server.properties (100%) rename {mcspring-build/mcspring-archetype/src/main/resources/archetype-resources/spigot => buildSrc/src/main/resources}/spigot.yml (81%) create mode 100644 mcspring-api/mcspring-base/README.md create mode 100644 mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/javaplugin/SpringJavaPlugin.java create mode 100644 mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/SpringLoader.kt delete mode 100644 mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/SpringPlugin.kt create mode 100644 mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/annotation/SoftPluginDepend.kt create mode 100644 mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/annotation/SpringPlugin.kt delete mode 100644 mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/execution/SimpleMethodInjection.kt delete mode 100644 mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/registration/BukkitCommandRegistration.kt delete mode 100644 mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/registration/CommandController.kt delete mode 100644 mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/registration/CommandExecutorFactory.kt delete mode 100644 mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/registration/CommandRegistration.kt delete mode 100644 mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/registration/CommandScanner.kt delete mode 100644 mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/resolution/ParameterResolver.kt delete mode 100644 mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/resolution/SimpleSpringParameterResolver.kt delete mode 100644 mcspring-api/mcspring-base/src/main/resources/META-INF/spring.factories rename mcspring-api/{mcspring-base/src/main/kotlin/in/kyle/mcspring/command => mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/mcspring}/Command.kt (90%) rename mcspring-api/{mcspring-common/src/main/kotlin/in/kyle/mcspring/common/commands => mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/util}/CommandMapWrapper.kt (92%) delete mode 100644 mcspring-api/mcspring-common/README.md delete mode 100644 mcspring-api/mcspring-common/build.gradle.kts delete mode 100644 mcspring-api/mcspring-common/src/main/kotlin/in/kyle/mcspring/common/.keep delete mode 100644 mcspring-api/mcspring-jar-loader/build.gradle.kts delete mode 100644 mcspring-api/mcspring-jar-loader/src/main/kotlin/org/springframework/boot/loader/mcspring/McSpringLoader.kt delete mode 100644 mcspring-api/mcspring-test/build.gradle.kts delete mode 100644 mcspring-api/mcspring-test/src/main/kotlin/in/kyle/mcspring/test/MCSpringTest.kt delete mode 100644 mcspring-api/mcspring-test/src/main/kotlin/in/kyle/mcspring/test/SpringSpigotSupport.kt delete mode 100644 mcspring-api/mcspring-test/src/main/kotlin/in/kyle/mcspring/test/command/TestCommandExecutor.kt delete mode 100644 mcspring-api/mcspring-test/src/main/kotlin/in/kyle/mcspring/test/command/TestCommandRegistration.kt delete mode 100644 mcspring-build/mcspring-archetype/build.gradle.kts delete mode 100644 mcspring-build/mcspring-archetype/pom.xml delete mode 100644 mcspring-build/mcspring-archetype/src/main/resources/META-INF/maven/archetype-metadata.xml delete mode 100644 mcspring-build/mcspring-archetype/src/main/resources/archetype-resources/pom.xml delete mode 100644 mcspring-build/mcspring-archetype/src/main/resources/archetype-resources/spigot/README.md delete mode 100644 mcspring-build/mcspring-archetype/src/main/resources/archetype-resources/src/main/java/ExampleCommand.java delete mode 100644 mcspring-build/mcspring-maven-plugin/README.md delete mode 100644 mcspring-build/mcspring-maven-plugin/build.gradle.kts delete mode 100644 mcspring-build/mcspring-maven-plugin/pom.xml delete mode 100644 mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/GenerateFilesMojo.java delete mode 100644 mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginMainClassGenerator.java delete mode 100644 mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginYamlAttributes.java delete mode 100644 mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/ProjectDependencyResolver.java delete mode 100644 mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/JarFileDependencyScanner.java delete mode 100644 mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/PluginDependAnnotationScanner.java delete mode 100644 mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/ProjectDependencyScanner.java delete mode 100644 mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/ProjectSourceAnnotationScanner.java delete mode 100644 mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/util/ClassLoadingUtility.java delete mode 100644 mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/util/FileUtility.java delete mode 100644 mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/util/MainClassUtilities.java delete mode 100644 mcspring-build/mcspring-maven-plugin/src/main/resources/MainTemplate.java delete mode 100644 mcspring-build/mcspring-maven-plugin/src/test/java/in/kyle/mcspring/autogenerator/TestGenerator.java delete mode 100644 mcspring-build/mcspring-maven-plugin/src/test/resources/unit/basic-test/basic-test-plugin-config.xml delete mode 100644 mcspring-build/mcspring-plugin-layout/build.gradle.kts delete mode 100644 mcspring-build/mcspring-plugin-layout/pom.xml delete mode 100644 mcspring-build/mcspring-plugin-layout/src/main/kotlin/in/kyle/mcspring/layout/McSpringLayout.java delete mode 100644 mcspring-build/mcspring-plugin-layout/src/main/kotlin/in/kyle/mcspring/layout/McSpringLayoutFactory.java create mode 100644 mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/McSpringPluginManager.kt delete mode 100644 mcspring-build/mcspring-starter/pom.xml diff --git a/.gitignore b/.gitignore index 9c421ec..d6ed412 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ *.iml dependency-reduced-pom.xml **/target/** -**/build/** +#**/build/** .gradle pom.xml.versionsBackup **/spigot/** diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index e15e90d..229dbd7 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -2,6 +2,10 @@ plugins { `kotlin-dsl` } +configure { + experimentalWarning.set(false) +} + repositories { jcenter() mavenCentral() @@ -10,11 +14,26 @@ repositories { dependencies { implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72") implementation("io.spring.gradle:dependency-management-plugin:1.0.9.RELEASE") - implementation("org.springframework.boot:spring-boot-gradle-plugin:2.3.0.RELEASE") implementation("org.jetbrains.dokka:dokka-gradle-plugin:0.10.1") implementation("gradle.plugin.com.eden:orchidPlugin:0.20.0") { repositories { maven("https://plugins.gradle.org/m2/") } } + + implementation("io.github.classgraph:classgraph:4.8.83") + implementation("org.yaml:snakeyaml:1.26") + implementation("com.github.jengelman.gradle.plugins:shadow:5.2.0") + implementation("org.springframework.boot:spring-boot-gradle-plugin:2.3.1.RELEASE") + + testImplementation(gradleTestKit()) +} + +gradlePlugin { + plugins { + register("mcspring-gradle-plugin") { + id = "in.kyle.mcspring" + implementationClass = "$id.BuildPlugin" + } + } } diff --git a/buildSrc/src/main/kotlin/in/kyle/mcspring/BuildPlugin.kt b/buildSrc/src/main/kotlin/in/kyle/mcspring/BuildPlugin.kt new file mode 100644 index 0000000..3c48254 --- /dev/null +++ b/buildSrc/src/main/kotlin/in/kyle/mcspring/BuildPlugin.kt @@ -0,0 +1,139 @@ +package `in`.kyle.mcspring + +import `in`.kyle.mcspring.tasks.BuildPluginYml +import `in`.kyle.mcspring.tasks.DownloadJar +import `in`.kyle.mcspring.tasks.SetupSpigot +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.plugins.JavaPluginConvention +import org.gradle.api.tasks.Copy +import org.gradle.api.tasks.SourceSet +import org.gradle.api.tasks.bundling.ZipEntryCompression +import org.gradle.kotlin.dsl.create +import org.gradle.kotlin.dsl.findByType +import org.gradle.kotlin.dsl.register +import org.springframework.boot.gradle.dsl.SpringBootExtension +import org.springframework.boot.gradle.tasks.bundling.BootJar +import java.io.File +import java.util.concurrent.Callable + +class BuildPlugin : Plugin { + + override fun apply(project: Project) { + project.extensions.create("mcspring", project) + registerBuildPluginYml(project) + registerBuildPluginJar(project) + registerDownloadJar(project) + registerSetupSpigot(project) + registerCopyToJarToSpigot(project) + registerBuildServer(project) + } + + private fun registerBuildServer(project: Project) { + project.tasks.register("buildServer") { + group = "mcspring" + description = "Performs a complete sever assembly (this is what you're looking for)" + + dependsOn( + "downloadJar", + "setupSpigot", + "buildPluginYml", + "pluginJar", + "copyJarToSpigot" + ) + } + } + + private fun registerCopyToJarToSpigot(project: Project) { + val props = project.extensions.findByType()!! + project.tasks.create("copyJarToSpigot") { +// group = "mcspring" + description = "Copy the built plugin jar to the Spigot directory" + + from(project.buildDir.resolve("libs")) + val pluginsDir = File(props.spigotDirectory).resolve("plugins") + pluginsDir.mkdirs() + mustRunAfter("pluginJar") + into(pluginsDir) + } + } + + private fun registerSetupSpigot(project: Project) { + project.tasks.register("setupSpigot") { +// group = "mcspring" + description = "Adds default Spigot configuration settings" + } + } + + private fun registerDownloadJar(project: Project) { + project.tasks.register("downloadJar") { +// group = "mcspring" + description = "Download the Bukkit jar" + } + } + + private fun registerBuildPluginJar(project: Project) { + val convention = project.convention.getPlugin(JavaPluginConvention::class.java) + val mainSourceSet: SourceSet = convention.sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME) + + SpringBootExtension(project).buildInfo() + + val springJar = project.tasks.create("springJar") { + mainClassName = "ignored" + + doFirst { + project.buildDir.resolve("libs").listFiles()?.forEach { it.delete() } + } + classpath(Callable { mainSourceSet.runtimeClasspath }) + + archiveFileName.set("spring.jar") + dependsOn(project.tasks.named("classes")) + } + + val builtSpringJar = project.buildDir.resolve("libs").resolve("spring.jar") + project.tasks.create("pluginJar") { + description = "Generates a plugin jar" +// group = "mcspring" + + entryCompression = ZipEntryCompression.STORED + from(builtSpringJar) + archiveClassifier.set("") + + from(Callable { + mainSourceSet.runtimeClasspath.filter { + "mcspring-base" in it.name + }.map { + project.zipTree(it).matching { + include { + it.isDirectory || "javaplugin" in it.path + } + } + } + }) + + relocate("BOOT-INF/classes/", "") + doFirst { + val props = project.extensions.findByType()!! + relocate("in.kyle.mcspring.javaplugin", props.pluginMainPackage) + } + + includeEmptyDirs = false + dependsOn(springJar) + + mustRunAfter("buildPluginYml") + doLast { + builtSpringJar.delete() + } + } + + project.tasks.named("jar") { onlyIf { false } } + } + + private fun registerBuildPluginYml(project: Project) { + project.tasks.register("buildPluginYml") { + description = "Generates a and plugin.yml" +// group = "mcspring" + } + } +} diff --git a/buildSrc/src/main/kotlin/in/kyle/mcspring/McSpringExtension.kt b/buildSrc/src/main/kotlin/in/kyle/mcspring/McSpringExtension.kt new file mode 100644 index 0000000..91a7889 --- /dev/null +++ b/buildSrc/src/main/kotlin/in/kyle/mcspring/McSpringExtension.kt @@ -0,0 +1,26 @@ +package `in`.kyle.mcspring + +import `in`.kyle.mcspring.tasks.BuildPluginYml +import org.gradle.api.Project + +open class McSpringExtension(project: Project) { + + var spigotVersion: String = "1.15.2" + var spigotDownloadUrl: String = "https://papermc.io/api/v1/paper/$spigotVersion/latest/download" + var spigotDirectory: String = project.projectDir.resolve("spigot").absolutePath + + var pluginMainPackage: String = "${project.group}.${project.name}" + var pluginName: String = project.name + var pluginVersion: String = project.version.toString().takeIf { it != "unspecified" } ?: "0.0.1" + var pluginDescription: String? = project.description + var pluginLoad: BuildPluginYml.Load? = null + var pluginAuthor: String? = null + var pluginAuthors: List? = null + var pluginWebsite: String? = null + var pluginDatabase: Boolean? = null + var pluginPrefix: String? = null + var pluginLoadBefore: List? = null + + val pluginMainClassName: String + get() = "$pluginMainPackage.SpringPlugin" +} diff --git a/buildSrc/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt b/buildSrc/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt new file mode 100644 index 0000000..2bb47cc --- /dev/null +++ b/buildSrc/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt @@ -0,0 +1,66 @@ +package `in`.kyle.mcspring.tasks + +import `in`.kyle.mcspring.McSpringExtension +import io.github.classgraph.ClassGraph +import org.apache.log4j.Logger +import org.gradle.api.DefaultTask +import org.gradle.api.tasks.TaskAction +import org.gradle.kotlin.dsl.findByType +import org.gradle.kotlin.dsl.get +import org.yaml.snakeyaml.Yaml +import java.io.File +import java.net.URLClassLoader + +open class BuildPluginYml : DefaultTask() { + + private val logger = Logger.getLogger(BuildPluginYml::class.java) + private val attributes = mutableMapOf() + + @TaskAction + fun buildYml() { + val props = project.extensions.findByType() + requireNotNull(props) {"mcspring not defined in build.gradle"} + + with(props) { + attributes["name"] = pluginName + attributes["version"] = pluginVersion + attributes["main"] = "$pluginMainPackage.SpringJavaPlugin" + + pluginDescription?.apply { attributes["description"] = this } + pluginLoad?.apply { attributes["load"] = this } + pluginAuthor?.apply { attributes["author"] = this } + pluginAuthors?.apply { attributes["authors"] = this } + pluginWebsite?.apply { attributes["website"] = this } + pluginDatabase?.apply { attributes["database"] = this } + pluginPrefix?.apply { attributes["prefix"] = this } + pluginLoadBefore?.apply { attributes["loadbefore"] = this } + + // TODO commands + // TODO permissions + addDependencies(project.configurations["runtime"].files) + + val outputFile = project.buildDir.resolve("resources").resolve("main") + outputFile.mkdirs() + val resolve = outputFile.resolve("plugin.yml") + logger.info("Building to $resolve") + resolve.writeText(Yaml().dump(attributes)) + } + } + + private fun addDependencies(files: Set) { + val classLoader = URLClassLoader(files.map { it.toURI().toURL() }.toTypedArray()) + val scan = ClassGraph().verbose().overrideClassLoaders(classLoader).enableAnnotationInfo().scan() + + fun getPluginDependencies(annotation: String) = + scan.allClasses.filter { it.isStandardClass && it.hasAnnotation(annotation) } + .map { it.getAnnotationInfo(annotation).parameterValues } + .flatMap { (it["plugins"].value as Array).toList() } + + attributes["softdepend"] = getPluginDependencies("in.kyle.mcspring.annotation.SoftPluginDepend") + attributes["depend"] = getPluginDependencies("in.kyle.mcspring.annotation.PluginDepend") + } + + enum class Load { + STARTUP, POSTWORLD + } +} diff --git a/buildSrc/src/main/kotlin/in/kyle/mcspring/tasks/DownloadJar.kt b/buildSrc/src/main/kotlin/in/kyle/mcspring/tasks/DownloadJar.kt new file mode 100644 index 0000000..746101b --- /dev/null +++ b/buildSrc/src/main/kotlin/in/kyle/mcspring/tasks/DownloadJar.kt @@ -0,0 +1,32 @@ +package `in`.kyle.mcspring.tasks + +import `in`.kyle.mcspring.McSpringExtension +import org.gradle.api.DefaultTask +import org.gradle.api.tasks.TaskAction +import org.gradle.kotlin.dsl.findByType +import java.io.File +import java.net.URI + +open class DownloadJar : DefaultTask() { + + @TaskAction + fun download() { + val props = project.extensions.findByType() + requireNotNull(props) { "mcspring not defined in build.gradle" } + + val target = File(props.spigotDirectory).resolve("spigot.jar") + target.parentFile.mkdirs() + if (target.exists()) { + logger.info("Jar already exists, skipping download") + return + } + + target.delete() + + target.outputStream().use { os -> + URI(props.spigotDownloadUrl).toURL().openStream().use { + it.copyTo(os) + } + } + } +} diff --git a/buildSrc/src/main/kotlin/in/kyle/mcspring/tasks/SetupSpigot.kt b/buildSrc/src/main/kotlin/in/kyle/mcspring/tasks/SetupSpigot.kt new file mode 100644 index 0000000..db2ac2d --- /dev/null +++ b/buildSrc/src/main/kotlin/in/kyle/mcspring/tasks/SetupSpigot.kt @@ -0,0 +1,35 @@ +package `in`.kyle.mcspring.tasks + +import org.gradle.api.DefaultTask +import org.gradle.api.tasks.TaskAction +import java.io.File + +open class SetupSpigot : DefaultTask() { + + @TaskAction + fun setup() { + val directory = project.projectDir.resolve("spigot") + require(directory.exists()) {"$directory does not yet exist, run downloadJar first"} + + copyResource("/bukkit.yml", directory) + copyResource("/eula.txt", directory) + copyResource("/server.properties", directory) + copyResource("/spigot.yml", directory) + copyResource("/README.md", directory) + } + + private fun copyResource(path: String, directory: File) { + val target = directory.resolve(path.substring(1)) + if (target.exists()) { + logger.info("Target file $target already exists, skipping...") + return + } + + target.parentFile.mkdirs() + target.outputStream().use { os -> + javaClass.getResourceAsStream(path).use { + it.copyTo(os) + } + } + } +} diff --git a/buildSrc/src/main/kotlin/mcspring-build.gradle.kts b/buildSrc/src/main/kotlin/mcspring-build.gradle.kts index 92c887a..6aafc56 100644 --- a/buildSrc/src/main/kotlin/mcspring-build.gradle.kts +++ b/buildSrc/src/main/kotlin/mcspring-build.gradle.kts @@ -2,13 +2,13 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { id("io.spring.dependency-management") -// id("org.springframework.boot") kotlin("jvm") } repositories { jcenter() mavenCentral() + mavenLocal() } dependencyManagement { @@ -26,7 +26,10 @@ dependencyManagement { } dependencies { + compileOnly("org.spigotmc:spigot-api") + implementation(kotlin("stdlib")) + testImplementation("org.assertj:assertj-core:3.11.1") testImplementation("org.amshove.kluent:kluent:1.61") testImplementation("org.mockito:mockito-core:2.+") diff --git a/buildSrc/src/main/resources/README.md b/buildSrc/src/main/resources/README.md new file mode 100644 index 0000000..f7dafec --- /dev/null +++ b/buildSrc/src/main/resources/README.md @@ -0,0 +1,14 @@ +mcspring server +--- + +1. Run the `buildServer` gradle task to generate the required files. +This will recompile the plugin jar and copy it over for you. +2. Create a new JAR run configuration in Intellij +3. Select the downloaded `spigot.jar` in the `spigot` folder as the target +4. Change the run environment to the `spigot` folder +5. Add the following **VM flag** `-DIReallyKnowWhatIAmDoingISwear` +6. Add the following **Program Argument** `--nogui` + +![](https://i.imgur.com/waoHVTz.png) + +7. To update the plugin on the server, run the `buildServer` task and restart the server. diff --git a/mcspring-build/mcspring-archetype/src/main/resources/archetype-resources/spigot/bukkit.yml b/buildSrc/src/main/resources/bukkit.yml similarity index 94% rename from mcspring-build/mcspring-archetype/src/main/resources/archetype-resources/spigot/bukkit.yml rename to buildSrc/src/main/resources/bukkit.yml index fb6a72b..ca883f2 100644 --- a/mcspring-build/mcspring-archetype/src/main/resources/archetype-resources/spigot/bukkit.yml +++ b/buildSrc/src/main/resources/bukkit.yml @@ -1,6 +1,6 @@ settings: allow-end: false - warn-on-overload: true + warn-on-overload: false permissions-file: permissions.yml update-folder: update plugin-profiling: false diff --git a/mcspring-build/mcspring-archetype/src/main/resources/archetype-resources/spigot/eula.txt b/buildSrc/src/main/resources/eula.txt similarity index 100% rename from mcspring-build/mcspring-archetype/src/main/resources/archetype-resources/spigot/eula.txt rename to buildSrc/src/main/resources/eula.txt diff --git a/mcspring-build/mcspring-archetype/src/main/resources/archetype-resources/spigot/server.properties b/buildSrc/src/main/resources/server.properties similarity index 100% rename from mcspring-build/mcspring-archetype/src/main/resources/archetype-resources/spigot/server.properties rename to buildSrc/src/main/resources/server.properties diff --git a/mcspring-build/mcspring-archetype/src/main/resources/archetype-resources/spigot/spigot.yml b/buildSrc/src/main/resources/spigot.yml similarity index 81% rename from mcspring-build/mcspring-archetype/src/main/resources/archetype-resources/spigot/spigot.yml rename to buildSrc/src/main/resources/spigot.yml index af31f36..a1b9af3 100644 --- a/mcspring-build/mcspring-archetype/src/main/resources/archetype-resources/spigot/spigot.yml +++ b/buildSrc/src/main/resources/spigot.yml @@ -1,15 +1,3 @@ -# This is the main configuration file for Spigot. -# As you can see, there's tons to configure. Some options may impact gameplay, so use -# with caution, and make sure you know what each option does before configuring. -# For a reference for any variable inside this file, check out the Spigot wiki at -# http://www.spigotmc.org/wiki/spigot-configuration/ -# -# If you need help with the configuration or have any questions related to Spigot, -# join us at the IRC or drop by our forums and leave a post. -# -# IRC: #spigot @ irc.spi.gt ( http://www.spigotmc.org/pages/irc/ ) -# Forums: http://www.spigotmc.org/ - config-version: 11 settings: debug: false diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 87b738cbd051603d91cc39de6cb000dd98fe6b02..62d4c053550b91381bbd28b1afc82d634bf73a8a 100644 GIT binary patch delta 25879 zcmZ6yV|1o%(4`%AI<{@w>e#l;JGPUnW7|o`wvCQ$b!^+{pyyfd%$je__xJpJ?p3vG z*HLM8khxQk_4MF~sg#SQ9pGSKxR79A%*hGlSjmdu7ytz;lbM}~t81+4q{E^xhTvkm zJ8Nmkql9;gu#pVNwx25kS|d@Ii9yvpa8>@2G;;!1>Hziw1D}heVHfh1W~c!j_Pc=_ zt1GeV%yP%e%&gH40Ol3d1NP9~6ww8Kz1JJBK#c6i0gaUlD%x@l)UEPo+9f=xKx@af zE!vI=2Lvr<&6&+gQBNYLW=fVV1QdK6^5Fw-Q&}g7tQu)B21lJ8-c@!cIMn8;>7~dm zim<(};YN4WZ5G`Z_$=D;+-QWUBJp?wYc%~xg(jp==H+rKW+*0#&!h~jTFyMzZ)n(; zHI2`VXi{-_tJS}x(7t!Rtx{Er!`bOm9hRFn02k=V=H4PJ6)wR7Pj@UsFfdgRw{jgwY8c$`@l&Dq@4^a#8Jwtc}q zQ?9bLET(&u0?7xD{9Q6Krrmv0YfU>(F9zKIetdqi{w#Wxkpx3$GaQ75#TLs*VWBj` zyP3EZNo657(Ue3;H`gEWWjjs)Fh{#%P_WGP`!$@qiA5EaYeSt324K)s*~r>S%?P+D z{Z=Cl{U}7>O;lDVqKin7m`r3w0>DYa3PUWuSxj(rTDzd+#jngB1 z#c1pzY-ns9Hl;3*wVkv`1k;!-PuPxNPOl96#-4hnxi==NGj?S#t?Tv!yJuakt&Ia$ zkGzvE6G%N%)}5K|92917%%|UoJ@2eN z5(O^p$frnrnW$^fOwJrMEl+RT7qb*jAJWclHAWHjTACrKO_vr~I+6`D)bO=MJH*Qk z&<5ka?J&u-V}0OWGw4ABVcNkzr_&K~Jr{`LDLlEU9qdbTyQQnv)CS9BdZS%Ze+w4X zw40*2xo|aPozTZQWLm(smGjAU>L|BFWf~o~;u`^ll`mE9l4Jhfak# zM&DR{%bKX1lOUN$8?DI{mV9HdU^vAC!ifP*9#_o8dASc@6#*TfFFhJUoCqI0 zm|=@Ia-+wnGmBQr%Za|Q%+a44FYDDt#o8AOJ}STvn@vlWi*EZ~6S4t^kOGBv-*m!D zSptU73&&6`{i+>^I8jlQAH)xL9c2bqg|>p>7o>!_Ip{yZ)Z#gDQL0X8I{`_HR=CUV zr&vR$8>G6SssRlc?aDzL?b)6e`z)}BX&e{!!u9qW6YAICaYU;lfFJ5X@9YHz-Q4{w zy~GT++l>e!z9k==qo-qe{80yh?pw7Hk1jzulDZUfX$A|{jY?FxIm;t=Pv@GB^T(0f z%aU5vfj`X{rxX%*#2cNh%SN&<_%xE$^AgoV@6slg_{jm)Lq(68%Ujh4A886bYByWK zgb9o3W2)ioYRAUTvJ>k_9=v<5-*&FCXq%~Zs{_iPf(wT$MR2zk*XHXC4chzA*L(<_ ze_B_KyOvtl?8usZ?q<7-Q*ssWB`%oDvpuUfx=R>s1aFgC%M;7Xy;+$NVDwh>-z{P9 zHP#6%UXlTi*a9_XZiZH*U@uV}zqYLDOkEiUfx8^p)=MdZd}J@M{aF~QG@_ikMZK)t znB2fU#CoH%Mno~UZw96quzZ+GU@{1c$tF-m6cQ7cP!xY*uH%0Uw62N$%lAa@)nC2& z2O0Rwk>mY)KcY0?K$b$TDmCL-4%ZQ-XiB5uW}grEPW}77QS?RUQ}}foM>k*=Xid_c zTujbug*wu~sbZFCdm8^7FYXU+uKmm04sf9Ozs9=Xu$P=Rs+ zpGOhs&OSU)_>~{hqal|%$}iK+S}0zB1^btBD0e?BBq1b1+(WS=19!VLPyF$!VtL=H z@a@#w54~a2$?N9Za|OA^V~yEQqR)#{{I#L4&2Re}1R1!W_y$GspTC9io(TQNh&oF! zK@q4p$~-yh6*S8>3BqNt&#Vx1qH)QaB%RLG->hf_Tu#2hB@vf`A*17f3qU@ zN;J0A@HH_0ZP{_bPkvLCrb%i2*~$qeTdCPxTw@<-ZB3J^j8emwe#m9lb-8Z|`HBB` zSIy{ep^~DJ-&xG}<;-UVE53l9)0mml*WJ-q)&pSI>zdNf4UUH}dA!4Ow&Ua@;Ka<4 z=;Pr|5Nvjr5#704IUeg*f~j&;jLg0{K&Y6>6N`uS>a8pze>g7l`M(=QKx4=c66&&jomNQ0F`!d$Vt>MnT?iUU=iUY zxeLXMVJw$6>^4D*tX9E$0~5RQp=luRun7s2^W+R1cr$mXI#>sHiE_k_;h_rm7y@m`+PloWq@{?IuC#-24qRxO zy#owpl$oflq_f*9>ujsi~k=T9EtdlJFvB*P-k zTwQRvCnoZgbG@*%g*4ROqHaE!R#ECV-nW22AHPURKM83gbfQzB>|;Jw?7TCUUcb}> zjaS?^YZ`C+Jz@?U=d$wm8`E>~kC12FDRnJdFKE8;>^2lii*nw3)EE%x3xl=eG3k!! zoGhW5WB8l|A!%pM;vb`lztoz)lC)4hvd}(Mfa;~{Q05%hD&jsZ1!kAXV*+QfN6)!F z@s+#voi=>M*=Hr75};i4D>k!Y<{EQpRNl%#Sn0IQDI_Bl{jV<%2`}}dMus@Q$14+_ zXoe!CTjjnz2jY1i3EyIEUH;t{A@mPns2W{7rTnS!72Jk8i~hvIY%8P=9$84z6spEacB34%6a3icLCOE(n_{TX3= zYV$RI4tF-7}lsO9HaPD$;pG4>gU74QLuo8^%wmA zYuYYS-x+a}Oy zfd7dwQ+inrWN9PI-()7M&%a8O2g@w9_n{A(t6gX|Sp4F46NS}w;)Qb!1HGb)G4JUK1apBNLfmV?!$Pj@&nEr_v7ro~qX)aBwS806-};8#b(6K+ge)*$EP{XL0Bq>(|JdU!F(J*m+&v5-Q#TRb6!R3|CDFDV4dbZ=NR9DO4Zea+eQ zEh0w_l*FZQLC{K0iAs|xlhPcy&@Saue3saHmPiE3&aL&OH2K115dIlrU1szK+r;j; z-TiV*_;0G#@N=9VFrq3{_y}OG$=`WU&FD1<+`frE<}ZAM4E>suBl=6u`fAqyWRUtb zBnQX>rE@Rc#q)W-XUwO}J&&Nj^z(j3w;wImUn6q-XO5VE`9lMCONJj`40Xl-?UtmU z5f48iOFSsieKw^V76U4$M#B#~;tw~4-U=N5_Da%E-I+fD>d$oRuPz9me^Mp?o)+Ry zN8vujOF+!a?5WAGRg{(H$8600VFq+vJ^C|5(#8_X7@-=W^xe&wy)}*4O0^iVr7o#n zn(#85aDN8u3`y|vtg)gj6RokLOBFffFbpf}jgBk~&-d}{Lei`JveV=4;8Gp6yh|p) zMS38Kjx-Dbyu)R>y1RuxUNsXJ7>3N!PjrsT9U=vCaNIr}k=Ys!sBF5Jy=r^0bdIWT zOsU*e-4aJN10(jL?;fe1<(s80T@(Boy?RFmbiXASx~2BE;5=DC{o(`ld+!5BIaVvBcGVc-}GB&U>a&*)>A>sIigYfM3gov1X3hUtsBW94X zhCTr7Kefnz1=eo)%CfO|?;o#btd8D0EaKS`Ha%KzS__(;jTf7=cudM@R~m0tDdET) zK87LEW3r>mw~X_CSu;_dXkG`K&(3I*S>BkQm*a zlZSDZ#}6Uy0qljN;qLTQBS`c1@uMh*m=8cE0{XN!wi7!Sb+eBA9^{xumMRsk6PJ6^jYJrf9sG2%m*^*C+yY73yB%<7v5cY-14(3nh@4 zi^CFW-H`HBNnD`#a#iFhQdK1_68cr!B1aHl2XP*u)txlrEIgZ z6S9$)A^5UznPhBIS!&H-E*G{4kU|CY0w4_m#} zbs6jl!g42C+R26tCNd@8Eh+MW-hih4JbH%y4wiB<^%Q?D{iQdFyZK!J-k&Oi__cKias&*`FCrlq{Ho$@2_;IXS z)JmowF7kHE1%gzpkP%Eke8G8(E9^d3TZ2z`cq1)WPTEdA8O3L$xgOsrkw*tfKKfiv z_7-lvJ~1MysECc<3CyE^N7hlVYs%4hYb+9wW{OOCJW~^#pHM%uD#dq2jb%nain?q_1`lY~j7k zxtFHBHBSjjh3*=LzH?lkh~XhSNgs$1Eu2C=R!gRzMU4#l;aF$V&m+NdNH*6e1Y(4O zY4T7W?$Bq|k4U|iOB&@@;rS;UhGK{AO|#kI?Tn5({g^T98X0{H0Y+Y;B~MUe$q};p zi{Rox0yC_!N^o65{beY-8GWquInA-<1J1G3ICpv(;hDXDYzK!kpzput)dGV;83!&y z21-X{0))+?!AFW&suf&C4;IOg=y$*%R8X6m8psKwCv|la77xCESnjPtt@Ol0FoX$J0JS<&U#CZ0DP)f(s==f?)!+-9s`b(CVHY!$CG}6pJOqJ zkUWt2^~NrHh9K!aE=ZXzSzPdffR=N!^gniHEv2&+*27g{5gi7xYKA60;?K+{!4j2j1;y43>c4CKq zf$I>&HQmY|yxb-$4q(?BBpa#ZK(C3g<2y(xkuK5ssT`EOf?gf1og5!LV)OQ_qF-I) zu+#dCN`L0(0`|Q8sE)Xil!c7GZhCFAl!ydBZmR|Qu_;LVX0lcSqr*aD_%t&dDL=B{ zenqYwJPa|W!BNQbC&*GKQ!R-@=I20Sd_zKb>AUH*J8R`Bp`XAXwPtPD5phgByjgzb z1&2b5(^KT%t>z_%akFh4jKqyHE!Azb3dHUqYh>E<2Hw`yFV@xb1MDjezBZKSmS4RC zhm7^GcPIK zoX5%2&UXK85bEk1z%|_woYvQ(g@PM0w9Jkt`xr22ZY`SF|VfN0_=N9klhSeSapPRQ=4SpblV=7PH z8^}nAs}kN7OLP|dSrYUWz;0pX`w8Y$Ru=82JkOuPHN;9<=~q`0uQ2xRcCSErqc3~= zuT@_AN(B7uZmHUDtm902MLW`utUNRzdFa{B9z&!EV9I_lg`keqcOI(o4;DVg_&$1U zSb0s`aD4iUGgS4syOgnKncx?y+%GOAQXqg|mgnH~1C#l7>wta}nS_yHOZLAzRcFf$oa)YVKXJMr*5Bf+U!~f(!O`QknN?M)T1^6Ky zz`&0<4!_z%AB3|6v|C@IGaH@0551@R{Wur6g9|Og?#4c4ZH_nN5I;H`X(}v81hTWJ zBkQDDZ<1mxto39{_|IS;@fu?u9emU6vF0^-9Pw*P zt`jX&kO ztNTf<>XT1bx5J9M(p|+4j9oMHpUsCyp(H=VW<@1dx|llZOKHV8aVrHkNm@INE#q}M zx=7m~KNV@Y6AY4e)DU~pbCWx5PQ87IHG7HuxoJk{{|PH*UT}CHdTwxC14pg*BesW= za|2wn7>saA^ZP6qe-svy41ZIL$!%;R#s<7lyzSjv*hlSL>0N8Fxn=M482j6YIe6yM ze_kU%uTNkvEn+WoX~bNwp4Y5u>NM0$7+q_zH`8^u6keK{>lZEK>OPR_e(>sE=%p7t zsi_k`+h-Jq;k&0s(=(lJ0CX3XhfS5WHmID6>H_~z7g&yZZa*k>sX!yGm0`>Y>)&+^ zj@)gYm79#*8EeOsC*c=oO(u?Pr{{9M?z4;kPO>*Qyiq-IT3Ii`H~&^Zg)6gm^-5}e zZMC${UOn;HL3(6cI@Y49_>|OB0Fg$a?ALWTL+93&-ptvK@d&IT146$?CXd*&VTenf ztY7*MM9jCro!rTld=uh(mO}K`#XrZA#3ByC2#ZnHMKJ!dY$=7)ioPt+NOBR1WD-e) zZiZL$27cdGA}&JxE5K7qBU`tCV;8abVja|r9PtWHc^3W%Rvk5e;fA&{^+{*?DE!jOx2(u@D`6RpD-lHYdH8~&56 z2aH+672QASN9ZL6=lzB{FQSD8T#d4InP{HoDLY(G`J5bF4^TbF3T6IcrPq=J3H*{V)X+$*~`LducwSs*p_|=^v}=RlM30CrS&pDHm~|A30SZN6QiguLWSC zWQLRK78R(InN28yI_9~9&>KgwMK(6vk~JZCfpq@2pa*+B#zukwXiG27I#=6Gu~*8wVr2LzcAbXB&%}S%%B!(b-ZU{Bu%Uth zz$3UQCp2+uQ&dsCRA~?f-QdY{BRw3JjCT?V; zK%?};d6bBq1mB0fj8}yYZlTKOw!Vt-5QBtc>{3&HyKEN9K&o{#i@dW%QWNSE`tRFL&QpsC1(e`Fh()IU~-zv zmdOgxudIKV2~(?t(0+@yB!!i6A`yllbNYGIEDht?X6hK9#3QIkIb6IRxrokBsH)hA zNz%j9^K$;UErvEaF4R@Fmt8ohmK+nD$KnHXyqs>%#p&DL}%6)iM1o^5j$&TpiCqy%=-MMs& z_S0)jxsBJU-q~!@o7^N1+8*BOV!j->!x`R;;(vG*!3A>DdNoCbdPPS;dsPLsRcJGS zUynk<5j`GU(G7ToM|tiW++S?Dc*PTJUyMo!F;$cgy&ryj()|E_B3F(XYY%@XQoUo5 z#PpNyx4^t`mKZi@KOYT03X6ZeQ-LN<&Ld8b&QrdV@vDT(8?nL=AmbIHCzbgk_`w{` zT>eVdTf9H{8WmJmxf)joJ9WOgv48~QBt`|2T;F-)twHIt%@8<8mV>L&oFs9AKedAUx+!s`|QL_eqfllLLO0tkRvPQ zoZ~P)`*bckizz`y#MM*_XBXU^@i=dwh`pzLrDtLHD_;W4r8*rDQR-&QbcCnL9RDx| z+N6_Pa@j`@Bi3pn>ri1|Wh5qHfDf}NbTLY9N0I(F5PWE7)LC&5+1hZT!wRl3$8KHS zqLj1QP${8HJ80{57o6x%aG`a%7T$yDj<%wTv4&sb9Gs9YBrfK)#a}L}O4Pe0+8V9L z4QG#>Ehqxmw#2Dd9O%tWWDZCO(iSH^5~TYKE=CGz3jr$;rS{1m(cQ1e7%nnxZF#YH zwY+e>GrOmspTiC)?X>zh2?LoiHRrM}sfNl!+sXtp>(!!fY^9-cs~^smB}uqh(wm~k(*eJd;l=h!?Geq|x_h|O%Yj}UuD!71yS1$IPOUE{b(XR@@RwBU;HDVVR z!+vfk%~0w%o5)H%s+KcJQgz>{W=zh_$6OtkUQOvhc}V04t_OKGhx1Ro=R&DL^u!fo z@{&E8(?4_KWuk-JcZ`33zbdnZQ@P&a!Zj+ouBDa1w)-XM)<*Gp( zl`R0QgIL<100BRuP#-eq$9EykTXe+u{j#aTJF%sTvTwNJh(d$2m>;5p%y;yNtO62) zD2gDSz%shmmav`OHV9XNsq>^G9-@;y59}G`qtu0h-}H~WIR6~Bi0ouaVxXEvlKg(B z4-!M23ud%6YL?h37XQw0sucl-@}QbO1PTF}>lw_0BgKI)oqi*Z4@_Y3))RLV*v$$9 z_VFeMbEe>J6NNSU(}2AlmmM>>ucAHY53K?c(jg3q6wmti)@k5;u>?WJiS4vXz(VRtLHbNUx(;qz$Hi-h0`R*t7Ct zr!Tdunwc}xc=Fww*WB44Q|7?V zxTcic=3nrLuEq&;VukXrciO*}!}lUV!tfMrL#d_(6(rl7gnq+*%2+V=0(KT{qAij= zUc>qfq1~(Ly=$>jl%#%|@@URIiW!0qXa{Q6a%~MM|0KFbpJLdOy%4BJ;G<6@+1 zHSv`m4|$d@47sT4ws{e3WoR8A%XcV^?Fm{RyYWG+>_k zJ8oa1WiwKtSmUlxHqSN50bEFApY?)a#nI-OO4nrg*H&a$4QikDGKF6dy%{>+Ncf$n zsgk@| zJr5p4?{HjL7{unRoP0t%aPspiMyBbNkR zm8x9EJnp~eido?wGGPg6N>o7`Q@y>v?#0CA)8sbnY4fpDSCAM3Q&nEqs4s}Z!@!dnz7~4usdDz=?TmQh% zbGtk2LD2gFbo-Fa4Y@(iIoyDsT}Bj`ha)rXKBM=SC=5!+x~b6+(GTHIlS2(#fwZo5h~5s9bEKks7jmk971XBAi5rRZ&r$EmzpOaE7 zT8hVKe9w&(<9t)EK(jw+z8135K*8?2pI&V=zoi2C-|20 zKOfweSOM8o=HBYQmbKgE|Xi`;USy14T# z>-mB?ajU0yTM3bkZOkz)-qVl{o~tnC304#ON8VZV&>*w@(i=MJsvE4J^unA+HTcSp zNbNA&ae(p4Wv-a#Xe1ZJWGh0hc|Ae|AhT!#?HkNTrO5;CJBom5v&xPtrDG#TKVQshUxZY+YdCjWjLHcq ztCig!)W|=fV*@J@TDPLl91eNU#`4uF1%sH*Mz|Wgm0#4V19_)foIRPGT;?!!zp;j! zz7|eL>EvOm2}WIs*A#L%`Y{oIQ_neQ@MC;f1};XqHeTy(_rctu^p2>IHl! z2EOkFlln@7qb_TkXsaya%q4T5g;CY# zAJqGZ$DV42oZv%GWKxxu{o>lf(oV@<`)j;D&)D>bt29G?I`Yw@4Smr<9;;NEXcZ(Mn=tW zHvP^g=3_G~8+cUG+?-7-#feb5QJme`B(YnYwe}r8N7*Kg|ALa(rgXW*7y6?{Gp4(@ zUW~h?*3DG8>7_iFAbbJ|Y0_|c#XHyF@qrdP#bc4;E-XtH#L;7umRxOJD)uge21ryC zmbUV=M_nhjAc5vhVDsE8l;ta;boiYeqh(8g&A-b@j&MT&;iWH&q=gz%P=)^H|a zmx)&F;{AFnKn?2IoThkbc;qAC1G=(;#Vwd-3eMH$;U41b-{ak0;gtBU@Dq^=Df=}- zYx&crI_f$jJk$#KTTyO4h@@hX0MAO9bcum7ieE{$U^@I3KC%O#z)9B`H}X!`+j=aE ztp}gS_6j~w_L6;$OoA1dg%pSVM%J0Q?s#N%ZHS;td}?b^)=9CxT~s z_LVoFU{@fZVpq#czW?AAXbIY>|Jor_%m6JSOq+NN5PNSn#wX@e-}1x$*1CoHQ0Y&8 zO%4(|U>4Vh$OtLdq_-ccRG6$#%x=3Fw~@`a87)-gfDP&7QkJIa$2QNls(@I7kZ~> z%|$gi{pB)Vqe@L2$wIU698twO9>Q^9aFp3y&r-Gc-5!vBQPD~;r|(+f0R5C=bt2m6 zdSN`lwh-4U=N>i&u-b4`mYf#q%4li$&1fm_u^uaDHrZtB*bmy-!1KOw@NR2N>G7qN zct@LMhF|8I(MlCnZI-Qhd_gxvD<03iad0MDJ=v^ud(ULSlQxuxAc?9bS$*5{cBU;> z{u#{5M1wJ9LAj)~qlLDk(bKBf=B%&>S+Gllvu6V=kx}|fnJ+x`KIJ-N?%tHJ2NJ3~{Xm?(;9~&8DC;cL7 zeLb3@Gpv3PiT+jJtEwE|nZ*<@OblC6PxnZ%$=-46`UTEi4<75vYMk%(#-u^mTJ&}#8QxtbLJDo5+h3yrVcKl1M#_bRFo(mjcRQxR74he7f2xkN8{H+`8L z{vM_JV!9&y*bgXrx>s5#<=>PWi@{@8X9|bUj|?upKHT%%h4BIWw|@H$W%FPYBr1S` z!6%Nm!644WIo{}Q75w|?ZXG<#2ue9T%qU7V{(m7Hppv#{X%jqzTs_3Nq&bf#BMj3C z<~fvgD$=h6u}&`7tA}bVXmPI-^7n#BT<6=vjOaD7R0p>jTZU1`&nyJ2xW%q?O@CZ{ zuBfLcT=lrZOe`p7AN*)4CvU4kD6Dugaf9WGMQzbfOx%#j08k!v^ZgI$FZA1YuEkBE zKnsIOK#EnM3U;zg-uf>5Q%A)VLd#NyzK$$t1fiCy5TUYX+E&|d75FxLM|$~EY_O5u z;PyNN8nMyF0 z&lK#lA|ZqNTu(oVCJ`HQqCA{jbbDVtt$II5crGm z$GwywbO`cEn0bXQV{8>web|0O>uk<1zG(MPflgBbZh6s&U^3FAN7frpR(I}N=YQcU z!OI~xoFQc`rv=FG{v7xe-J`HOSWw2ZEXuZVbQ-U((0eDxYunt%k zfOGU}7*YeF!9IycG}Loase?(8)MmWkK7r3L`6~5Hk&!?k_#MV`G_e1({imLRJaLfx z-*gl1XOti%RmGacdU2Ledou;+P;YPbQfEpJ0k3tT`--? z!No!}=01@m7b3*kLog5|M&Zk|1G!Q;(7b@~2JtNOUFJ048~pK(vn6KFHDk9`uLa==54XF!E*Cm8x4tPN>Ji$+8vX*uGsOCy6lws?n>4u{w zZNy=F*ObPE$&#tft>D{6+%?a_U&Q}6SNb2Gd=(*y!VluVyMb?DVBr57C`V@c4#+sT zx|!J7iCJ5i+5T_*BsIlA30(vuYzNe4(77eb7(i0!C#OfJ2bYC1j3X06CBe+1aO!{J=1}!T?$|yjUN}D_$NZr(!rmmOCyU6TFgvxD^TKz)4dHT)W3S6^R`w0`hCD^b;?lvWCF3y2AZlG znaGhV)G9P3)DUlZz7_9X-Msp8Y52m!UdhYxEw@Ua)9zZc{bWp_8z}!XBYzRF(MoHw zI+lCmlyQDXt+)jew8*##MzF3FwbTno(jX`e+Iz*^DQo!uNo#3BlWdtH<$Q<)%7z8$_@eiR0z*1pA&YS0MG3UigI>c)!&`XVqq23ET}iu zxCPltMoQ(dpBAa253rk%Squm-3}IZKYg=h|7Luc*w=)ikg}MFN$nGIGX~UuV7q617 z-jC3u0_$@}HYN?jF)7W@K}AFDnU5~J#>ep=%%n8rsq=;ZI4jGk$jWq65366+fm=O* zILu<69Q@sz>nDqQ97=oS*f+5OCJPn3(ee-JfHw@?UZ<$jbO4D;jIyt^paGX+l|gdt zm*@->oBTdfz9rhsk~|lF0kPm){gf--QIGl;ee=#-i_%;nwHxXBfqTQKEgjTO5rB|* z$eu>j&!K_nr8yRzc(p2HGq7XGHsX0yxDavk48E z3sZ3Sc0bgzoDxoMH%SP0n~F2boShR&Z~Y34@||UV$_2Y&r-Sr~iqG^>i@Q2heXu6< zQo9)!yqpsT2;lC~4DpKj45{)aOLR!5C+^&0W|3~`Vyi5?+!KK9N!#LwUto2qCmkXI zFn^%hH6_o*|GJB66Bm;i67N17c8DB=k<=W<;^h$+YiJ|o#4gw+mCC%@rB&!bd?=o+ zRXR6~Y(R7i_VE{+StZ!K87wqIoq2DU+&RSVFxFv1o%sbQC2-w`;u&ESPY?K$m(|);+zFn=L8Q|nm*q{+*C`81Tz9;C$h0Ezj2;c9$LCJ zaK-TlRn&5+gKZM`NR6&`M}eLh-xBY-ksEl`nCxQj-zWjX2a_DV0@7)7h6BVYu*7H< zLIlO!Lx=_o-ThkDAE>c)<_q}yG8+++7oAhBg6HfGy6%7qc_v4)e1ZGZg>5UhLzk0* z*h6a#V#`Jwr=3HmliYMGqCE6}WOX{8h6?$%&a$0eqSuK{DXcr}9(J22&U^~)uD|@3 zbz#$DbA}}cRrO4z;-qzamWE*3{Fc40Myw4AJLFYlt`~<%GyAj$xt(@8QMyJaVpr8S zvJu^)0dJsoBZRSoBZFh>(muL0DN%0vDY0B zk-wq!hVR+dO-_zB=nNj$OSk6Eq~es^w^XBBvC-W?K!}1tvzK%WH<38x-#TmQBD>v` z|C35+B=*-h+XAn(gDI&wQ?tXSe$jiNfG}|;#R6DO6!&wlICzvUl(i$PwVoujq7$^x zoGT}s)Jt4R@pEh|lt(9gc3!Mq?`;962<@PlHW*1dqR&!eE&V7j?`5)5KB$`8;_1u= zt;y5~I8HHCSC3CzkZEd`o8-8+yw*y$`3!&shCJ)cpeCLS+9r>4nyjh{f8Nx*t?BsM%yXtJv=U}aCqusJ( z@Vf;FA%X`kD-rLmQ({x)$*Ja7)?se&Tl+bXE(u{XN@Ei%HD$=+99m;r1+!xuLfK>M z=3k5O{B;iESrvMgn#lV;Y_nFBXN2Zx~V5*^0cR#Y3+(rpK3 zj=n=dt;vL6IPsdCk}0@BNEe8aI0)SC=6#RuRzXw(Hv3tLrga82ccbCU^5J!8^t3Yu zhHRP*$#ijJE2eRayHG{binuD!sm|l#6E^G`!ZF;wUDI3}c3R|t<-s!Lcpjr_wZQog z5qN3pDOTG43bZ=|dfyMTHoe?i zuxq?eJSN5A7s>p)%v$HP3-CPLwwm5*^prr>7C4=1LrIF7e#kWf(*7iqMu4}>h^?qT z>U(39OUCl38Y_GDx}YqBV;AjvbSrl&H+kp<-5MY5 zpmhg}uIt|zmy2?QI!x*lcVNLTd9Ov)OCuMl&R{TkHf{VyAauMe8}#6lj#SqutEuC^ zE+XlW91A=KEN$s5K|$ z_7}xpyDR{2?hLON2|iDAnJ&5Z&lz2(ZW%o&Z-o2wgLb>_q3lgE77!vQhH>kSbX<9B z@?*CdYZHDqHF@UEaop1@5UmZ1csWMoOr`O2>x~tP&z~inhBbBiS?k9Sh@3Frr@MWz z8I?VVe8IWI`{EYfX}N#l0R|^xe&I7ayNz|O~rqI2=kB0cI=zB=!_w{Sh_Pp!ZhQCTK^djiY zwA-g^u#5Cgy55L#;+=laK>EQfLQ`Xq^?0DkQ4BP+Q3uXQy&1;dEP5yW9&Wxn#EZT_ zkjt=iNA8+}IC+1|P|Pdaa1Z!Ny!5ugLhE^z&`7;d>f*~Y5A1PqZkqIUT)9Ra4Buh> z+(@Oed<^Z1k(+j^0*wr|qF$ttk!+$jbYWqQE}!*9gGbk8FQryqp9s87=@ zy{)DD9u$_A7bviu;mVi@Ry%l;Y=0@BFFp)>R8DU02KfM^ZK6QPGw8@jV_S;cI5U%5 zNOf@P%6!RuepkBN7WZK^8gBvmbml*A;Srop-s&f#0M6cf2v|U+uf-^a%UX91YYs4^ z1Xom6{C4-fq%k#bMk!jN+f*sCG0%KVQqzg=pZb{PZ+FzPyIHq@FrfCKDTzON1;QN;ZgzgcQ{aAYY^MRiOO#T*=IoCx!d$juMnE_Cwz7qeXG~8x1DxCbGc>2`Y z6mV@u=ZY^sDLpFP+azuHXEa0zET0iSD&_neF@k_o`t^~C^~;nnq>-1Mp+6?cdHjrc zg#CwB?SxMk?4p5n37`icdm&#!|WU?_1J!RC?mj&(qVw}ojDqfJ#k2dwy# z31pp6mfgSUAfXK5ne{AvMV+1!*Nc_O6-9GWXd)YVu`K#(?qSt=>sA}My^e1i)M~XlEhWXV7w=G?*>JBlgTUTL>E!cbnV{hw)krX~3S7#Bo zX(oE2;G z?;7K!Ejlbdsj-`*DCvqFei|cD2E~zKQDRgPO+6RhY35Y`Jv#xVj(=M5Y)a9Xk^%N_ zZ`gCxac4@V)|6<3ldpksMb4UWaSn@Yw40*R!;$E<3-b?W7f{P~N7Mui)Mz;>AX7WJ z)LLIQk0hSse(x*Bz=}JtgnK7W&;nXYw6uww42_HmE7sN>ns#5hy-HXy6SrF;Tt!f~ zJ2t;B`~}5%kL8~AeiTyeIdZG~H%-n560IfnoX&O{MCv^ino&HakhwAVD+{qJOBMlr zA(G}n&_&P5M{6=iqf}Y7Y*h^oSenw%I}Q1R;qsJpN}wb6T#yyVo9QUm`O_*3kd>>Q zadPpnUei8ga`JV3mMZ@hxJ;MLN#&EUf;bLja=L)@|IDETTHYMTp&NIKY>844B6Ju| zhRVA1aNdjBsir4xrqoaUEyoSkLxDdyu&m4ZBDl)4gP1pFI)R%f-Z;<)T*AW=IgjtF z=(PTz{_&*_s;`6Dh?a4+W==&*j2C?b?~KV{8r355Pk=j|0l$HE0WZkaTKMtQ9aTH> zfG}q$MlA8p>Sut2WGHZwia6KaC<51t5i?sR^-Wqx6n$bMK0#B{Ipo74Rmytbq-9`q zUXfa8{lCM;Qd&1G^9-pbpaRG7;Y>Fsg;(GV{ZWSOiHfbk2fgBcDSPZlW(T60@p{gOlduL`RxKzLE$TaZTYT@Cb5(U0~}(eSR`_ z!1WJv2(x6*Ke0M<&Cil!Kf~&NJJXzcHa)h_2tH^pLf(n(1(STnmgko(PzdJeDe?Ph z$>FW(^+3JY2C>{L>bPCQ(ZqImQI<*H9lEU-_N9w@?+sUQSRE|(yYjMZ4MwK!c~Wld zpRk0`|0?V&!{S()wqbF1TbAJN5D3AYpuyc8g1d#qgWCqT;O-jSCAdQfPH;`|An%^M z=bY!s_v8I~eoR&OU3JaQZ1+snbl;Pub3#_&GztSAFTM8GyhVGYJgZ%?Q4z)nZ4Z4a zBT6ZuGY(+9?UV@J;?P*S6{f(+0rRap0=N zCEd7T^>jivn1Os97<$JyzF2aqn9k9g^!O4SAGV9_buanE%rB~jD%XCa zW)oGMcgK)KR`^4JQ#EkRl{t(_2Fv@jFw`4995p>ob^b=I!5TjSouyN4grYO`?e>&E zypIt|%njv66(V+Z?mV#&7U3$ouYL%2tL8yD&DhrpiJp)1D=JI8So&;vz6Zif$Aa2m zf9!n2d%{6?3muX+ax>F5*`3!)CNZ&!B1%7vJ8&$$Vfgth?M>HbUef8JH?Xbf)aa|BCckR-ny$_JJfOAw4KNqkJKrg$yNvq-V`>=-G_f@~?Jbhr zmM}=YU(`KC*(#fB$(U^g$KBXH+{k1Oe!Rt;Y0k~Tnqxi?@Pb^br8G*A2^FXow9B^a zmNuW2)d*K4GCZT9yYf7|5XyA-%Z}WP32s1D(PFi+xq{UV9!jUP?r2cW^tKDdiw^Bv zv5AG>!lg%^d@brD^`Sh){YSL6+;9^%0R;hh8MlOo)ldQ5vKNz)pD^#0dD z7VCWAhf;uT=`9gi7;h+YO}TZO&sqS@R<{$8qLeRrg%I+u)wrymU}AN7;+OMFVyZwPRl1r?*>w* z)ri%7lxW#=g5W2{iUp~9QFoFBeL1}Y8)e_xwYy8TdlEla4C|cSoP0+S!C&Y?<=s!p zgm74;HPT{e=#S!o9oQv4U^cXCA5{3T8V8DPvxj*b_44VTK>N9P<$bO$imL?tCadIYUk&RAD}OT9)Y+wB;il4 zsn7|2kFPDFOEEt*0QnUQQq;#HhhfEivgvSlM$VWIG~#-(6VEi0!Q~u!;WB%4lV5cR zmN54|GKy-B=>jA%G&}fZPefIA{B?Iv?%R>Pju{eV$6S~nwJB`f z6OLm1QFd48z4WRIr+sVri62Q7i=M@*{K*FIkigM5Z}~qwrL2iP1B$`H zi`cZkz+WzIj^?6KQ9NUowtpc&w0SNX0d~al2`Fg*2g2et8$p?) z$gMm+!OAQkZ!l!=J4Vk@CrbpX3JEZ1&oAH7G-6D+j?DA{j?U>CXW15^YT$0k1y1pM z$-ZS%#rc{79y60h$D*gxJ`ap5Z=KrYxnYT%HfDUzkecw(O9*|gZkobRJ`t-)_6glQ z;_FTcojhWViFdY>p0+Gl6nc`+#fMdWOoKmSMH-+mcS(FrxorWx=mt6 zypNsJjztLk z2Io@N4NA0_w_GflQi8%YakA^9;-mFrW)I^an5TzBH93LLc3nM$e>*9j%l~!NkMm6C zQfKx98m+nk4oh2F-D2~?tYJRWbIklVC8ooAtYWMy-?it1TeljO;XNZ?Wr!ot3x57u ze2;;l_D&>h3d7%k#z<{9bNbu;jb8nnO!X(6z}c3{vOKhhPFlV6J<4DyEI22jg3VTK zQ!vAW(F+t#Ch-^Z!P{~Y>Uf9(#YWEGt8KIk-zrQ`>Yg_uM*6Xaw_`}(*O?=s7$|>G z^rv}7PhQ{e{^ICiECMXEvc}YrZ4zsEhfdv(ZK2uvvezsi;?m2Ks$(i|X~@e_QPglz z^^RT}E2nRku5?VadS!}qnB#(Z`AMnsIh4uHWM+vmI&ra>Kb6`b4eQSrDh+#XUaz3k z5($_vFm%5Y)52qT;3UlhFzGyjkeNKtThK6X35C}~IaAbJwkaJ(F(dWk5?4fboq^*V zim~*JWVU2FXFZLFjl9jQ_)T(^&4IX9YZPGtesjTS^OGWL*O~x#vK=MS?Wdbl-`2Bl zt|^{-kH1b{tpl7;pA(%#76dp@6?--GA#a#2ly}95OwO+n!R*;MCUWsL69RkcF zi?rb_Ju4Z4b`iHNA;Dgnz4Yg@C^@E~DuP6V#k7KPlW&GM>seOuFJ5Phu$v&WpP%Q2 zvxncB5*sBt;TvIYGgmxlynxOVGO{`#r7}d_&S3Fpj&}TTL58wEaFg0!$aU1Xk6pw^JK^-DJ5B*om$x-&L2bWtqoyy6x?~yBxdx`eW;|_v^st@>K8sA$JnHYUI8{feATvP;9F-kPsNi}PIOw32!&o)Z+b@Q<;^)G9)W(rMg zwJY6~${z#1h_)1c1HoXaNNd|%YA+NNniP5q?fLOZIl z*XLWt$O^OWG$+A=kidPFW?UEb2B`kQ0UwstL>GvUJ;9jFU+zTFhY)ntuQbasj4`Vh z@qkq6cK}XoqBLf^o;dEn645g^bQ_zI8qlSWFx{8EX{?#~;iRS0$WmBR)RJ+x~RWE7Up&loUn_{H4`&_Epb@M#ik9|XynEAtSNJg9#2t z1fK%ul!kU(ad`L%)>c0pW-k?!4H%oIoDYLNg;&38Z2^W9HoAG1e6&HqxP9L0Q*C{N1MpRgQ7g_-!7JDVC{vNE z7I7Vd=Z~&F7Z(A=8)cT+uWVqd2U8Vrj`PRF`H(%0o8@VFUo^kby7@Yye(%{&(aU{TEC5q|5n7awCJeQ@of}nD9vAJeL}WYv@#xX6O&fr@1iX(m{Fg z{d9x+lWabjN7<_dOZpUX{O2s-N6&O+pA8BLRe>zl+2+HSQkIdI)ngb5-kZI6lv6`}n>ek;*3p8bR_xhZNY#RiXW`>% zvQW4a48MKB@ zTZUyA4^(>Jg`5e-K=x^xeHk~YKmIT?&$<&5r9__o`JAs!@hb~IPl{!F*^GwWY~aeW zeZn6U*LEx-?Sd?>L?dATfOxKBR0z@cdmn+U(sq-Zn}VaDj(Z}Z@lDJ549zvNC0j^3 zB7>4y$1PzUtBv+3yHf86+()~Z{sx$G0k8Ap+zZh?srg(SF zwa;8oM-~$)yNYU|#y?yT(VK)u!~**838i*(q1Fp_M-0~Pn>D8n_h)e69C7PNm2Vp@ zgzE+4;3SywUoKr7v`cb;THL5`tErsPHgAut%W0@&J?BT8skuC)NVjtNb1#m##WRPdNiOgUZ83%-G`IbWkNRhok0PkpRv#~)37X$Tc}=mZ-FMH# z_`r}o!__}&xZ$A8Swx|cL+37#L42*G;l2Z4;(_dpP~FPK#N5t;)zihq$;rZn)yc%g z#NNUUDmg&+uF(0Jj?ij18w*!Bs73v%RTvHi4S}VvJd`l83%aI80OFiO1F@I_LaMw` zz>GpQtjfyRXW<07tPKb_H38yDQTV80vghkf21nw{v@2462syo4-IBp_rVqszY0=IT z4uPh6O~km*nWJNwj-9UsxPASfo_-K?Q)tq>q@H;rp)QU_g=&X5i#ygjuk!+9lR>LIxOW9mO0LxpkNq7b6Lr%GfJnSsFNHM zWZFOCqd%|inrPCrxcuOVCZEY#8+B93*@EYzX=05NRG3xSU@8ci^-gS{y^78>f?#MI zM~OFo8g+6$EG#AFE>q#q@|=-+gSg&a#3)V0PK1i86T|0%uHKu=*O6v6imIjd0*oR7 zsj5t`Gbw6P)L7)hMX$X6>LDcusWH5yd$qIZLc{*<^Kh9>i7uNV+kva4VVEN5(43l{ ztqMJ^Gt*%iuQYK=RchI0eXR)xHZf&vH?Q6PJ!mG4|#`~dR2JW&qIk8+Bu+Cb-4dBfnEdK@kMQu4$c+(#e-F z7p@}P$ewy4jPISUgjwnEO*r4vGDjFf#f@Le>F}#dm|QMfw3?OOA!{U+)PV5-OnaH8 zT$dj`qSbvQMp8dm$xDGo=bDv5uA5Y=Uw?36vFC3jJQG)ZU#;0dK5^qq1%3<1#@q7j z&be9tDKE-@pY`&{%D>B6tD!7d-i>|gZGwkuLv{W#5&HWOW@6N{{8OP51MA{io&6=2 z!(~vr-3v0BBMP$RZQLrDg-Gx{<_K{WY#w3Ou9Q=oI-ew@X0xRyae?QkmXP)=AvVf1QC;O*&U)ajhi zRwczM9A>{r!G6H$ChI+*w1Hb?3-HeVvuKls7IVMIqM;ypn7nL8Q`jNtEn#y(979?(2;Sy9J+imlvfshi<1 zk|m|sKrEK^i*S9Hv1B8kXc$3C^bIl71wt@iQd=0yGTRA=>(={7l8VnY^%T5G=7mpu z{l>v`QE7)GYjg7R@pApIlVYLr$DcVy{%{;0K5!|SUBF|=bA{CmCqO7obfgJW`dWg@ zLFAF+s6%DS;5IsmLHAfv%5OU@wA;MGiJSrMvF*_eynLu2Pm=pA$7mnFFcTKX2hEiPr0Vr_({9Tp{GV$)ynG`Ltcfx&%I8rGe(c@7+USCc_|rR&Epp;?6WpM zV%xp6wOH*ePA;d3*kx6$bSP`0yjIMm+2BI2Jj7zv19zD$xfd@nyt3zFXjW8fUM#ci zsDgaWV;}a?lEAmo7EnGBSF&P3hfCIRQ(s{e^gtioxeYOy((~=ypQ6;$gWSm+Hrp1J zr6(y&*J9Ks9(`1}lQ^qUBn~Pwz?pMj%6GK-d@q^Ue2!_YXT4(Sm z0@^7-=0JFmN#8;-taytTELB@~pa5iOgSGzQ}x+9u4Mkr7@uGNKFZ-jkvh)e<`s;p@a5a zrghiJuToS>$7_z5?Jlgr|BYzhDprUA6Cu&Fp7TCJ{GQAA2-~BR08D{Yfv0!HGP^ld zGIX)U{WWW9QioS6BV|PgS3Z8uVJ{Z_u+F@{{G4}+whfC6WI(qEl-Uxjn((co&W?W6 z^3(%MAt5Kj2VEFq+c{(BZ1Q?^9SwdVWe}=FXwy=nuBOg)Q7Y9@i%2eH<0A)+&-ov` zICRmv{neM#aNQLE{`}lkB2PBNWI88+p)xu^g`?7LLm(+S=fcsh*rwNjHB&*8C_)+k zrpN>5A@CZuU1D3-d2n}nh!ES7=$vgN>>wI^$tl;gSyhsp7`+LX>8SgN273Rp+&5ls z1b;C5v9BAut@MXLKiPau8w1cz7eDWRZ3(%tF_a4LG*0eCqfq+ULUX^vk~oJs ziL91@Et%7fIE`TCjFgu%v1P^>z;6A7teW_Y@K@{%QJx0YTZiNVQ2mZoC#Iki5$SLs z&I$Aoj2RGkT>HQi56Z>Z!8=|s3oD<%B;_o*pXe4CB%t|Ds-i)vl8~7VeIy>UfZN@2 zCW!R3Ej;p#ij#yhk)UeDhR-*w=diuoS6+OF9o&!tdw|W|PRh;NH_w|ZTJXcqt7aJ8 zi_mDFZ?yTap7Jd+(u}+x@g%n`sM(zjaohK!TV=(;TtToXVbM}cEcpPv+mDgPH2^vm zX<(vCl<(ba^2V2vK__@>F4H9@Gt$%Tj9(VtqLGL%dXQh34qu~w3;N*gE!msz_eg9c zVi-tYW6%pxxc?Xe4X~*{X-ia@q;*u;r^=#C;+UVnBfs}ZFLLlKcsVpp7MRR7s?fRK z{O(iuoHa9ME^q8fTCZo58n!~`^vOTWkomL}ycL_4c7gF8kc-JIwvaWzAau|Z)B3)= ziBl@RjE%Xf@c!k>#u*H;3lh4E#uacD$oamK&inbt7Aig+?c_-EvIG5WaO(l8zFyAR zT3A{bZ=S`S#`k)>RH^U_*43}d#;bO*p_Xkz_nIl{ING`*W@+`1R^Q;=9Tvt)$Cf^B za4f?IRes;nG5ObD^he32P8!8Z86*t(xO2P@AVen=%rxy3CLcnG5d42V@98@qTNoN9 z+ZlWru)fb79!gKWWCgNm zko#~`SWHN^@++Mtph)PxB}z5pQm_hw!M=J8(L4P1SRULp?3RL(<~EF>T>T$EL%|A| zx;^bdP7B`T&L_1Kk}3$lE{Z~$@!MlO@!O+TQ871!5YAj_zP8RXE`9=z)_Kg~tS%If zKTrrOpHu2R;|OQ(xOeH}`}JZzWi{!3Zi0e)^rzIE#V>P7VC^d!guilc*aXu zkzY;}6d~9aFP4>H?3Go_&fAYS+JS+$18BOR9DtSMXP`MJ8w|AyB%Z^r6rxm`qqZVK ze2f$ToT*_hWB-_1YnmI9pib7@Px>x&3^XfxybppJrG!nMHK$1 zO|{lPA5!1D7yZtObt6k#Z8J-I5%;YBYqAgh!Yj!evKF?fQ`W>@ZJ}u}=df?>1~Jop z`NyeOsq;p_N8HV|YK~tF6ZdSqVPD3sG9TXYrwk0LXau?UTc1*HX&>sSNUQ~T5he18 zrgz2ZI%C^!Nw`JE38Iy~KhQz*sX<+YIFuiN#77X&4*cNw;p|^|eT9>Mll?NRUWPM= zHV)#-5venzygF$pyf2MZy`(0XSDyGNfp?jg#`=$kDz7h`vqsSdd?z;arIDsy}_uX}Sjte*Uq{@}fBN%Vt(a zPx*;SD01WJMi+RdR&Z}lvktVyw;Sn*jkm6*4(=A(l8Fw_sgZw(R=GgyNc8V>z7X)o z1#f6M_x{xS=vNo@qbXxcKXQBL^NTO)6O(v>SSvMf@0``*5zw$>vv%kg$LUqrHj}$l zTyY~k`MPk`t(y@yO}*S_IuBhDra%r-W0jDSzki=Q5`$rS*=nPA(|376r!BtNxyP z$~%$YcR%(SV7*tY4^#5Cv%Zjn{#e@i2cCrYHwcRj3-a&<8}#2xFQBv7|1AZ9)xv)R zkahvC=f#)N3lVFyu|neysP^+SO{QZ`dehzB!g6sF#(*deyitWk^no~zeOa7>i7%L zzZn~-C+a`E4Y@)BtZ4uG4|4pK!uh8Z1k$_x{}MWoqH!L8lN)4pTmX>c2EnxEhA2)j z0hm0YA_*kFmjg4Z@g_28^cvFFuwO!Z?Esu}b*w-t{HxcVlV-^jFO=0VY5F zRuyMO0qgm{#mW>SXbU!Ec@_xxR`|QBEDaaJG{*#3D*0PQhlI}Y1BS{WJ#&Tt(kck$ zyb9=#A?S}o)2M}-Q9(x}4nTDuM7Nm%g1W#2Xdi`&RFMBYk=PKo1t4H!0usMq322yt z81)PMbqzrOj!;mCeZs~yUoq(>26*7c0)Y{M z>?ri%O-pLEjZ$5|K$geV-1l&HwR(JHYT$hV2CefXa+{NDqmEceNJoFjZ5g^{E1N zGBi*`1qKieMr}*}=R$XGkVhsarcsn9eVtL@;O2Fd`$KXAH!)Eb>3``XO8Xrgf7v96$6;aT6D1k*(_qwm36Un!CPUyI_aeGD>LH|x$lSa zD}^V|b*EP__qhd{v;K9v@KAqxS39M8~Yx#lN5~5}bl)S-LICnt(@`tc~ zU_igROt$;{7q;aS8|rf{UL2;itGAs+z=J!o`fCTX*Q#=2olq}5-^hKv^@^IPSbTYd zkw$QoUejI0#_i1`g;uFGnXndPP=l&dN>hbgHY}&&Otlo1VZ|O)OgTsBGWL^gKWgBb zM}A)qKDN7R8s%uM$_i!0ivU0?zB5u<>Y%9Vi1ePB)ctUMBvV|eN)@tYzi2@^;llZBNMbmRTes18R5jW zA*66c2&}Ah9O;Wqh}(+NO@ufqB2PCa9*2Hj+a<@#O1hs$!~w(EZaiD0xirnfQgj0A zaZ3;@G%snjNFVxpAdK|S#vK{#4X3F1SutWl~ z7pky`>6pyG3Mmq!5Th#%hc8froaPAQobqOI`dCHk&*Q95G=z`S8WG6o7i*d6MHg}0 zqJ#H$PweKd#(?1b%~hPIDA7G+u>cEt7MW{r`+=f=NZzy)e!_@>5^{G5-2QS}@02f~ zeU3=|53~HyemZ@Iv;zIMeOkGw@gL~?y`};wNW+v$KS_oD;m$R}#t(x39gs#l zWT`5-Ve-e|lix?5<5Di^B6Ce;=7n{+5eB z$y7G~G#{fDnm(mCnS>XAt}+DRZ`0#TQ1h6TVqH$e@d!@i<)9O+P9h0PcXy0M_IC9N zBri&izj=D|bQ<}0tnfpZb3ad$)n?|U%b*ije(*}w%Flhl^)(F0+DjeNIA z{n>@II2@EjD+vXZ{`ncXHP&%C<}9tZSeY3S{UD1y#2az!>!bj97SU%z^P?^2DPDKG zqvh*se$b6!V@T9A*Bzs!Asv`|1Un<5;6b#MiKmo6(+#({Kla9hL---L=7xA@$8ga6 z{|4h|$1@CCHK2`w+xD&wk_FsuRK7gpFhA5jq(C_a}%OF;9$Q-`YOogo0`;3(zqc*fDx*1ns$_IJT;&8#=9Wo&o;dRR4 z>G876CSC(!!;c0H>vKk_hJ6#Fl0s+n{q_3Te=N|49+kdq!gfcIcKDT;ON>o~#+?Ey zSc^E)CEi|vOD17EiSWz%^qYq;Hb(iHq2O1P+KFF+41eT6t60FIs_=T=q;O%e-(sHw z2pyr#_;^8hbc$HRX`OuUXd*Int{_paI}eD4IvuC^M9y&O@p@jJh>wxsS14BHUA|;c z-ofVDNGyL8GcKn3NmVR94~D#t5rDpRPh4>odpF|J^M1fpKS*gWcoC%T?qv9zDghu0 z(qKPfKtQ0NKtMnQL0Fr4GJ6m}K>p%`fDrvJSoM|=fGkaDADv;%Z(#1KyJT^G_pz@yjy4z$tp)&bk%x#ZHQhsi&Lig}edtFLOSPH&~EU18Ga<))lU4Uw* z@o_AAjr`}CfWMP+?jfbZl)@E;h1N5FL#SI(mBhI|i~Uii{ZYp^bHnRKi{2aDpRBv3 zUrYPTK)^|_dBsXcUdFP@{dxH-JnK6r>ObrO``s3JAHJ|(eiUKbgV*43WKUu9ljX)-7CCGyu+>_3%Bee9R|w=Y+u>0SGP z7i}L=RzO|U60qMR40sX$Nc0ETZoQB)ZyEf6QsD7Vcn!pD%}a~b2lp3AnE7oE`Ioff zm-giYtLSTne+`ZAmpJQpMaH-CB)iIeh*fu4hCNl5U4~G@o2X3L@1i**eWdqWP34ad zsXw$vrP-3~lB!6$C@e)>Vb(_JO2!gZ#Ea=l_G3(7@>nP^V@{feV^U4$)WT}G^+QQO zEtNIAruo?0uMm?!DqPt%(M#Ms^7_#vZP~WTq{(6%#zQyd=8U;qwiPpp0wUQT6*Tb< z8&j=WQk+zRrPH!{*_?4tKvG z<9e3U@d^@LH|v7t6SHv~H$c5fuPBen#4lgsE{7I4#2-8p8Q*|@C2HltgMi1 z=A~G2)cv~f#&cR3_{*m;h(Q(9$BS85ii^{~tVbGhl{+aR7v0dq*0>kfW0K)y>j=Yp zF)0Qs#>P^t#nMzX4JH$ko^>LCoSN;a7I{zJc)?U-#=+iO=R|eTaVs>p+~J&^PjB_6 zj#{Q1=aoM+MQRz`hlH1pj;YLGI>K3^g#077)HBYSoW19!oKat<3GF@SQo0Nc(ZsRw z0Wq*OiJ<=48nISQg*?6{!BHTkO0AGC;~k(1?(8+c<+4^VkMA8ZkIo3t>uaW6N=?Y) z%o#~k5ZQ*?^c8^KSK&2e8TTRG|Tu%p4P9uzC> z0T+ig4$SM7L;Th%Ze(NUtLB0yl+pfCBU2t60K1}0zLJaWNG`2CMdI&-U$_CL&>t7X8J;J z3}G1zW9StkS}cGZZ6^r?Z6}eKI~}b)5b5@VG_v8M^*Gd&2ZMSKAmm@jfPPQ21e7%2mn-GH3JdJ^+k0Wni795Ms;D2a1WB(ZoS7>O}Y*v5*4 z)@`HM+a3a=FrDKnmRMnQC*fa=MnzWMI&x_tVo-$ENy&$mU#RzGwW|tIL z(SG4zm2E_-T&0NyU#~9I%vxKuo?x|}5W!+2c{%@WOLL}mSIZ+rOQpQ43kmC<4lhl# zf&i!;J^5@N-h_ndUPYC>9>y*j$w;8pVPZPFO9sHnw2w-!N-wwXMZ`{#^1M4Kma3=l zwoBdHhqwz%RhUsB#ad>u7fjcZO)jbXdwOGZ;s;O-bDu=5pK_oAy0eZNeI?JOibl4n z1C{4z_o15~!3@7$4QY>xADZr2U`M5`0052S_}XI}2ScBwAO6r@$ErHzh&%$U&aF3_ z46_xf>dc)$>fVj(y0psliu3S#p{cQvw7ViwhOYa{^jajVs6}CIYf!tmqNemKgc;vT zLq7qfZQXiJb!#G4ngLO>+^cy|N%uZf=60|_K2K6G@z8W6sfH-DVodigY5L#3GY~wh z(*IM`jzMpO)3QVHaDAXu*~ySAw@tNLIkWhBuS)pxjsj_$N{>qvIUL&b{0{uN!FV`n z>Py;g!uSWygROdMzRI1Y)SCrQy3#WF5GfbeDP<+&3V()-=c*o|;P2%I)l!)!xnlg_ z{_;|Uj;Dd7?h)C4WjdNNr*B&+Q~>UehcbM5Iw;jEugdbWL)p!FXYs1`7O&ubxz#d7 z(KeU$KJB}1o1xSmIS)qdD<;|Ra*4y{^}BD%WXxQn1ORG7-E1;l)H=dl^Py4WKI`aY zSYyxV?uYQdmMFsMBuCq};V>tS;iwWnCr#BUp+^K;9WHU0ksRM*b{Bed8X(#CembGY zV=k+{3!FUZRs|c?NoOVK@x;H!-~YD}$MhFC2zrIvdVi{!6 zSFy7`AIS@R!ZVZE3g5)+uaBEs?Zp++NxFtO%ty9Ziy@fiOoKhO&g8BkT9R%=`+5Fz zRZGEqZz+}FD=Ncet$_kgWZ?QB>W#?3c%POUC$l=QzU~mmC)#kE^#wPxhNuVHSno;< z2i~T*k#6JKZNZ2}E!RJ0B?Nv!=(L`u!&opi8eEsnikZe`dcM6hdu~jenoyCm%Bv}* zCnC-hUzJ^fJ9MXZBL9txwn(~YsYtEHJ`Yi=4sX55hNgp_;{7s19Y{%h0rP+qHR;kS z!(3uDS4b4REE_65(p;EaPuqM(xax@fb!`nGT&O?05nhYrpXiQFqI|y| zgRR&I`t!9~6M|Ua2F$xInAcYF{tlhF!An6@zKPJRynu#e(pbE*wt;y!tIMF*pSKJ0 z&2v~8F|b`p?QM7a2I?Vv#%$OZE&8tv7PHUFd07pCl?j9Wo2&AOV@6l}784{-7#jW% z**tCLR$GM3P+@H|$M^9HD>XIUy61(;@}=!F)kT_G*YY#o>AbRA)KWOx@!Z=aaxU+3 zN$3p$3EvDgh?>gsSJI3h7SNf=G!;x1e!E83!Gs3s)aipn=GjVIF2>{MGlMHS}tG`e$3}!uw1V*h)CUluJEt z7(JdCVa&{~IOD)9R!rSn40Bd{`W7#oG(+~U;dzd5#C$?Rvg2XKKkAMV_450$9DxkG zwB1pVH|T#00F07b*SW18O=O;5et#3GSBwlrN_t|IGPODb-1o(?7P34rVwoM`rMmmt zDAMQ=>z9BiKsD)&?SsQxaLnf?zkiMht7~V}bFI3twO+sctXhs#l#f(hsx;M8J(Via zq6f;ga8YUFRP{|{c!@=#KE-oIY5QojHC!coZGR6l0Fljl;|Dg4?Xe~B&mR?u@NmN< zntqTh;~e*-RZlh_FC@i@@P9-bHlYsp#`*SM+h0grf+(#=$@EQVhVA$pA-3C zTHx|05G~uVvOa%@vi;ZVNispWkDM(S_ zOb}bYl2%AC@3JWDVggeU?u?wj#XHdD`FBgdC;k&0Z#5PZ%YX~oefOA!ty|aFZ4kHA zEJ_V6YDI>;`?^7a$`JJ}Y-y|HZ88_TCd{&ml(_w!&MmqGsReba$?r_=!t5gnaA$Fgw9tj{8Ovtg zNp=U=cspHseUAaWQv2y08EH2CYNifgN{`NDg4$Q9I~CL?q5WU1=>wYKReHY6s^W}a z=#;6RVd<@_Z@%`=1bGamBAh!}7Hi*k&R3KpCojKvAr+%7!uA{-w@`wawQS|J5CW6+ zE7;YueZ=Te`Clwr_Gci>-@dbkg#2G8zvoH0@SqyZgTQ7D8>qN+2rbNv8%1h#jcvMN z8dPt19zQ@9YLbakT+-UbHGH)-eaD&${@Xa3!J$TS=HlzH!sM;jg?|2-wpi~AdP{wT zC+eg23g+d1UX9i_WDuBAZ#vK)4C)qBHPHFimqk7M7L_A@v26(cH3|dYLu9kd{vS;y zMh5|*{$KGOS;hpiG$3`*)N#IJX(yPX{zTeF6Ejl7V(^TY8OR_Z{idN5MUc$jXX-n< zLYSI@(Hn2ld6eq3s$M>5*IPY2SU4wJ_P*}&>N;|I2AId|O@Dk`Jtwx@cRzQ1FaLYa zM&xxm#Q9UDR1`%)wGv4|qu>yWl}#K{GUkyHfFelWJ8uf`2oH7X@yth>L-#eH^FU5N z?s@9{O}+I2kNmk$+$liYt*3W9knhHZbhHwp8@BQgN;Cilgw0&9yrq{aIdfnlpG z7wsD5-kk<$4H}2p3Vf#aAl$0?6`m9&iA|_b3#*PMV=xwp7y+-|)k?nK{_+v0@lmVs z5hk#^EPl(#=%dq1aU%(y(^q23Q<#gN6JJ}|Rno?~;#T>urRrEi{JFQ;cr)~ktD}ST zC7`6SK}%R9GMeQ<%Z!6TO#kIcn9G~ zvH=o z;(-%%ON(rf%P<~xj;TZA?ye8Jga-Zyyu_8vfSMyIr4R*5s4>tQC zO)8gh6A_sfoHuW-Hgd(Oay+B!wzMKylM7_kC2PyPdB`S4?mSZ;xxS^I4I-ME{bs<^ zHiM2&swV!oaC>5hG;D-<1JVdqhd?9FeqC4|e8tHS5xTfxIY%HPy1QYNuW@yf`mLvr z|I}m+2l4(H%>@6xZ zjn2dwS{ECN;UO37zKUn0!->2zFUKSx(IA$&@5nn=$q2qZJ#ga;-9$g}z7n>FLD8o# z!!ewzzmtlxaKV!=IW;xO+$GcVB{fu|`T_Az^_ClXV|za&f`13X`djWOxD8Mf(ws`+ z-;+1m&N2NE9n`;N#|G4I5kHiN{;7Q6B96brMu^|&AI;Mq3d&C&h{zM01s8ak#lI}* zB?Mt)C#vWrV@QaiJT+U?r5P^5owG2FAuRcoCIYP3N+dUUc*I(Hn_Aljh$te9df`>-{Rs z&a~y}aK5VRrYCX!xGu=|-lATU(gsS_)zcX|a&sVzTd2T zb)RK#wY44oLW*=qy9Bx|DXjYGxm6mFDB-4KS zFx53XW^y_=;48uUSQkbf12D^d>H2$$F2gst`DvdfM_^k;p3KIavaA^`oBp$4{k#5_ zVR^U@SBBj@#d>hBVDor^7vnv!-q&_?qp9?Vt3j&sFt&MU3J@L*ts{UP)S? z2(&RsF=T(_k#iK)$-b=YR-DJeTozd8CidydQ{3z}IyXnr3PR9In7qc5mvt2%?U{?U zg4?jeyhd!haE#nQN$ql@TMajf#vp(Nzl^oI*GS_d)&VWMBw&C0TgB=eR+&Rc!Emg? z^XcW}noO~asc@Hzy zQGb+tbC>yS6cZ|Gxl=Q+z4wNOq0_AC#gVcyuy}G<$^~~K%nwLA{scSNGdty@Rux-& zltcq(m+5nk6LwrilY&r=!{zmpN7(}XSMowF1sUmM3Umb$p~o zYYb#{4;cY+4+h zN!3`X{fhix76ZTLs3h=wD|}h0Hh6+r1O4n_S_*63V9uc$`NG$*8xVVj9eKNSq=msK z#d%W<0WdW~l4fI6u9KSnm4IPGnwZ3VV@UE^)Mo?sD^b*`eKj3}ztx60m+g$hya$Ah z#_WT`BS@Q?5u{fK%Ri)AC(K8YA}EgqH=>OXxT3ITS=&E2jwz6gI9#sqjwqFvE<0D@ z8Doy?qA_0;^L2CW4F4K&$gMk(io8r_`f-gB0Y-E?_H_81!950DHT1!6k~X=B^M&x5 zh1j7<%8a@2sw?s>jq74`uDJ#Z&@1}xt724pzR4J^Bw1>vQQKn2+eMuRTye=drp@{S zVa(WJM=aY++PkD|xAqM+y)9(n!-ci1gw-^#cA5||_WxKm(A(usHcyokR#FC@NZm+p zffa6op1vJ4H+VY+v-$x*q&xX0T< zXpbW+ufL^CWfo=-x;N(B;MR7FYQ6L|B~a)v>sFvCDXJtJp$Ez9^gT)_X#$2RPdL;FrZZF5l6gg23)d4=q;fgn{xreR@x+nqL$#@<_)4T_eT8ul z*nKpH1Ob6YN@QfC0@PmtJX3tXtqaEa$)I9?h}34JqM6AQ6YrU&Vn{kkns`~Q1oFH* z%EZl6vy9ORF79=3Q}e_cQ`WIe30afR8$O%oF*3vFap@gfbGfnqcb#?mx3gJGopq`w?a z|3w$_YKK$NFI;&x(N(-yqCix}zXMe0&tu84|4rFTtIDeg-1Bat)1mU@3kx3!_*Lrs zG~51fzU3%KA3ZjGg-mw^o=j+6ED#}f8^{h-vbEIerHHPY9GurYiq&fYk8uyQsIMt`M#Lj{$j_*S< zabVxJv=q;V&o|-Z&oY~B@ag4X8^Nb&H>Zk1s0@yQQlv-n$-XuC9~HQsr!BDAcA83d zC6<|6cK+|E($MssL1GPKnhdWBk&5myFhSl~wD!K9YmL#pdk;ao1alN7cx!V^v<2oduJ!E?Kf9moI3ihJ@ zP*<()jUDt4Hx?5U(qOYWve~3*cWH626MzQ+?zog)bo-x_x;iyZt3k6@YL#7>#T?YN zIPo@>qIS2;&1p*nP~fn(3KLat3PY~!O7 zQx+Zw)374#_wDDFFToppk(yprKQo573kMSdoNoIQ&9=UiRaL>}^B4B#~y>;FekMN%P?6>UjKTO4eq zCeu-}L=p{X#g0%6Q|K|~8*iX%k0TAm{pZ5pi;T!Xl4PdpJa0ceXd8QtR-BZ-qNfB> zBN_7v%$oT|1B7xM+7G)E>)aBWzw9q;^7Am5^`M6<& z=1E0df9QAy3M(w=)Zsvzd;X4w=rl*EcVZ_NZz3a1wK#2EH0k$SG5}JZ#Ml4 zG|dw+oy0}f ze{I*ECt>o-mTSsxK8alVkrh!D$eaZW?vpk!+q~<7_e`P)EFXgWdUH)p+ZGgC=x%1U z|KRzi)@YWXb=UckqJ^R(HK&GWlZ@c@vAwO@O+{ewr};rgw%exTLOKZ$r7ukn9D;5O zLhyUF3+b5*Yi=Hc#4z|*wQ-;R=-u7uH$A*`{fcwXAQ<+KcSZVAn-fPI743CY)kPqaABe3M;o zq5b!VE}4#S9*P=xW<}Af#|zI=5E3HfQZ;@>Pe#8h?-zZ}b+!b+<3w6Ozn zuHc>TDpy^S=#gX&J(Gk#Sm3O|?e@mckW42o)tWc(GD8S#=%{a5z-*Aq?Pq68WSx@A zLO>PWHb3^VsAO+7_83NMrHY7zZmRlw6b6tCOyYW;``37wAW&s&{>F&?>yOlBBoTFU zt+`?z8m;=3GyN0LY|o;VP0SP@8K>pslvLxBkmD0k%O+FSBU9h{CH^Hi={`fTWRRl~ z`biwVF2Z1kDa;`;{$I@my#;|7Y~c8nJsjr&-oNLHhhlHEV7`k?Td=8pBWNa=ma??( z7L&iw5%~oqGIO!-7a9SfC{|qR@6tkI1mo$)-Aq zpqs4d)U2DTwCS{#%Popj6;OUfSTAaAlvl0ERx~e_=QMv}d`1+1-)~KrB*6W+BYEy} zPj#NY;~($3-@t@KM4rX_8j)T1Y-J%S#HO00v=lQ33P$72qI_apm5Gk2d8WiZ&W!Dx zoU@~%Q-}7Cpm`KCB<->%ls8&Z|thI#Z>#l_iWHI;XHS6s0pqA>8Q%AGdjQ_raPB39(NeC>H|pku(Qw z8Y{X0v2jT|Rcvbe3yUZH_>q*})e&{I+kTbUF1npoSl4g2b{3kVGISVos9f~Z&ayP0@-QAjwQ=jzpDa?@o`>;;T z_EXeu2|k8|mdHUCOsWrA@+LOCr2zif@G&V!O}^M5`(R^qEQ3B-yX?e=mbGhRppgrX{1ZK zrnQB$_#xQUNmAa_az1;<<%~q5`z0aHfi?Rfqs$~*StS=WPJfv(s|JlBJSmvRLpL9= zcrzY`BqcM+(Y#LfH`LW; zcWE`Ym+LRH-*jV?y%6Wnfz3a)_7x|gMEW9O=?Wl;5A#tOgBg7Pmff^U3KH`J&9M*} z`*&URI3_rT@rdVQukEZ&X$hHpMRl^7%z&UV&j1S-Wxjd|JoN{i%QF0xS%$j|ojhSZ z3^z@^v+J)6qjG(-#f^`@0v$fWsXgIa^y5Y_*MBsBpF~3DZLx%upu-g^rHy*q*m_Z* zLf)vuN;jt+Gzd9_k1jo=T}6rmU}EZ?ytPoj)F8DU(}-xa2Tt0`c&+*`9l=~&>4FqP zaQ;r-reOGcEe##sG#}Ax=rgkv+t^q+_OSRtsgF;p8l8v<-`iF#ugU(U@+s@vRy*PQ zRi(;K-T(FI#WFLOTs^=V57onJ9k(NVNDxrg=~AG6&Zy5aW4AhYg(yM<98+3@4+S2& zf+~aVL%a#8d(RPe1|HF4x313(!sv$_uG2oDW@|1Mzc&vJ*xkd1F!{*%?=(t{)FVAq zyhgQRuqejiM-CM5Z4SKs?rP9|#%|xna|EYr738uvD}9izxHmo*hw+u0qtA#}F5-91yj^~gyttdbauNdF=&lL?{PalHI`bF{v5>zAAwz-#-Jc-_9 zXoDTyBHVPUyrBlAfer0H<#1j=jRIq7_>vld9&gcCf%b*)fE#8DKn2Ao{c5esy}=v7 z3TL&b=UA_SbcN2Ig&XS=A;Vx#1lJf!9bbl6&F9+THZvBhfhP+~;3+Km(KKl? zezi=dO}U=v&tQK7T#<*u=;z(Iqj#eaZDCqoOTmiCn1W*ms+c{|o(MX#q3Sf}TFh3Z zuVwLBFVPmaN43$UpU81Mn7B?Qm^HIFzqMr0X`9L=j#e>|cdNK2lCp?H(Q!Hooi55J zENvLibBX;rEn$_nNX~|;a(=^{P8~GIAKpWS&)3Xyes|Xdes$3D89JAA$R;fGp{A%| zuxHe*2XtqosguvEcx7ZOp5l9{7wLIv6m8EFs(Ag)Ryaj@OOe8yXIJ%#BTzbZ>W0D1 zQ7EH|>@JyWIb}+AL#n@eR{a9iuexLPsp?xE9e$+#jRotC9dHXgbtBiMWK?qsDCW}ZHZKcuna zF?Le&!_y%>S?$uxL=xwTk!B=&TBEEkT8nhjK^^D;U8vz4D~DNG8Oy36O8LOI>f~{r z_F>NbXMPE!%2xmLZ(A;E)bK9j72-X3UseQ_t#QP17n;rDJK;|GLpzJ$^$9EL9cHjv z_PM>u0pUo9`4{;y#N*IRkjW@#A+6LF+4W-YOjq6u)<%VnLR)+B9t)ar#7uBcmIKx` z7O-gGDl5HiBm2}gR;+gB}lTcJ61-7|K4b|Vwte(5@(PPJ4XOCW0AxW3()S-Ac}j=rV+=PJqxRe@-K;Il-K%ug`&MQsn%|C=`C(BP;qDd z0fKUSmRmjeQ%;ZbBI3Na*`ZP(61oY2&L(VrkWt(q#UaYI>BgN5g$A}DLiyFzPk48` z?8Eyja11!$H>~WZeo^sD+*W#>{s7ourlQDVq^|F|ssGVHy}*96T4oTrrj1=o7QSch z7|$7;SK%n+M$03%o(dbaJ`ctK>Zdsm49WaE6)InG_To1@UX|kEnHR`8VxH{B1F^l5 zcxgw{k>jZU4t{6EWIeRo5A>{o73~i74lbV%eK84qTe4EkugE;^f%q*qivlG0OV~({ z9cYu_O-*-yJ^!+!86b^j!*t%XKVv+!misNKrUUhU}mKA4xc&z;jYWII{8>QlpUQd8e`cs6-n@+ z^w*Fr-R#kvUo|>{y~kq-S{cgAm>lu5BNsb}Va8aYkAcyKi{~|^DgpfX_IzxRk`LUR zgjx`eQufu@hKYMy^CD^BWLkAgnRc?+4yqq;gBN#-zjb)Xh1nNo>GcKMBIdZkiUsMw z_Q#{@SDj)>G*}pvU_u944gED->tDcNGYFfxUsu#kR*cY0%IEsxe~r$T>uMEuvbFERS`hz=cF_&D40F+usL7bxS#P`D2lq!+&Rj>Qs!&l zl)p%3AsYmeNagZ+rA6r|j9V^UyU^^O6{}N4M9lI>ep~hn#1(dRJt` zRcgO9vnZ3&&G0Zk2PGXV2Phr2>_(SZi~aN^aW!lp!rLVv^8wPcoG;$FW0MVGLBj&~ zYAenNu%(P=XGYlH*w>{z;HSmd9^5STvXP78`ChULh`kUXU$wPcmZW@;xFm;@Fbk%e zC7iKM8r zoK8Z+-Z6N~lK=(6+7W7r9nY}A)!_qvqx#OdkwWc0XIE)xeWcguW)iav3-7w{86S?G zN@r|ReuD1-E%l~2$x|A)59Z}hr~IstWIY^dionoQmf*Wvw!rEEiFnb(>ilcubo)Nk zO#437lvi?})tPtSH&Cyj%+CXI@adw#O_q9s$QFyh4Q&zr!mKzO;)Bk3yS_?GmT;zMmkU%d5ndop>l7v3g|r9bbopY%2b89^u= zrjXp$;!B4e9-r>bQf(5tvGb(&S%xL8wQ6Y%6qk#_!KaBtVU=%bLMwZ#N zpyU-`#xld-mS)a$g1LxQFifl}bmZ?PiXa@cfbJWu3g4aU?QaHCNfCvfSe^g9?B9%P z64d2y1qzH&fm zYvbf~L97{U%rJ>6GSzZj0`9F;OT|>@1I@M}*l(~<7e+}ikc54+-R6<~V_OsAUl|aR zqhslni0Vtt`=bm`qb>|#SGKT5Qc_=wp7{&)la`MIQ@S;+w%`m=*0~o#sxMHgb){UqHt^T z(w?$|oYLMWjb3&qMW(V-;-&SYDGhiKlVt&%hKpxI1m$&mM=%4jSPu_bjrl;T8r!3i zZK6-ktg!i$LY7FH7iDdp5Hfs~Rk;JNtS14Pc~h`^Y(-t>)Y=~pQhyWKe)&aOSn$;p zxkBe=Nqbsc2T)yw>|F_twPOOzS70M<%HVswP9j0@3I`Z#fg_EWiibp%*Wg8V78Q^V z7vN2wP#cg2F@e9=`*WY59Gs!9zSArR+(4Yb@a{F=$Xq(WN6$H#SsG_exZqyeYS(eR zvi1Jd(VWP5%|4XV{C+llV|}X*)zmLmm*F6k1Ks)kn+gOgFbgp5yz)s{zC{Va;ewao8Gf!VpGJ#7*!QP;Oa}d> zdzW07p4>dBpqaXLPt10u;JoOt?NBE8Yh3?MssTg{fg0_D{#G2~h!hfnOBS2CmZ?EY z(G7~@(&HFO$oYZNaTN*><9X22vhkL~#in?z&yA9e$WoxEpU|fBK;B#sbsRL|jvB6a z^^}aKR=p zM;sjNZNALkO{uWGr;NQi>yTr+jFU@!J#yr2hZM3C%7_l=pu|3!htHJ;cgHSo8CQa_ z0a<}caTI1P$b|tAlNhtMLhiQml!;E6ybzPNAgd7L+WCJLA;u;1iHN;HJ@ARqe*awH z3)$lne7r|gUbW6mJ#}IG4E}8v)e!?`s{UY`JOMsptD3&G?If(= zgjGihgtW(4jVRir%%UVKFBG3N@lU=6>Z7JuCKWRx zgw*`C*^_uGKJfiN7fM;Nn-yh7Lcr@EsZOP z_BCbJP;siZU*5GUozFM$STFjvqy!bA0M$IRC%jEQ8YiRU-fvRyQiBF3MEC~wRvQrD^)op zgnnGRc(R;3wKxyjAuzF()UYX4AwZnYPy$flNQ0s)$r4cWlEWz^m!ZgLSJ#Nfm&-XP zR8vp_n)pQxs&EPJ+ha5NW+$OMGd0b0R(RDfY=ZCfA3_EG zT{BQW#!N|O)jSeCCfxiB=-*#zH+TbXfnne zWUQiH#eilJ*nsF{RkU91)pnd)!q-OiMDZ+)5etVmrHV`OrBa|wsuy0>wJ1~o`<${j z;&wNqU9FZ`NA$YxE_uJ=boYFoFev`vY~EB*|_hF4a&tm~0(f z6HS!|9}xV>xmqVm9lSfD6C*GvyV_0GUmQ^d6|;+q?4$G*xu5=<552eKHXX{vN3GCY z)kp4VTHFCeKW?`<;=l6?!XZqOuw7|GAI+qH#Qo}nGs_}x?Gg4tx3b9p(1&0ReYgXz zcRB*Tjj*<1_urjfM9~?jIJk5*=I;~DM(ZXQ>j7g7T5zqkdN^<5np7z<$=>itXXC_q zWtzx)WoMdHGiGI)dsyew^hlWe`Q^tr916?K-{`DcUcmyOj0F=+z`Bn<*f^ z|DFe4#cR&-zF&u8Z*tCPU)-`hp|iRq>h5x5s|dM;;t;;Nrbh#VlVJ%}`m3`%H40nz z2lmulB}MaE@Q=;fu4HP!;ZRzAB0pgD1<-DIrvI>s^K7y?W;iW!m5R!v*jxrF+TCr2 zFHo6t9x6;{mWm1BlP@!=Otzh0l`?($lKtE{OCecYb#c}47b5I<(#TrVVUFBXu1<2nfev9b(`p3=wbjQ~@&8z8JvgbVg=6 zQ4=k?avaLrKWr=GG!F^pg+1)jp-oOkyd89;d^mSVkQf~Gj50gux6*wvcyKXl4q_`X z9Fv<7DBD*K2@YL#Jfjb;UMD##e65`}gX2=%Kk^*u5u5$!9N>(#G~RA6Su+Uz!n6JU zU^InqU`gQgazRp;^IN@O`C9L+VX5F0cWO@hGj3MPi!raXZnMKygk-6GX>^+p7XPXD zKKHixys+p}pP3A05P-O|I~aZdjjtBQSP`Mp&b^?qv`663c2_ zt8jh!+#6B5)!BA4)bGxoi{YN5u25|MAF>;CK2BA_D>dUJ+%Y$7_a8ld+I1+|dKw(1 zM&XIyCX+2Q4$y&(j`v9y(+4KZe4+gi@7=4MPO|Qm2eONF5bS$5a2=jwq#`otc=q$w zm-Spm-1<+ zkE>nBKB_Y@FUzh}fJgGKQ+6he9>-MBF8gA9%UcV5E^3jmOR{Hp?~fHl5%eoV0Lwr` zv;RZ8a8Ci<@Z-#K{}8J%;qeHJ{XP{=hg@=*Xu(lS^Q8xe0-{O}Sa8S(CGE?MCFG6P zeDq(57>^$q!S)?4QWeRKQt%jra&3#IvNB1%% zrSH1OPNm2-u!c~?e#b)8L|M!=YB20%IyDLQ9o9TOB;*&O(UN)z7<~h$Qq-KCXIt5G zfQMATubK85;On~T%#O!4$Dz8L+@JiICN^0O<{J0 z{Vt!j@|!1 zU;R!+wy=3yS7N8<8Wx_)Ug0L8o4BrW)^t-NJo@|_ePXtwOMEvLF-WHtX=T_!ILK&o@s4TH2!rv{a zPx`imY19uFV-Izb>E{#B2e+MhndZ^rEm;e}4CGMkVHDMc4wAH5t>wfmbQAe);3`XN zqC0F8CX+7&)r`{1Q zc|MeUz)?-PIDUgsZ(-?0hPM$uZpMHvyoLfRp2~Ny>tA@~lM!W%_g8A8$4Fb~baY9e zD~bzvyFvvRBkXs_IRW$0n1rY8*FevOjD?>+$L3yOOs1!ezfz>s{&zk*2&VTYiVl8rrO?83+|o0&lV z`!tahqmbz52Fd7sPYuntZfWb5UsH-RH!=+ysKwGW5(vAvOrDL@zJAVIRy1?)-D;B) z8ZDd$wP#3#XAY@?zY_&@C28?ASvVj@1^dT0$K$xrNteIsgxk0~6DhC_$L>+{KA!ri zkdn>sGsaUhh+FOaRSCs?xIIk!K}JHK+Ni2ma%WKz#v)r+P%-kE?)2T7Z+DG1zy2D7 z(@+)V*kX}^o~4?wVp+=o6CXIMq#(|zAkc{0%U`hy{`&5QJSnq?yA+WnlmZIKX|B^M zWEnexaZKp_@~e0HQ-c+y8yN24qqwW^25d_cOo%iYF;_jZ^V)_$p7@bQ<(HgGnN%8a z`3IWPPqj4T`o5$(mql2?v=d~4rXKCJJ)4M9TI1Bs8H_HVj!6t*(|345^=4wLVW*^+ zihNohUeH4Q0{XnfYUTW>bPeRmL`uAavN&v?>S?<_j zUuRx_=s{`D-gU?!|CwGa+coE+-0~@Z5qo}9%qKlw?F#eXxi!Iy-UiL&)UTXzw4A9& zcq8s+H7!TM5U0z7Yt+a=BDrObAqBa6WL@;#J4{uKKOV3^8<|5E3_ZkE^g?>}2x~$K zr#0|7#Y}CZ53*3(EN-@sbtQ3MM>D&AYhLW%?y^Cu3_4><_ZvldhMs20;Q3%He%5)Q zw;kadUM!$^jIl?;yz+u8_FO8xM0yvv2Gy5D-ND2w8W>o+gN1>S1=PeU!}S)GDX(X# zD_CQCo72NS6cLNw3G7-*GS6be&NOy+GrHHn@o`Yzoyu8)by&+ZKYqMP)egzbHC1#2 zn`+u#?NE@E=8-VB4AtZizA9XnzSzlr@i0dk2EV@8CBX3XWwN>(b8kB_k*teFMH>;u zPHXs_7=>Tu5y(b84CMix!fOd>=ftaU-9k4?VvWxf`!Qz4kA=DozHz!OS!_3T3PWmX z5w(HKWko!xt@<-lMl-6_vreKjOG9)e!77cA))dy0Bn!wX6@m^}Ih-Sl^D8Gx%R-tb zJl!>h^>A4jyqFzoeff;<*&zAooM>Zfz7fKpDK;r@nAC8BahmT6-fAKG!?J$qcrb5C zNNd@svg{2)yGBVTskSO^yGYK35vi0tS}#C2JyK9<=dq-*a092srWuvdfh|rU=gyS&W0~#>AfV%(FiMbz!IQAobj2zm=2uCv z?r3BfbpMkk!5&q1SLADp?61wk39!@>)K&n#*rCJ#4Zixkb)^5!`!$&k>(9Q*t#+3e0Z z2TE!^cx?J{5$=k#OCTfc53r~il=H_SDZNm5puZx&<8Xieqa(yW2QS4_eI!%8&bsxm z9p0c9P%JsQWkPmv@PM<2;Rf%3vHOPI^;q1LtazVWUW=9>T+#1c2BVDbUe_UirRyY8 zr;uFd5@aToxD;}8&*kg|T$(`JOIT4fMmMzwY%HH-q8w*Z{MEfL zzjKXz5nfnr1p*X}zm(X2RUplZWL1+JJh+LHIb^LU>r{`D_;% za9DZ|SOuW)WrGVJ${G5bC2L^88Ru>Y76qlUmTH|J`C*79GW|7{7=aiVa@18L>53yJ z>~1TD*Rg2ivU{QHh!H}a?C5~)M6Q>oAKzFR0WbV^a-y@nUb=el<%esM$$T&4)z;|m z;Dg^UJIC5CX6E2mbg#5EBgU~BV^gn~!^i+q5@rne8^QRaq&1sJnw&IDP52{up-2ln z<-1MYgmV3N72}coFgj;rBv)xx=WX=(~k|Q+09)EFJ_8g*_xd zeT(($fRe1yw6`;@E6H55Gv1UzJNti)%;`x_eD+`|vKSs`};#NZMK0 zFFJwYnKQr>7vp+tBx^~Ey^evS)ceMPB>#YT38@OmQKYMV+mqqhiq?foAA)3m2$pLL zaruD4^j0me5J^lqi%d+Sfx5HGOA)*mq)m464_UF0_=~WZTrGNifUCnsY4a7V?87^# z^a8_#OT_5V`LD2i7*`C|+=%4Lk3FC)(f&95V7U*K&AeQ-+k8DDO_1(i@4Bs>_9^X6 z_#h2!cz5poAKQHr^$xO3<5vBH#>44snUIxg!?u|#weBa%U(z$7kN9$a%Lsj1AunCA zNVh`R&1BoVay!cgKRtH&EYe!3=x87e4GEz0?3rnpe}Y#=>zC4k!b&Bz_+kDQhFW!n za}CHhW|xZXCvbu2-^9$eh~!HwK9LLnQ2iyi%fn>fRQ0Uxr!;eErkKMNG8?DvOZ)W_ z+4*My<`NU9_43_Wt9y`;HG=?#BOqnlt&IMOJg64 z^Q}wM9&E(MK3pq}B{|HGedxi~dakFY^3MND~R)O<-$VQgSrn3_{r zXR1>FWT3%boue!x7P?Z8)TKvF(k{$MKu7Qp(GAR0$E*FrDlg=Otwku6&` z^Rs97MqzB&ZPDmAenS2BHFEhnkTQYuKlHY>&Nij->nW?%a6?uK-us_$vB$t zpFBjVr<~hdulk9<_e&GZrqJ&ANlD!3Nzlrp>p6{JIt+swXWfr^>}t%M+9wMKX5X6U zP)6Um*)v?2Prf*>80qC+es0^xTWaUg>`&|oYf^h9QiQxbWlBL@`~%6N{&C_fb@3@d zZ7(P_1F}%)naTH-^`n^;0?zJ9+H2|1^VPm&@_RMShlWm^yz%NBfvZezCH8pn*W7+X zEoKm}{WRO0i(-kpA#Hd0yO*h`q@+wnS#pcLjunvf%7PZJQr&*CMR!|5`qpFa5^5J( zlNYb2s;REes$unrhNvZnJKd#&9jN~(c}c`33zbuC59M~JY(#-)qv1Zf_u~o;vp;=Q ztS4jVWt5%=HIpuyY|1D^%SW-0frX1%MC3VNOqpy}rDlkh2}1qq&!UmP2CTT`hGN#x^%UWdBtpb84R zadsKm>a+2Wa*gk#JwxG~AM(?j7q#6ml$_>B+cC*jkwIY;Czbbty11Tba;r1 zb+2D{Fm;XL#G93F8lPTJo$g^lY%{(~)Nxd^4*SCIH0I9Ekj2CIl>JS#);VbAX~c89 zyE45V&3+{rA~XR9@d-C)%seG?i~01fSroYh3vU%(y_}*Zl=eQ2nK&_t12ZqC=d8#_Q zH-RQD0w3M%7Gt-KOz2{h=L(LZ|20Y#Yo? zufYD`_~xza(sBz%ij?G-6{ReZ`VDZIyI5CQPwmyIR=I^7X}6gdcMk957d4qqsnfZ|LyB_?2Qr{jkkkKT=2}pLwDDaX>s7!v;fEn2S_BP2NY+(|IxA= zpP>S2V`AovV@GLi|j21{_76o|c zxIwi{Xb=_PGRF=5s|@_VH0ZV{KKE|3D)D-e*QFTlJ4gZ{PV@$aMl`R@9DXaFmaxuOWV47@eBqR4@W zIa27qlQ#kk45`2R9KZp&ReO+n*e!l^RTTPv!#NBL>A$cSh+7bPfCreaaf6KFZr#c? zC6Hm_tx24Z1CZoWqwkUe3hNM%Y4U%<)W8sm6evZ9T+?nt3`I1+^*T4mDdX0yLJ!tQ7PbJniL@0f`Alyf$?$4e_ZIl{}`H#@h?K^ejs^^l@c50pQs@E Nq$R_^uaqX; diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 4f0c171..a4f0001 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Tue May 12 23:06:12 MDT 2020 -distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-all.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.4.1-bin.zip zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index af6708f..fbd7c51 100644 --- a/gradlew +++ b/gradlew @@ -1,5 +1,21 @@ #!/usr/bin/env sh +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + ############################################################################## ## ## Gradle start up script for UN*X @@ -28,7 +44,7 @@ APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m"' +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" @@ -66,6 +82,7 @@ esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then @@ -109,10 +126,11 @@ if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath @@ -138,19 +156,19 @@ if $cygwin ; then else eval `echo args$i`="\"$arg\"" fi - i=$((i+1)) + i=`expr $i + 1` done case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi @@ -159,14 +177,9 @@ save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } -APP_ARGS=$(save "$@") +APP_ARGS=`save "$@"` # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 6d57edc..5093609 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,3 +1,19 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -13,8 +29,11 @@ if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome @@ -65,6 +84,7 @@ set CMD_LINE_ARGS=%* set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% diff --git a/mcspring-api/mcspring-base/README.md b/mcspring-api/mcspring-base/README.md new file mode 100644 index 0000000..e76db38 --- /dev/null +++ b/mcspring-api/mcspring-base/README.md @@ -0,0 +1,3 @@ +mcspring-base +--- +_A lightweight Spring Boot wrapper for Bukkit_ diff --git a/mcspring-api/mcspring-base/build.gradle.kts b/mcspring-api/mcspring-base/build.gradle.kts index 71c146d..fdd6c23 100644 --- a/mcspring-api/mcspring-base/build.gradle.kts +++ b/mcspring-api/mcspring-base/build.gradle.kts @@ -3,17 +3,14 @@ plugins { } dependencies { - api("org.apache.logging.log4j:log4j-core:2.12.1") - api("org.spigotmc:spigot-api") - api("org.springframework.boot:spring-boot-loader") - api("org.springframework.boot:spring-boot-starter") +// compileOnly("org.apache.logging.log4j:log4j-core:2.12.1") + api("org.springframework.boot:spring-boot-loader") { + exclude("org.springframework.boot", "spring-boot-dependencies") + } + api("org.springframework.boot:spring-boot-starter") { + exclude("org.springframework.boot", "spring-boot-starter-logging") + exclude("org.springframework.boot", "spring-boot-dependencies") + } - implementation(project(":mcspring-api:mcspring-common")) - implementation(project(":mcspring-api:mcspring-jar-loader")) - implementation("org.jetbrains.kotlin:kotlin-reflect:1.3.72") - implementation("org.springframework.boot:spring-boot-loader") - implementation("org.springframework.boot:spring-boot-starter") - - testImplementation("org.springframework.boot:spring-boot-starter-test") - testImplementation("org.spigotmc:spigot-api") + implementation("io.github.classgraph:classgraph:4.8.83") } diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/javaplugin/SpringJavaPlugin.java b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/javaplugin/SpringJavaPlugin.java new file mode 100644 index 0000000..3483859 --- /dev/null +++ b/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/javaplugin/SpringJavaPlugin.java @@ -0,0 +1,52 @@ +package in.kyle.mcspring.javaplugin; + +import org.bukkit.plugin.java.JavaPlugin; +import org.springframework.boot.loader.JarLauncher; +import org.springframework.boot.loader.archive.Archive; + +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Iterator; + +import in.kyle.mcspring.SpringLoader; + +// This has to be written in Java for loading +public class SpringJavaPlugin extends JavaPlugin { + + private SpringLoader impl; + + @Override + public void onEnable() { + try { + new McSpringLoader().launch(getClassLoader()); + impl = new SpringLoader(this, getClassLoader()); + impl.onEnable(); + } catch (Exception e) { + getLogger().info("MCSpring failed to load"); + throw new RuntimeException(e); + } + } + + @Override + public void onDisable() { + impl.onDisable(); + } + + static class McSpringLoader extends JarLauncher { + public void launch(ClassLoader classLoader) throws Exception { + Iterator activeArchives = getClassPathArchivesIterator(); + Method addURL = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); + addURL.setAccessible(true); + + while (activeArchives.hasNext()) { + addURL.invoke(classLoader, activeArchives.next().getUrl()); + } + } + + @Override + protected String getMainClass() { + return ""; + } + } +} diff --git a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/SpringLoader.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/SpringLoader.kt new file mode 100644 index 0000000..e9e4cdd --- /dev/null +++ b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/SpringLoader.kt @@ -0,0 +1,80 @@ +package `in`.kyle.mcspring + +import io.github.classgraph.ClassGraph +import org.bukkit.plugin.java.JavaPlugin +import org.springframework.boot.Banner +import org.springframework.boot.builder.SpringApplicationBuilder +import org.springframework.boot.loader.JarLauncher +import org.springframework.context.ConfigurableApplicationContext +import org.springframework.core.io.DefaultResourceLoader +import java.net.URL +import java.net.URLClassLoader + +class SpringLoader( + private val javaPlugin: JavaPlugin, + private val classLoader: ClassLoader +) { + + private var context: ConfigurableApplicationContext? = null + private var logger = javaPlugin.logger + + fun onEnable() { + try { + initSpring() + } catch (exception: Exception) { + logger.warning("MCSpring Failed to load ${javaPlugin.name}") + throw exception + } + } + + private fun initSpring() { + val mains = ClassGraph() + .enableAnnotationInfo() + .scan(4) + .allStandardClasses + .filter { it.hasAnnotation("in.kyle.mcspring.annotation.SpringPlugin") } + .map { it.name } + require(mains.size == 1) { "There should only be 1 main class on the classpath: $mains" } + logger.info("Using main class: $mains") + val config = Class.forName(mains[0]) + val builder = SpringApplicationBuilder() + var sources = arrayOf(config, SpringSpigotSupport::class.java) + + // TODO search +// if (SETUP_PLUGINS.isNotEmpty()) { +// val parent = SETUP_PLUGINS.entries.reduce { _, b -> b }.value +// builder.parent(parent.context) +// sources = sources.copyOfRange(0, 1) +// } + + // this is sad :( + val cl = Thread.currentThread().contextClassLoader + Thread.currentThread().contextClassLoader = classLoader + + context = builder.sources(*sources) + .resourceLoader(DefaultResourceLoader(classLoader)) + .bannerMode(Banner.Mode.OFF) + .properties("spigot.plugin=${javaPlugin.name}") + .logStartupInfo(true) + .run() + Thread.currentThread().contextClassLoader = cl + } + + fun onDisable() { + if (context != null) { + context!!.close() + context = null + } + } + + class McSpringLoader : JarLauncher() { + fun launch(classLoader: ClassLoader?) { + val activeArchives = classPathArchivesIterator + val addURL = URLClassLoader::class.java.getDeclaredMethod("addURL", URL::class.java) + addURL.isAccessible = true + activeArchives.forEach { addURL(classLoader, it.url) } + } + + override fun getMainClass() = "" + } +} diff --git a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/SpringPlugin.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/SpringPlugin.kt deleted file mode 100644 index 32fb2f0..0000000 --- a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/SpringPlugin.kt +++ /dev/null @@ -1,67 +0,0 @@ -package `in`.kyle.mcspring - -import org.apache.logging.log4j.Level -import org.apache.logging.log4j.LogManager -import org.apache.logging.log4j.core.Logger -import org.bukkit.plugin.Plugin -import org.springframework.boot.Banner -import org.springframework.boot.builder.SpringApplicationBuilder -import org.springframework.context.ConfigurableApplicationContext -import org.springframework.core.io.DefaultResourceLoader - -internal class SpringPlugin( - private val plugin: Plugin -) { - - private var context: ConfigurableApplicationContext? = null - - fun onDisable(plugin: Plugin) { - if (context != null) { - context!!.close() - context = null - SETUP_PLUGINS.remove(plugin) - } - } - - private fun initSpring(config: Class<*>) { - val builder = SpringApplicationBuilder() - var sources = arrayOf(config, SpringSpigotSupport::class.java) - - if (SETUP_PLUGINS.isNotEmpty()) { - val parent = SETUP_PLUGINS.entries.reduce { _, b -> b }.value - builder.parent(parent.context) - sources = sources.copyOfRange(0, 1) - } - - context = builder.sources(*sources) - .resourceLoader(DefaultResourceLoader(plugin.javaClass.classLoader)) - .bannerMode(Banner.Mode.OFF) - .properties("spigot.plugin=" + plugin.name) - .logStartupInfo(true) - .run() - } - - companion object { - private val SETUP_PLUGINS = LinkedHashMap() - - @JvmStatic - fun setup(plugin: Plugin, config: Class<*>) { - setupLogger() - val springPlugin = SpringPlugin(plugin) - springPlugin.initSpring(config) - SETUP_PLUGINS[plugin] = springPlugin - } - - @JvmStatic - fun teardown(plugin: Plugin) { - SETUP_PLUGINS.remove(plugin)?.onDisable(plugin) - } - - private fun setupLogger() { - if (SETUP_PLUGINS.isEmpty()) { - val rootLogger = LogManager.getRootLogger() as Logger - rootLogger.level = Level.ALL - } - } - } -} diff --git a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/SpringSpigotSupport.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/SpringSpigotSupport.kt index 57c46b9..d2e7a65 100644 --- a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/SpringSpigotSupport.kt +++ b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/SpringSpigotSupport.kt @@ -20,7 +20,7 @@ import java.util.logging.Logger @Configuration @ComponentScan(basePackageClasses = [SpringSpigotSupport::class]) @EnableScheduling -internal open class SpringSpigotSupport { +open class SpringSpigotSupport { @Bean open fun plugin(@Value("\${spigot.plugin}") pluginName: String): Plugin { diff --git a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/annotation/PluginDepend.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/annotation/PluginDepend.kt index 397e866..6c4296a 100644 --- a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/annotation/PluginDepend.kt +++ b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/annotation/PluginDepend.kt @@ -5,4 +5,4 @@ package `in`.kyle.mcspring.annotation */ @Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) -annotation class PluginDepend(vararg val plugins: String, val soft: Boolean = false) +annotation class PluginDepend(vararg val plugins: String) diff --git a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/annotation/SoftPluginDepend.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/annotation/SoftPluginDepend.kt new file mode 100644 index 0000000..bb72823 --- /dev/null +++ b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/annotation/SoftPluginDepend.kt @@ -0,0 +1,8 @@ +package `in`.kyle.mcspring.annotation + +/** + * Declares a dependency in the `plugin.yml` + */ +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) +annotation class SoftPluginDepend(vararg val plugins: String) diff --git a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/annotation/SpringPlugin.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/annotation/SpringPlugin.kt new file mode 100644 index 0000000..d0211fa --- /dev/null +++ b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/annotation/SpringPlugin.kt @@ -0,0 +1,16 @@ +package `in`.kyle.mcspring.annotation + +import `in`.kyle.mcspring.SpringSpigotSupport +import org.springframework.boot.autoconfigure.EnableAutoConfiguration +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.context.annotation.Import +import java.lang.annotation.Inherited + +@Target(AnnotationTarget.CLASS) +@Retention(AnnotationRetention.RUNTIME) +@MustBeDocumented +@Inherited +@SpringBootApplication +@Import(SpringSpigotSupport::class) +@EnableAutoConfiguration +annotation class SpringPlugin diff --git a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/execution/SimpleMethodInjection.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/execution/SimpleMethodInjection.kt deleted file mode 100644 index 9dd3336..0000000 --- a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/execution/SimpleMethodInjection.kt +++ /dev/null @@ -1,56 +0,0 @@ -package `in`.kyle.mcspring.command.execution - -import `in`.kyle.mcspring.command.resolution.ParameterResolver -import org.springframework.context.annotation.Lazy -import org.springframework.stereotype.Component -import kotlin.reflect.KFunction -import kotlin.reflect.full.isSubclassOf -import kotlin.reflect.jvm.isAccessible - -@Lazy -@Component -class SimpleMethodInjection( - private val parameterResolvers: List -) { - - fun makeResolvers(contextObjects: List): List { - return contextObjects.filterNotNull().map(this::makeResolverFor) - } - - private fun makeResolverFor(contextObject: Any): ParameterResolver { - return object : ParameterResolver { - override fun resolve(parameter: Class<*>): Any? { - return contextObject.takeIf { contextObject::class.isSubclassOf(parameter.kotlin) } - } - } - } - - fun callWithInjection(function: KFunction, types: List>, contextObjects: List): Any { - val contextResolvers = makeResolvers(contextObjects) - val parameters = getParameters(types, contextResolvers) - try { - function.isAccessible = true - } catch (e: RuntimeException) { - // java compat required - } - return function.call(*parameters) - } - - fun getParameters(parameters: List>, contextParameterResolvers: List): Array { - val methodResolvers = contextParameterResolvers.plus(parameterResolvers).toMutableList() - - return parameters.map { parameter: Class<*> -> - val candidates = methodResolvers.map { it.resolve(parameter) }.toMutableList() - val firstIndex = candidates.indexOfFirst { it != null } - require(firstIndex != -1) { "Unable to resolve parameter $parameter for func($parameters)" } - methodResolvers.cycleElement(firstIndex) - candidates[firstIndex]!! - }.toTypedArray() - } - - private fun MutableList.cycleElement(index: Int): T { - val element = removeAt(index) - add(element) - return element - } -} diff --git a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/registration/BukkitCommandRegistration.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/registration/BukkitCommandRegistration.kt deleted file mode 100644 index 8aa5fd7..0000000 --- a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/registration/BukkitCommandRegistration.kt +++ /dev/null @@ -1,27 +0,0 @@ -package `in`.kyle.mcspring.command.registration - -import `in`.kyle.mcspring.command.Command -import org.bukkit.plugin.Plugin -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean -import org.springframework.stereotype.Component -import java.lang.reflect.Method - -@Component -@ConditionalOnBean(Plugin::class) -internal class BukkitCommandRegistration( - private val controller: CommandController, - private val commandExecutorFactory: CommandExecutorFactory -) : CommandRegistration { - - override fun register(command: Command, method: Method, obj: Any) { - val bukkitCommand = commandExecutorFactory.makeCommand(method, obj, command.value) - with(bukkitCommand) { - aliases = command.aliases.toList() - description = command.description - usage = command.usage - permission = command.permission - permissionMessage = command.permissionMessage - } - controller.registerCommand(bukkitCommand) - } -} diff --git a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/registration/CommandController.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/registration/CommandController.kt deleted file mode 100644 index 0a665fc..0000000 --- a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/registration/CommandController.kt +++ /dev/null @@ -1,19 +0,0 @@ -package `in`.kyle.mcspring.command.registration - -import `in`.kyle.mcspring.common.commands.CommandMapWrapper -import org.bukkit.command.Command -import org.bukkit.plugin.Plugin -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean -import org.springframework.context.annotation.Bean -import org.springframework.stereotype.Controller - -@Controller -@ConditionalOnBean(Plugin::class) -internal class CommandController( - private val plugin: Plugin -) { - fun registerCommand(command: Command) = CommandMapWrapper.registerCommand(plugin, command) - - @Bean - fun commandMap() = CommandMapWrapper.commandMap -} diff --git a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/registration/CommandExecutorFactory.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/registration/CommandExecutorFactory.kt deleted file mode 100644 index 6931f39..0000000 --- a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/registration/CommandExecutorFactory.kt +++ /dev/null @@ -1,49 +0,0 @@ -package `in`.kyle.mcspring.command.registration - -import `in`.kyle.mcspring.command.execution.SimpleMethodInjection -import org.bukkit.command.Command -import org.bukkit.command.CommandExecutor -import org.bukkit.command.CommandSender -import org.bukkit.command.PluginCommand -import org.bukkit.plugin.Plugin -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean -import org.springframework.stereotype.Component -import java.lang.reflect.Method - -@Component -@ConditionalOnBean(Plugin::class) -open class CommandExecutorFactory( - private val injection: SimpleMethodInjection, - private val plugin: Plugin -) { - - fun makeCommand(method: Method, obj: Any, name: String): Command { - val constructor = PluginCommand::class.java.getDeclaredConstructor(String::class.java, Plugin::class.java) - constructor.isAccessible = true - val command = constructor.newInstance(name, plugin) - val executor = makeExecutor(method, obj) - command.setExecutor(executor) - return command - } - - private fun makeExecutor(method: Method, obj: Any): CommandExecutor { - return CommandExecutor { commandSender: CommandSender, bukkitCommand: Command, label: String, args: Array -> - try { - val injectionResolvers = - injection.makeResolvers(listOf(commandSender, args, label, bukkitCommand)) -// val contextParameterResolvers = -// commandResolvers.map { it.makeResolver(command) }.plus(injectionResolvers) - - val types = method.parameterTypes.toList() -// val parameters = injection.getParameters(types, contextParameterResolvers) -// val result = method.invoke(obj, *parameters) -// if (result !is Unit) { -// commandSender.sendMessage(result.toString()) -// } - } catch (exception: RuntimeException) { - throw RuntimeException("Could not invoke method ${method.name}", exception) - } - true - } - } -} diff --git a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/registration/CommandRegistration.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/registration/CommandRegistration.kt deleted file mode 100644 index 705ce8e..0000000 --- a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/registration/CommandRegistration.kt +++ /dev/null @@ -1,8 +0,0 @@ -package `in`.kyle.mcspring.command.registration - -import `in`.kyle.mcspring.command.Command -import java.lang.reflect.Method - -interface CommandRegistration { - fun register(command: Command, method: Method, obj: Any) -} diff --git a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/registration/CommandScanner.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/registration/CommandScanner.kt deleted file mode 100644 index c91dc67..0000000 --- a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/registration/CommandScanner.kt +++ /dev/null @@ -1,28 +0,0 @@ -package `in`.kyle.mcspring.command.registration - -import `in`.kyle.mcspring.command.Command -import `in`.kyle.mcspring.util.SpringScanner -import org.springframework.context.ApplicationContext -import org.springframework.context.ApplicationContextAware -import org.springframework.stereotype.Component -import java.lang.reflect.Method - -@Component -internal class CommandScanner( - private val scanner: SpringScanner, - private val commandRegistration: CommandRegistration -) : ApplicationContextAware { - - private val registeredCommands: MutableSet = mutableSetOf() - - override fun setApplicationContext(ctx: ApplicationContext) { - val scan = scanner.scanMethods(Command::class.java) - scan.filterKeys { it !in registeredCommands } - .filterKeys { it.returnType in arrayOf(Void::class.java, String::class.java) } - .forEach { (key, value) -> - val command = key.getAnnotation(Command::class.java) - commandRegistration.register(command, key, value) - registeredCommands.add(key) - } - } -} diff --git a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/resolution/ParameterResolver.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/resolution/ParameterResolver.kt deleted file mode 100644 index aa62af0..0000000 --- a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/resolution/ParameterResolver.kt +++ /dev/null @@ -1,5 +0,0 @@ -package `in`.kyle.mcspring.command.resolution - -interface ParameterResolver { - fun resolve(parameter: Class<*>): Any? -} diff --git a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/resolution/SimpleSpringParameterResolver.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/resolution/SimpleSpringParameterResolver.kt deleted file mode 100644 index 0a927b8..0000000 --- a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/resolution/SimpleSpringParameterResolver.kt +++ /dev/null @@ -1,18 +0,0 @@ -package `in`.kyle.mcspring.command.resolution - -import org.springframework.beans.factory.NoSuchBeanDefinitionException -import org.springframework.context.ApplicationContext -import org.springframework.stereotype.Component - -@Component -internal class SimpleSpringParameterResolver( - private val context: ApplicationContext -) : ParameterResolver { - override fun resolve(parameter: Class<*>): Any? { - return try { - context.getBean(parameter) - } catch (e: NoSuchBeanDefinitionException) { - null - } - } -} diff --git a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/event/EventHandlerSupport.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/event/EventHandlerSupport.kt index 8992282..bb14b43 100644 --- a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/event/EventHandlerSupport.kt +++ b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/event/EventHandlerSupport.kt @@ -11,6 +11,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean import org.springframework.context.ApplicationContext import org.springframework.context.ApplicationContextAware import org.springframework.stereotype.Component +import java.lang.reflect.Method @Component @ConditionalOnBean(Plugin::class) @@ -21,10 +22,14 @@ internal class EventHandlerSupport( override fun setApplicationContext(ctx: ApplicationContext) { scanner.scanMethods(EventHandler::class.java).forEach { - val executor = EventExecutor { _: Listener, event: Event -> - AopUtils.invokeJoinpointUsingReflection(it.value, it.key, arrayOf(event)) - } + val executor = makeExecutor(it.key, it.value) eventService.registerEvent(it.key, executor) } } + + private fun makeExecutor(method: Method, obj: Any): EventExecutor { + return EventExecutor { _: Listener, event: Event -> + AopUtils.invokeJoinpointUsingReflection(obj, method, arrayOf(event)) + } + } } diff --git a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/event/EventService.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/event/EventService.kt index 40cd2ec..ab6633a 100644 --- a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/event/EventService.kt +++ b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/event/EventService.kt @@ -19,6 +19,7 @@ internal class EventService( private val plugin: Plugin ) { fun registerEvent(method: Method, executor: EventExecutor) { + require(method.parameters.size == 1) {"Listener can only have 1 parameter: $method"} val handler = method.getAnnotation(EventHandler::class.java) @Suppress("UNCHECKED_CAST") diff --git a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/util/SpringScanner.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/util/SpringScanner.kt index e81889e..5f4e152 100644 --- a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/util/SpringScanner.kt +++ b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/util/SpringScanner.kt @@ -6,9 +6,7 @@ import org.springframework.stereotype.Component import java.lang.reflect.Method @Component -class SpringScanner( - private val context: ApplicationContext -) { +class SpringScanner(private val context: ApplicationContext) { fun scanMethods(vararg annotations: Class): Map { val methods = mutableMapOf() diff --git a/mcspring-api/mcspring-base/src/main/resources/META-INF/spring.factories b/mcspring-api/mcspring-base/src/main/resources/META-INF/spring.factories deleted file mode 100644 index 01adb48..0000000 --- a/mcspring-api/mcspring-base/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1 +0,0 @@ -org.springframework.boot.autoconfigure.EnableAutoConfiguration=in.kyle.mcspring.test.SpringSpigotSupport diff --git a/mcspring-api/mcspring-chat/src/main/kotlin/in/kyle/mcspring/chat/Message.kt b/mcspring-api/mcspring-chat/src/main/kotlin/in/kyle/mcspring/chat/Message.kt index 436a3dc..13a323b 100644 --- a/mcspring-api/mcspring-chat/src/main/kotlin/in/kyle/mcspring/chat/Message.kt +++ b/mcspring-api/mcspring-chat/src/main/kotlin/in/kyle/mcspring/chat/Message.kt @@ -8,14 +8,15 @@ import net.md_5.bungee.api.chat.ClickEvent import net.md_5.bungee.api.chat.HoverEvent import net.md_5.bungee.api.chat.TextComponent import org.bukkit.ChatColor +import org.bukkit.command.CommandSender import org.bukkit.entity.Player -object PlayerSupport { - fun Player.sendMessage(vararg components: TextComponent) { +object CommandSenderSupport { + fun CommandSender.sendMessage(vararg components: TextComponent) { spigot().sendMessage(*components) } - fun Player.sendMessage(lambda: () -> TextComponent) { + fun CommandSender.sendMessage(lambda: () -> TextComponent) { spigot().sendMessage(lambda()) } } @@ -23,7 +24,6 @@ object PlayerSupport { object StringSupport { fun String.toTextComponent(): TextComponent { - val player: Player return TextComponent(translateColorCodes()) } diff --git a/mcspring-api/mcspring-commands-dsl/build.gradle.kts b/mcspring-api/mcspring-commands-dsl/build.gradle.kts index d065866..76cbd19 100644 --- a/mcspring-api/mcspring-commands-dsl/build.gradle.kts +++ b/mcspring-api/mcspring-commands-dsl/build.gradle.kts @@ -1,4 +1,3 @@ dependencies { - implementation(project(":mcspring-api:mcspring-base")) - implementation(project(":mcspring-api:mcspring-common")) + compileOnly(project(":mcspring-api:mcspring-base")) } diff --git a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandParsers.kt b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandParsers.kt index 0b3c2fe..35ce6ac 100644 --- a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandParsers.kt +++ b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandParsers.kt @@ -25,16 +25,18 @@ abstract class BaseParser( protected fun require( hints: List = emptyList(), predicate: (R) -> Boolean, - error: Error + error: Error = {} ) { context.tabCompletions.addAll(hints) - if (returnValue != null && !predicate(returnValue!!)) { + val condition = predicate(returnValue!!) + if (returnValue != null && !condition) { + if (error == {}) { returnValue = null throw RunInvalidBlock() - } - if (stringArg != null && returnValue == null && context.runExecutors) { - error(stringArg) - complete() + } else if (context.runExecutors){ + error(stringArg!!) + complete() + } } } } diff --git a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandUtils.kt b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandUtils.kt index 45defb5..c84d6c6 100644 --- a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandUtils.kt +++ b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandUtils.kt @@ -1,6 +1,6 @@ package `in`.kyle.mcspring.commands.dsl -import `in`.kyle.mcspring.common.commands.CommandMapWrapper +import `in`.kyle.mcspring.commands.dsl.util.CommandMapWrapper import org.bukkit.command.Command import org.bukkit.command.CommandSender import org.bukkit.plugin.Plugin @@ -9,17 +9,25 @@ object CommandUtils { fun register(plugin: Plugin, meta: CommandMeta) { meta.preRegister() - CommandMapWrapper.registerCommand(plugin, object : Command(meta.name, meta.description, meta.usageMessage, meta.aliases) { - override fun execute( - sender: CommandSender, - commandLabel: String, - args: Array - ): Boolean { - val provider = meta.executor.provider - val context = CommandContext(sender, label, args.toList()) - provider(context) - return true - } + CommandMapWrapper.registerCommand(plugin, makeBukkitCommand(meta).apply { + aliases = meta.aliases + description = meta.description + usage = meta.usageMessage + permission = meta.usageMessage.takeIf { it.isNotBlank() } + permissionMessage = meta.permissionMessage.takeIf { it.isNotBlank() } }) } + + private fun makeBukkitCommand(meta: CommandMeta) = object : Command(meta.name) { + override fun execute( + sender: CommandSender, + commandLabel: String, + args: Array + ): Boolean { + val provider = meta.executor.provider + val context = CommandContext(sender, label, args.toList()) + provider(context) + return true + } + } } diff --git a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/Command.kt b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/mcspring/Command.kt similarity index 90% rename from mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/Command.kt rename to mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/mcspring/Command.kt index 68d2076..d87c318 100644 --- a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/command/Command.kt +++ b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/mcspring/Command.kt @@ -1,4 +1,4 @@ -package `in`.kyle.mcspring.command +package `in`.kyle.mcspring.commands.dsl.mcspring /** * Defines a command to be handled by a certain method. diff --git a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/mcspring/CommandDslRegistration.kt b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/mcspring/CommandDslRegistration.kt index 534f814..0f12961 100644 --- a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/mcspring/CommandDslRegistration.kt +++ b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/mcspring/CommandDslRegistration.kt @@ -1,18 +1,20 @@ package `in`.kyle.mcspring.commands.dsl.mcspring -import `in`.kyle.mcspring.command.Command import `in`.kyle.mcspring.commands.dsl.CommandExecutor import `in`.kyle.mcspring.commands.dsl.CommandMeta import `in`.kyle.mcspring.commands.dsl.CommandUtils +import `in`.kyle.mcspring.commands.dsl.util.CommandMapWrapper import `in`.kyle.mcspring.util.SpringScanner import org.bukkit.plugin.Plugin +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean import org.springframework.context.ApplicationContext import org.springframework.context.ApplicationContextAware +import org.springframework.context.annotation.Bean import org.springframework.stereotype.Component import java.lang.reflect.Method @Component -open class CommandDslRegistration( +class CommandDslRegistration( private val plugin: Plugin, private val scanner: SpringScanner ) : ApplicationContextAware { @@ -20,8 +22,13 @@ open class CommandDslRegistration( private val registeredCommands: MutableSet = mutableSetOf() override fun setApplicationContext(ctx: ApplicationContext) { + registerAnnotatedCommands() + registerMetaCommands() + } + + private fun registerAnnotatedCommands() { scanner.scanMethods(Command::class.java).filterKeys { it !in registeredCommands } - .filterKeys { it.returnType == CommandExecutor::class } + .filterKeys { it.returnType == CommandExecutor::class.java } .forEach { (key, value) -> val command = key.getAnnotation(Command::class.java) val meta = CommandMeta().apply { @@ -35,11 +42,17 @@ open class CommandDslRegistration( } CommandUtils.register(plugin, meta) } + } + + private fun registerMetaCommands() { scanner.scanMethods().filterKeys { it !in registeredCommands } - .filterKeys { it.returnType == CommandMeta::class } + .filterKeys { it.returnType == CommandMeta::class.java } .forEach { (key, value) -> val meta = key.invoke(value) as CommandMeta CommandUtils.register(plugin, meta) } } + + @Bean + fun commandMap() = CommandMapWrapper.commandMap } diff --git a/mcspring-api/mcspring-common/src/main/kotlin/in/kyle/mcspring/common/commands/CommandMapWrapper.kt b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/util/CommandMapWrapper.kt similarity index 92% rename from mcspring-api/mcspring-common/src/main/kotlin/in/kyle/mcspring/common/commands/CommandMapWrapper.kt rename to mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/util/CommandMapWrapper.kt index e67c1e7..fd5cc7a 100644 --- a/mcspring-api/mcspring-common/src/main/kotlin/in/kyle/mcspring/common/commands/CommandMapWrapper.kt +++ b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/util/CommandMapWrapper.kt @@ -1,4 +1,4 @@ -package `in`.kyle.mcspring.common.commands +package `in`.kyle.mcspring.commands.dsl.util import org.bukkit.Bukkit import org.bukkit.command.Command diff --git a/mcspring-api/mcspring-common/README.md b/mcspring-api/mcspring-common/README.md deleted file mode 100644 index 900f66a..0000000 --- a/mcspring-api/mcspring-common/README.md +++ /dev/null @@ -1,3 +0,0 @@ -mcspring-common ---- -This doesn't really do a lot. You can probably just ignore it. diff --git a/mcspring-api/mcspring-common/build.gradle.kts b/mcspring-api/mcspring-common/build.gradle.kts deleted file mode 100644 index 6de75a4..0000000 --- a/mcspring-api/mcspring-common/build.gradle.kts +++ /dev/null @@ -1,3 +0,0 @@ -dependencies { - api("org.spigotmc:spigot-api") -} diff --git a/mcspring-api/mcspring-common/src/main/kotlin/in/kyle/mcspring/common/.keep b/mcspring-api/mcspring-common/src/main/kotlin/in/kyle/mcspring/common/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/mcspring-api/mcspring-jar-loader/build.gradle.kts b/mcspring-api/mcspring-jar-loader/build.gradle.kts deleted file mode 100644 index f95e930..0000000 --- a/mcspring-api/mcspring-jar-loader/build.gradle.kts +++ /dev/null @@ -1,3 +0,0 @@ -dependencies { - implementation("org.springframework.boot:spring-boot-loader") -} diff --git a/mcspring-api/mcspring-jar-loader/src/main/kotlin/org/springframework/boot/loader/mcspring/McSpringLoader.kt b/mcspring-api/mcspring-jar-loader/src/main/kotlin/org/springframework/boot/loader/mcspring/McSpringLoader.kt deleted file mode 100644 index 37e9731..0000000 --- a/mcspring-api/mcspring-jar-loader/src/main/kotlin/org/springframework/boot/loader/mcspring/McSpringLoader.kt +++ /dev/null @@ -1,18 +0,0 @@ -package org.springframework.boot.loader.mcspring - -import org.springframework.boot.loader.JarLauncher -import java.net.URL -import java.net.URLClassLoader - -// Packaging is so that it all blends in with the Spring loader -class McSpringLoader : JarLauncher() { - - fun launch(classLoader: ClassLoader?) { - val activeArchives = classPathArchives - val addURL = URLClassLoader::class.java.getDeclaredMethod("addURL", URL::class.java) - addURL.isAccessible = true - activeArchives.forEach { addURL(classLoader, it.url) } - } - - override fun getMainClass() = "" -} diff --git a/mcspring-api/mcspring-test/build.gradle.kts b/mcspring-api/mcspring-test/build.gradle.kts deleted file mode 100644 index 0761443..0000000 --- a/mcspring-api/mcspring-test/build.gradle.kts +++ /dev/null @@ -1,9 +0,0 @@ -dependencies { - api("org.springframework.boot:spring-boot-test") - api("org.springframework.boot:spring-boot-starter-test") - api(project(":mcspring-api:mcspring-base")) - implementation("org.springframework.boot:spring-boot-test") - implementation("org.springframework.boot:spring-boot-starter-test") - implementation("org.assertj:assertj-core:3.15.0") - implementation("org.junit.jupiter:junit-jupiter-engine:5.3.2") -} diff --git a/mcspring-api/mcspring-test/src/main/kotlin/in/kyle/mcspring/test/MCSpringTest.kt b/mcspring-api/mcspring-test/src/main/kotlin/in/kyle/mcspring/test/MCSpringTest.kt deleted file mode 100644 index 7485ded..0000000 --- a/mcspring-api/mcspring-test/src/main/kotlin/in/kyle/mcspring/test/MCSpringTest.kt +++ /dev/null @@ -1,15 +0,0 @@ -package `in`.kyle.mcspring.test - -import org.junit.jupiter.api.extension.ExtendWith -import org.mockito.junit.jupiter.MockitoExtension -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.test.context.junit.jupiter.SpringExtension -import java.lang.annotation.Inherited - -@Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) -@Retention(AnnotationRetention.RUNTIME) -@Inherited -@SpringBootTest(classes = [SpringSpigotSupport::class]) -@ExtendWith(SpringExtension::class, MockitoExtension::class) -annotation class MCSpringTest - diff --git a/mcspring-api/mcspring-test/src/main/kotlin/in/kyle/mcspring/test/SpringSpigotSupport.kt b/mcspring-api/mcspring-test/src/main/kotlin/in/kyle/mcspring/test/SpringSpigotSupport.kt deleted file mode 100644 index db92429..0000000 --- a/mcspring-api/mcspring-test/src/main/kotlin/in/kyle/mcspring/test/SpringSpigotSupport.kt +++ /dev/null @@ -1,13 +0,0 @@ -package `in`.kyle.mcspring.test - -import org.springframework.boot.SpringBootConfiguration -import org.springframework.boot.autoconfigure.EnableAutoConfiguration -import org.springframework.context.annotation.ComponentScan - -@SpringBootConfiguration -@EnableAutoConfiguration -@ComponentScan( - basePackageClasses = [SpringSpigotSupport::class], - basePackages = ["in.kyle.mcspring"] -) -internal open class SpringSpigotSupport diff --git a/mcspring-api/mcspring-test/src/main/kotlin/in/kyle/mcspring/test/command/TestCommandExecutor.kt b/mcspring-api/mcspring-test/src/main/kotlin/in/kyle/mcspring/test/command/TestCommandExecutor.kt deleted file mode 100644 index 2e8b154..0000000 --- a/mcspring-api/mcspring-test/src/main/kotlin/in/kyle/mcspring/test/command/TestCommandExecutor.kt +++ /dev/null @@ -1,39 +0,0 @@ -package `in`.kyle.mcspring.test.command - -import org.bukkit.command.CommandSender -import org.bukkit.entity.Player -import org.mockito.ArgumentMatchers.anyString -import org.mockito.Mockito.doAnswer -import org.mockito.Mockito.mock -import org.springframework.stereotype.Component - -@Component -class TestCommandExecutor( -// private val registration: TestCommandRegistration -) { - - fun makeTestPlayer(): Pair> { - val sender = mock(Player::class.java) - val messages = mutableListOf() - doAnswer { messages.add(it.getArgument(0)); null } - .`when`(sender).sendMessage(anyString()) - return Pair(sender, messages) - } - - fun run(command: String): List { - val (sender, messages) = makeTestPlayer() - run(sender, command) - return messages - } - - fun run(sender: CommandSender, command: String) { - val parts = command.trim().split(" ").filter { it.isNotBlank() }.toMutableList() - if (parts.isNotEmpty()) { - val label = parts[0] - val args = parts.subList(1, parts.size) -// registration.run(label, sender, label, args.toTypedArray()) - } else { - throw RuntimeException("Empty command") - } - } -} diff --git a/mcspring-api/mcspring-test/src/main/kotlin/in/kyle/mcspring/test/command/TestCommandRegistration.kt b/mcspring-api/mcspring-test/src/main/kotlin/in/kyle/mcspring/test/command/TestCommandRegistration.kt deleted file mode 100644 index 7697eb3..0000000 --- a/mcspring-api/mcspring-test/src/main/kotlin/in/kyle/mcspring/test/command/TestCommandRegistration.kt +++ /dev/null @@ -1,50 +0,0 @@ -package `in`.kyle.mcspring.test.command - -import `in`.kyle.mcspring.command.Command -import `in`.kyle.mcspring.command.registration.CommandRegistration -import `in`.kyle.mcspring.command.execution.SimpleMethodInjection -import org.bukkit.command.CommandSender -import org.springframework.stereotype.Component -import java.lang.reflect.Method -import java.util.* -import kotlin.reflect.KFunction - -typealias CommandSig = (CommandSender, String, Array) -> Unit - -@Component -class TestCommandRegistration( - private val injection: SimpleMethodInjection -// private val commandResolvers: Set -) : CommandRegistration { - - private val commandExecutors: MutableMap = HashMap() - - override fun register(command: Command, method: Method, obj: Any) { -// @Suppress("UNCHECKED_CAST") -// val executor = makeExecutor(method.kotlinFunction as KFunction, obj) -// command.aliases.plus(command.value).forEach { commandExecutors[it] = executor } - } - -// private fun makeExecutor(e: KFunction, obj: Any): CommandSig { -// return { sender: CommandSender, label: String, args: Array -> -// val temp = CommandResolver.Command(sender, args.toList(), label) -// val miscResolvers = injection.makeResolvers(listOf(sender)) -// val contextResolvers = commandResolvers.map { it.makeResolver(temp) } -// -// val parameterTypes = e.javaMethod!!.parameterTypes.toList() -// val parameters = injection.getParameters(parameterTypes, contextResolvers.plus(miscResolvers)) -// val result = e.call(obj, *parameters) -// if (result !is Unit) { -// sender.sendMessage(result.toString()) -// } -// } -// } -// -// fun run(command: String, sender: CommandSender, label: String, args: Array) { -// if (command in commandExecutors) { -// commandExecutors[command]!!(sender, label, args) -// } else { -// throw RuntimeException("Command $command is not registered. Make sure to @Import it") -// } -// } -} diff --git a/mcspring-build/mcspring-archetype/build.gradle.kts b/mcspring-build/mcspring-archetype/build.gradle.kts deleted file mode 100644 index 5741968..0000000 --- a/mcspring-build/mcspring-archetype/build.gradle.kts +++ /dev/null @@ -1,3 +0,0 @@ -dependencies { - compile(project(":mcspring-build:mcspring-starter:mcspring-plugin-manager")) -} diff --git a/mcspring-build/mcspring-archetype/pom.xml b/mcspring-build/mcspring-archetype/pom.xml deleted file mode 100644 index 38fdcc4..0000000 --- a/mcspring-build/mcspring-archetype/pom.xml +++ /dev/null @@ -1,76 +0,0 @@ - - - - mcspring-build - in.kyle.mcspring - 0.0.9 - - - 4.0.0 - - mcspring-archetype - - - - in.kyle.mcspring - mcspring-plugin-manager - ${project.parent.version} - - - - - - - org.apache.maven.archetype - archetype-packaging - 3.1.2 - - - - - src/main/resources - true - - archetype-resources/pom.xml - - - - src/main/resources - false - - archetype-resources/pom.xml - - - - - - org.apache.maven.plugins - maven-resources-plugin - 3.1.0 - - \ - - - - maven-dependency-plugin - - - prepare-package - - copy-dependencies - - - - ${project.build.outputDirectory}/archetype-resources/spigot/plugins - - mcspring-plugin-manager - true - - - - - - - diff --git a/mcspring-build/mcspring-archetype/src/main/resources/META-INF/maven/archetype-metadata.xml b/mcspring-build/mcspring-archetype/src/main/resources/META-INF/maven/archetype-metadata.xml deleted file mode 100644 index 8c27f28..0000000 --- a/mcspring-build/mcspring-archetype/src/main/resources/META-INF/maven/archetype-metadata.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - src/main/java - - **/*.java - - - - src/main/resources - - ** - - - - spigot - - bukkit.yml - eula.txt - server.properties - spigot.yml - README.md - plugins/** - - - - diff --git a/mcspring-build/mcspring-archetype/src/main/resources/archetype-resources/pom.xml b/mcspring-build/mcspring-archetype/src/main/resources/archetype-resources/pom.xml deleted file mode 100644 index 0fbad3d..0000000 --- a/mcspring-build/mcspring-archetype/src/main/resources/archetype-resources/pom.xml +++ /dev/null @@ -1,34 +0,0 @@ - - 4.0.0 - - - mcspring-starter - ${project.groupId} - ${project.version} - - - \${groupId} - \${artifactId} - \${version} - jar - - - 1.15.1-R0.1-SNAPSHOT - - - - - - org.projectlombok - lombok - 1.18.12 - provided - - - ${project.groupId} - mcspring-subcommands - - - diff --git a/mcspring-build/mcspring-archetype/src/main/resources/archetype-resources/spigot/README.md b/mcspring-build/mcspring-archetype/src/main/resources/archetype-resources/spigot/README.md deleted file mode 100644 index 91612d4..0000000 --- a/mcspring-build/mcspring-archetype/src/main/resources/archetype-resources/spigot/README.md +++ /dev/null @@ -1,16 +0,0 @@ -This is a simple Spigot installation. - -To start the server in Intellij: -1. Go to `Edit Run Configurations` (next to the run button in the dropdown) -2. Add a new JAR run configuration by clicking the plus button and selecting `JAR Application` -3. Set the `Path to JAR` as the downloaded spigot.jar. If you do not have a spigot.jar, run a maven install. -4. Add the VM Option `-DIReallyKnowWhatIAmDoingISwear` -5. Set the `Working Directory` as the spigot folder - -You will then be able to run spigot by clicking the run button in IJ. - -Further Notes: -* You are also able to launch Spigot in debug mode and set breakpoints. -* Running a maven install will automatically copy the latest plugin jar into your plugins folder. -* Once you perform a new install, restart the server to get the latest version. McSpring does not - yet support reloading altered jars. diff --git a/mcspring-build/mcspring-archetype/src/main/resources/archetype-resources/src/main/java/ExampleCommand.java b/mcspring-build/mcspring-archetype/src/main/resources/archetype-resources/src/main/java/ExampleCommand.java deleted file mode 100644 index 4e99434..0000000 --- a/mcspring-build/mcspring-archetype/src/main/resources/archetype-resources/src/main/java/ExampleCommand.java +++ /dev/null @@ -1,30 +0,0 @@ -package ${groupId}; - -import org.springframework.stereotype.Component; - -import javax.annotation.PostConstruct; - -import in.kyle.mcspring.command.Command; - -// Remember to annotate Spring beans with @Component -// If you forget this, your class will not run. -@Component -class ExampleCommand { - // Do not extend JavaPlugin, you will regret it - // To get an instance of Plugin, have Spring inject it. - - // Use this in-place of an onEnable method. You can make as many of these methods as you like. - // for onDisable see the @PreDestroy annotation. - @PostConstruct - void onEnable() { - - } - - // Commands will be automatically set up. Do not create a plugin.yml - // The return value of a command method will be sent to the CommandSender using Object::toString - // void methods simplly will not send a message - @Command("test") - String test() { - return "The command works!"; - } -} diff --git a/mcspring-build/mcspring-maven-plugin/README.md b/mcspring-build/mcspring-maven-plugin/README.md deleted file mode 100644 index 35a0341..0000000 --- a/mcspring-build/mcspring-maven-plugin/README.md +++ /dev/null @@ -1,14 +0,0 @@ -## MC-Spring Auto Generation Plugin for Maven - -### What does this do? -This plugin removes the need to create a plugin.yml and main class. -At compile time we resolve all dependencies that your plugin uses. -Those dependencies will be inserted into the plugin.yml, along with -various maven properties that you can define in the pom.xml. - -### How does it work? -Step 1: Resolve all maven project dependencies and find the corresponding -jars associated with them to add to a class loader. Load all classes -from the jars and check for the @PluginDepend annotation provided by MC-Spring. - -Step 2: Profit \ No newline at end of file diff --git a/mcspring-build/mcspring-maven-plugin/build.gradle.kts b/mcspring-build/mcspring-maven-plugin/build.gradle.kts deleted file mode 100644 index bbebf50..0000000 --- a/mcspring-build/mcspring-maven-plugin/build.gradle.kts +++ /dev/null @@ -1,11 +0,0 @@ -dependencies { - compile("org.yaml:snakeyaml:1.26") - compile("org.apache.maven:maven-plugin-api:3.3.9") - compile("org.apache.maven:maven-core:3.3.9") - compile("org.apache.maven:maven-project:3.0-alpha-2") - compile(project(":mcspring-api:mcspring-base")) - testCompile("org.apache.maven.plugin-testing:maven-plugin-testing-harness:3.3.0") - testCompile("org.apache.maven:maven-compat:3.6.3") - compileOnly("org.apache.maven.plugin-tools:maven-plugin-annotations:3.6.0") - compileOnly("org.projectlombok:lombok:1.18.12") -} diff --git a/mcspring-build/mcspring-maven-plugin/pom.xml b/mcspring-build/mcspring-maven-plugin/pom.xml deleted file mode 100644 index d085aea..0000000 --- a/mcspring-build/mcspring-maven-plugin/pom.xml +++ /dev/null @@ -1,87 +0,0 @@ - - - - mcspring-build - in.kyle.mcspring - 0.0.9 - - - 4.0.0 - mcspring-maven-plugin - maven-plugin - - ${project.artifactId} - - - 3.3.9 - - - - - org.apache.maven - maven-plugin-api - ${maven.version} - - - org.apache.maven.plugin-tools - maven-plugin-annotations - 3.6.0 - provided - - - org.yaml - snakeyaml - 1.26 - - - org.apache.maven - maven-core - 3.3.9 - - - org.apache.maven - maven-project - 3.0-alpha-2 - - - in.kyle.mcspring - mcspring-base - ${project.version} - - - org.apache.maven.plugin-testing - maven-plugin-testing-harness - 3.3.0 - test - - - org.apache.maven - maven-compat - 3.6.3 - test - - - org.projectlombok - lombok - - - - - - - org.apache.maven.plugins - maven-plugin-plugin - 3.6.0 - - - org.apache.maven.plugins - maven-compiler-plugin - - 8 - 8 - - - - - diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/GenerateFilesMojo.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/GenerateFilesMojo.java deleted file mode 100644 index 80b7f30..0000000 --- a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/GenerateFilesMojo.java +++ /dev/null @@ -1,118 +0,0 @@ -package in.kyle.mcspring.autogenerator; - -import in.kyle.mcspring.autogenerator.scan.ProjectSourceAnnotationScanner; -import in.kyle.mcspring.autogenerator.util.ClassLoadingUtility; -import in.kyle.mcspring.autogenerator.util.MainClassUtilities; -import org.apache.maven.artifact.Artifact; -import org.apache.maven.plugin.AbstractMojo; -import org.apache.maven.plugins.annotations.Component; -import org.apache.maven.plugins.annotations.LifecyclePhase; -import org.apache.maven.plugins.annotations.Mojo; -import org.apache.maven.plugins.annotations.ResolutionScope; -import org.apache.maven.project.MavenProject; - -import java.io.File; -import java.net.URLClassLoader; -import java.util.Arrays; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -import lombok.SneakyThrows; - - -@Mojo(name = "generate-files", defaultPhase = LifecyclePhase.PROCESS_CLASSES, - requiresDependencyCollection = ResolutionScope.COMPILE_PLUS_RUNTIME, - requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME) -public class GenerateFilesMojo extends AbstractMojo { - - private static final List VALID_SCOPES = Arrays.asList("provided", "compile", "runtime"); - @Component - private MavenProject project; - private URLClassLoader fullyQualifiedClassLoader; - - @SneakyThrows - public void execute() { - initializeClassLoader(); - addGeneratedSourcesDirectory(); - preparePluginYml(); - preparePluginMainClass(); - } - - //Initializes a class loader with all maven dependency classes and project classes. - private void initializeClassLoader() { - ClassLoader parent = getClass().getClassLoader(); - File projectSource = getSourceClassesFolder(); - List artifactFiles = getDependencyArtifacts().stream() - .map(Artifact::getFile) - .collect(Collectors.toList()); - this.fullyQualifiedClassLoader = ClassLoadingUtility.createClassLoader(parent, artifactFiles, projectSource); - } - - private File getSourcesOutputDirectory() { - return new File(getGeneratedSourcesFolder(), "mc-spring/"); - } - - private void addGeneratedSourcesDirectory() { - File output = getSourcesOutputDirectory(); - if (!output.exists()) { - output.mkdirs(); - } - project.addCompileSourceRoot(output.getPath()); - } - - private void preparePluginYml() { - getLog().info("Scanning for project dependencies in qualifying scope"); - Set artifacts = getDependencyArtifacts(); - getLog().info(String.format("Dependency scan complete. Found %d dependencies", artifacts.size())); - ProjectDependencyResolver resolver = new ProjectDependencyResolver(fullyQualifiedClassLoader, getSourceClassesFolder(), artifacts); - PluginYamlAttributes attributes = new PluginYamlAttributes(project, resolver); - attributes.loadAttributes(); - getLog().info("Finished obtaining data for plugin.yml"); - getLog().info("----------------------------------------------------------------"); - attributes.getAttributes().forEach((key, data) -> getLog().info(key + ": " + data.toString())); - getLog().info("----------------------------------------------------------------"); - getLog().info("Writing plugin.yml to generated-sources"); - File pluginFile = new File(getSourcesOutputDirectory(), "plugin.yml"); - attributes.writeToFile(pluginFile); - getLog().info("Write completed"); - } - - private void preparePluginMainClass() { - getLog().info("Scanning project sources for spring annotations"); - ProjectSourceAnnotationScanner scanner = new ProjectSourceAnnotationScanner(fullyQualifiedClassLoader, getSourceClassesFolder()); - scanner.findPackagesThatUseSpring(); - Set packages = scanner.getPackagesThatUseSpring(); - getLog().info(String.format("Scan complete. Found %d packages with spring annotation", packages.size())); - getLog().info("Preparing to generate main class"); - writePluginMain(packages); - getLog().info("Default main class has been added to generated-sources"); - getLog().info("Auto generation process complete"); - } - - private void writePluginMain(Set packages) { - String mainClass = MainClassUtilities.getMainClassLocation(project); - File destination = new File(getSourcesOutputDirectory(), mainClass.replace(".", "/").concat(".java")); - if (!destination.getParentFile().exists()) { - destination.getParentFile().mkdirs(); - } - PluginMainClassGenerator generator = new PluginMainClassGenerator(project, packages, destination); - generator.generate(); - } - - private File getSourceClassesFolder() { - return new File(project.getBasedir(), "/target/classes/"); - } - - private File getGeneratedSourcesFolder() { - return new File(project.getBasedir(), "/target/generated-sources/"); - } - - @SuppressWarnings("unchecked") - private Set getDependencyArtifacts() { - Set artifacts = project.getArtifacts(); - return artifacts.stream() - .filter(artifact -> VALID_SCOPES.contains(artifact.getScope())) - .collect(Collectors.toSet()); - } -} diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginMainClassGenerator.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginMainClassGenerator.java deleted file mode 100644 index 37556df..0000000 --- a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginMainClassGenerator.java +++ /dev/null @@ -1,52 +0,0 @@ -package in.kyle.mcspring.autogenerator; - -import in.kyle.mcspring.autogenerator.util.MainClassUtilities; -import lombok.RequiredArgsConstructor; -import lombok.SneakyThrows; -import org.apache.maven.project.MavenProject; - -import java.io.*; -import java.util.Set; -import java.util.stream.Collectors; - -@RequiredArgsConstructor -public class PluginMainClassGenerator { - - private final MavenProject project; - private final Set packages; - private final File destination; - - public void generate() { - String templateContent = getTemplateContent(); - templateContent = templateContent.replace("{name}", MainClassUtilities.getMainClassName(project)); - templateContent = templateContent.replace("{scans}", createPackageScanList()); - write(templateContent); - } - - private String createPackageScanList() { - return packages.stream() - .map(s -> String.format("\"%s\"", s)) - .collect(Collectors.joining(",\n ")); - } - - @SneakyThrows - private void write(String completedTemplate) { - FileWriter writer = new FileWriter(destination); - writer.write(completedTemplate); - writer.flush(); - writer.close(); - } - - private String getTemplateContent() { - InputStream resource = PluginMainClassGenerator.class.getResourceAsStream("/MainTemplate.java"); - InputStreamReader inputStreamReader = new InputStreamReader(resource); - BufferedReader bufferedReader = new BufferedReader(inputStreamReader); - StringBuilder builder = new StringBuilder(); - builder.append("package ") - .append(MainClassUtilities.getMainClassPackage(project)) - .append(";") - .append("\n"); - bufferedReader.lines().forEach(line -> builder.append(line).append("\n")); - return builder.toString(); - } -} diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginYamlAttributes.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginYamlAttributes.java deleted file mode 100644 index a890337..0000000 --- a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/PluginYamlAttributes.java +++ /dev/null @@ -1,66 +0,0 @@ -package in.kyle.mcspring.autogenerator; - -import in.kyle.mcspring.annotation.PluginDepend; -import in.kyle.mcspring.autogenerator.util.MainClassUtilities; -import lombok.SneakyThrows; -import org.apache.maven.project.MavenProject; -import org.yaml.snakeyaml.Yaml; - -import java.io.File; -import java.io.FileWriter; -import java.util.*; - -public class PluginYamlAttributes { - - private final Map attributes = new LinkedHashMap<>(); - private final List softDependencies = new ArrayList<>(); - private final List requiredDependencies = new ArrayList<>(); - private final MavenProject project; - private final ProjectDependencyResolver resolver; - - PluginYamlAttributes(MavenProject project, ProjectDependencyResolver resolver) { - this.project = project; - this.resolver = resolver; - } - - public Map getAttributes() { - return Collections.unmodifiableMap(attributes); - } - - public void loadAttributes() { - attributes.put("name", project.getName()); - attributes.put("main", MainClassUtilities.getMainClassLocation(project)); - attributes.put("version", project.getVersion()); - attributes.put("description", project.getDescription()); - loadDependencies(); - } - - private void loadDependencies() { - List list = resolver.resolveAllDependencies(); - loadSoftDependencies(list); - loadRequiredDependencies(list); - attributes.put("softdepend", softDependencies); - attributes.put("depend", requiredDependencies); - } - - private void loadSoftDependencies(List dependencies) { - dependencies.stream().filter(PluginDepend::soft) - .flatMap(pluginDepend -> Arrays.stream(pluginDepend.plugins())) - .forEach(softDependencies::add); - } - - private void loadRequiredDependencies(List dependencies) { - dependencies.stream().filter(pluginDepend -> !pluginDepend.soft()) - .flatMap(pluginDepend -> Arrays.stream(pluginDepend.plugins())) - .forEach(requiredDependencies::add); - } - - - @SneakyThrows - public void writeToFile(File destination) { - FileWriter writer = new FileWriter(destination); - Yaml yaml = new Yaml(); - //Pretty sure #dump closes the writer automatically - yaml.dump(attributes, writer); - } -} diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/ProjectDependencyResolver.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/ProjectDependencyResolver.java deleted file mode 100644 index 5e5db50..0000000 --- a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/ProjectDependencyResolver.java +++ /dev/null @@ -1,49 +0,0 @@ -package in.kyle.mcspring.autogenerator; - -import in.kyle.mcspring.annotation.PluginDepend; -import in.kyle.mcspring.autogenerator.scan.JarFileDependencyScanner; -import in.kyle.mcspring.autogenerator.scan.ProjectDependencyScanner; -import lombok.SneakyThrows; -import org.apache.maven.artifact.Artifact; - -import java.io.File; -import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.jar.JarFile; -import java.util.stream.Collectors; -import lombok.val; - -public class ProjectDependencyResolver { - - private final URLClassLoader fullyQualifiedClassLoader; - private final File sourcesFolder; - private final List jars; - - public ProjectDependencyResolver(URLClassLoader fullyQualifiedClassLoader, File sourcesFolder, Collection dependencies) { - this.fullyQualifiedClassLoader = fullyQualifiedClassLoader; - this.sourcesFolder = sourcesFolder; - this.jars = dependencies.stream().map(Artifact::getFile).collect(Collectors.toList()); - } - - @SneakyThrows - private List getJarFiles() { - return jars.stream().map(this::getAsJarFile).collect(Collectors.toList()); - } - - @SneakyThrows - private JarFile getAsJarFile(File file) { - return new JarFile(file); - } - - public List resolveAllDependencies() { - List dependencies = new ArrayList<>(); - List jars = getJarFiles(); - val jarFileDependencyScanner = new JarFileDependencyScanner(fullyQualifiedClassLoader, jars); - val projectDependencyScanner = new ProjectDependencyScanner(fullyQualifiedClassLoader, sourcesFolder); - dependencies.addAll(jarFileDependencyScanner.getScannedAnnotations()); - dependencies.addAll(projectDependencyScanner.getScannedAnnotations()); - return dependencies; - } -} diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/JarFileDependencyScanner.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/JarFileDependencyScanner.java deleted file mode 100644 index 8f6fb1b..0000000 --- a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/JarFileDependencyScanner.java +++ /dev/null @@ -1,59 +0,0 @@ -package in.kyle.mcspring.autogenerator.scan; - -import in.kyle.mcspring.annotation.PluginDepend; -import lombok.RequiredArgsConstructor; -import lombok.SneakyThrows; - -import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.List; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; - -@RequiredArgsConstructor -public class JarFileDependencyScanner implements PluginDependAnnotationScanner { - - private final URLClassLoader classLoader; - private final List jarFiles; - - @Override - public List getScannedAnnotations() { - List list = new ArrayList<>(); - jarFiles.forEach(jarFile -> list.addAll(findJarFileDependencies(jarFile))); - return list; - } - - @SneakyThrows - private List findJarFileDependencies(JarFile jarFile) { - List dependList = new ArrayList<>(); - Enumeration enumeration = jarFile.entries(); - while(enumeration.hasMoreElements()) { - JarEntry entry = enumeration.nextElement(); - if(isClassFile(entry)) { - String className = getClassName(entry); - addDependencyIfPresent(className, dependList); - } - } - return dependList; - } - - private void addDependencyIfPresent(String className, List destination) { - try { - Class clazz = classLoader.loadClass(className); - if(clazz.isAnnotationPresent(PluginDepend.class)) { - destination.add(clazz.getAnnotation(PluginDepend.class)); - } - } catch (Throwable ignored) { } - } - - private boolean isClassFile(JarEntry entry) { - return entry.getName().endsWith(".class"); - } - - private String getClassName(JarEntry entry) { - String fullName = entry.getName(); - String excludeClassExtension = fullName.substring(0, fullName.length() - 6); - return excludeClassExtension.replace("/", "."); - } -} \ No newline at end of file diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/PluginDependAnnotationScanner.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/PluginDependAnnotationScanner.java deleted file mode 100644 index d4be184..0000000 --- a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/PluginDependAnnotationScanner.java +++ /dev/null @@ -1,11 +0,0 @@ -package in.kyle.mcspring.autogenerator.scan; - -import in.kyle.mcspring.annotation.PluginDepend; - -import java.util.List; - -public interface PluginDependAnnotationScanner { - - List getScannedAnnotations(); - -} diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/ProjectDependencyScanner.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/ProjectDependencyScanner.java deleted file mode 100644 index ae1cc5f..0000000 --- a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/ProjectDependencyScanner.java +++ /dev/null @@ -1,46 +0,0 @@ -package in.kyle.mcspring.autogenerator.scan; - -import in.kyle.mcspring.annotation.PluginDepend; -import in.kyle.mcspring.autogenerator.util.FileUtility; -import lombok.RequiredArgsConstructor; -import lombok.SneakyThrows; - -import java.io.File; -import java.net.URLClassLoader; -import java.util.List; -import java.util.stream.Collectors; - -@RequiredArgsConstructor -public class ProjectDependencyScanner implements PluginDependAnnotationScanner { - - private final URLClassLoader classLoader; - private final File sourcesFolder; - - @SneakyThrows - @Override - public List getScannedAnnotations() { - return FileUtility.getFilesInDirectory(sourcesFolder) - .stream() - .filter(this::isClass) - .map(this::getClassName) - .map(this::loadClass) - .filter(clazz -> clazz.isAnnotationPresent(PluginDepend.class)) - .map(clazz -> clazz.getAnnotation(PluginDepend.class)) - .collect(Collectors.toList()); - } - - @SneakyThrows - private Class loadClass(String className) { - return classLoader.loadClass(className); - } - - private String getClassName(File classFile) { - String fullPath = classFile.getPath(); - String classNameWithPackage = fullPath.replace(sourcesFolder.getPath(), "").replace("\\", "."); - return classNameWithPackage.substring(1, classNameWithPackage.length() - 6); - } - - private boolean isClass(File file) { - return file.getName().endsWith(".class"); - } -} diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/ProjectSourceAnnotationScanner.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/ProjectSourceAnnotationScanner.java deleted file mode 100644 index d16c0b3..0000000 --- a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/scan/ProjectSourceAnnotationScanner.java +++ /dev/null @@ -1,69 +0,0 @@ -package in.kyle.mcspring.autogenerator.scan; - -import in.kyle.mcspring.autogenerator.util.FileUtility; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Component; -import org.springframework.stereotype.Controller; -import org.springframework.stereotype.Service; - -import java.io.File; -import java.lang.annotation.Annotation; -import java.net.URLClassLoader; -import java.util.*; -import java.util.stream.Collectors; - - -//Objective: Scan all classes in the provider folder and check for spring annotations. -//If present, get the class package and add it to a list for springs package scan at runtime -@RequiredArgsConstructor -public class ProjectSourceAnnotationScanner { - - private static final List> SPRING_ANNOTATIONS = Arrays.asList( - Service.class, - Controller.class, - Component.class - ); - - private final URLClassLoader fullyQualifiedClassLoader; - private final File classesFolder; - private final Set packages = new HashSet<>(); - - public Set getPackagesThatUseSpring() { - return Collections.unmodifiableSet(packages); - } - - public void findPackagesThatUseSpring() { - List classFiles = getClassFiles(); - classFiles.forEach(this::addPackageIfFound); - } - - private void addPackageIfFound(File classFile) { - String className = getClassName(classFile); - try { - Class clazz = fullyQualifiedClassLoader.loadClass(className); - if(hasSpringAnnotation(clazz)) { - packages.add(clazz.getPackage().getName()); - } - } catch (Throwable ignored){ - //Proper error handling here. - } - } - - - private String getClassName(File classFile) { - String fullPath = classFile.getPath(); - String classNameWithPackage = fullPath.replace(classesFolder.getPath(), "").replace("\\", "."); - return classNameWithPackage.substring(1, classNameWithPackage.length() - 6); - } - - private List getClassFiles() { - return FileUtility.getFilesInDirectory(classesFolder) - .stream() - .filter(file -> file.getName().endsWith(".class")) - .collect(Collectors.toList()); - } - - private boolean hasSpringAnnotation(Class clazz) { - return SPRING_ANNOTATIONS.stream().anyMatch(clazz::isAnnotationPresent); - } -} diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/util/ClassLoadingUtility.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/util/ClassLoadingUtility.java deleted file mode 100644 index 916bcd7..0000000 --- a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/util/ClassLoadingUtility.java +++ /dev/null @@ -1,30 +0,0 @@ -package in.kyle.mcspring.autogenerator.util; - -import lombok.SneakyThrows; - -import java.io.File; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -public class ClassLoadingUtility { - - public static List getJarFileURLs(List jarFiles) { - return jarFiles.stream().map(ClassLoadingUtility::createJarFileURL).collect(Collectors.toList()); - } - - @SneakyThrows - private static URL createJarFileURL(File jarFile) { - return new URL("jar:file:" + jarFile.getPath() + "!/"); - } - - //Creates a class loader with all jar file classes and classes in the sourceFolder parameter. - @SneakyThrows - public static URLClassLoader createClassLoader(ClassLoader parent, List jarFiles, File sourceFolder) { - List urls = new ArrayList<>(getJarFileURLs(jarFiles)); - urls.add(sourceFolder.toURI().toURL()); - return new URLClassLoader(urls.toArray(new URL[0]), parent); - } -} diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/util/FileUtility.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/util/FileUtility.java deleted file mode 100644 index 706b018..0000000 --- a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/util/FileUtility.java +++ /dev/null @@ -1,23 +0,0 @@ -package in.kyle.mcspring.autogenerator.util; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -public class FileUtility { - - public static List getFilesInDirectory(File directory) { - List files = new ArrayList<>(); - File[] filesInDirectory = directory.listFiles(); - if (filesInDirectory != null) { - for (File file : filesInDirectory) { - if (file.isDirectory()) { - files.addAll(getFilesInDirectory(file)); - } else { - files.add(file); - } - } - } - return files; - } -} diff --git a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/util/MainClassUtilities.java b/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/util/MainClassUtilities.java deleted file mode 100644 index ca9c378..0000000 --- a/mcspring-build/mcspring-maven-plugin/src/main/java/in/kyle/mcspring/autogenerator/util/MainClassUtilities.java +++ /dev/null @@ -1,21 +0,0 @@ -package in.kyle.mcspring.autogenerator.util; - -import org.apache.maven.project.MavenProject; - -public class MainClassUtilities { - - public static final String ROOT_PACKAGE = "org.springframework.boot.loader"; - - public static String getMainClassName(MavenProject project) { - return project.getArtifactId().replace("-", ""); - } - - public static String getMainClassLocation(MavenProject project) { - return getMainClassPackage(project) + "." + getMainClassName(project); - } - - public static String getMainClassPackage(MavenProject project) { - String groupId = project.getGroupId().replace("-", ""); - return ROOT_PACKAGE + "." + groupId; - } -} diff --git a/mcspring-build/mcspring-maven-plugin/src/main/resources/MainTemplate.java b/mcspring-build/mcspring-maven-plugin/src/main/resources/MainTemplate.java deleted file mode 100644 index 8ce5644..0000000 --- a/mcspring-build/mcspring-maven-plugin/src/main/resources/MainTemplate.java +++ /dev/null @@ -1,35 +0,0 @@ -import org.springframework.boot.autoconfigure.SpringBootApplication; - -// @formatter::off -import org.bukkit.plugin.java.JavaPlugin; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.loader.mcspring.McSpringLoader; - -import in.kyle.mcspring.SpringPlugin; -// @formatter::on - -public class {name} extends JavaPlugin { - - private McSpringLoader loader; - - public void onEnable() { - try { - new McSpringLoader().launch(getClassLoader()); - SpringPlugin.setup(this, MainPluginConfig.class); - } catch (Exception ignored){ - getLogger().warning("MCSpring Failed to load " + getName()); - // error will be logged by Spring - } - } - - public void onDisable() { - SpringPlugin.teardown(this); - //if (loader != null) loader.close(); - loader = null; - } - - @SpringBootApplication(scanBasePackages = {{scans}}) - static class MainPluginConfig { - - } -} diff --git a/mcspring-build/mcspring-maven-plugin/src/test/java/in/kyle/mcspring/autogenerator/TestGenerator.java b/mcspring-build/mcspring-maven-plugin/src/test/java/in/kyle/mcspring/autogenerator/TestGenerator.java deleted file mode 100644 index 618f528..0000000 --- a/mcspring-build/mcspring-maven-plugin/src/test/java/in/kyle/mcspring/autogenerator/TestGenerator.java +++ /dev/null @@ -1,19 +0,0 @@ -package in.kyle.mcspring.autogenerator; - -import org.apache.maven.plugin.testing.AbstractMojoTestCase; - -public class TestGenerator extends AbstractMojoTestCase { - - @Override - protected void setUp() throws Exception { - super.setUp(); - } - - public void testMojoGoal() throws Exception { -// File testPom = new File(getBasedir(), -// "src/test/resources/unit/basic-test/basic-test-plugin-config.xml"); -// GenerateFilesMojo mojo = (GenerateFilesMojo) lookupEmptyMojo("generate-files", testPom); -// assertNotNull(mojo); -// mojo.execute(); - } -} diff --git a/mcspring-build/mcspring-maven-plugin/src/test/resources/unit/basic-test/basic-test-plugin-config.xml b/mcspring-build/mcspring-maven-plugin/src/test/resources/unit/basic-test/basic-test-plugin-config.xml deleted file mode 100644 index d431930..0000000 --- a/mcspring-build/mcspring-maven-plugin/src/test/resources/unit/basic-test/basic-test-plugin-config.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - in.kyle.mcspring - mcspring-maven-plugin - - - - diff --git a/mcspring-build/mcspring-plugin-layout/build.gradle.kts b/mcspring-build/mcspring-plugin-layout/build.gradle.kts deleted file mode 100644 index 72900cc..0000000 --- a/mcspring-build/mcspring-plugin-layout/build.gradle.kts +++ /dev/null @@ -1,4 +0,0 @@ -dependencies { - compile("org.springframework.boot:spring-boot-loader-tools") - compile(project(":mcspring-api:mcspring-jar-loader")) -} diff --git a/mcspring-build/mcspring-plugin-layout/pom.xml b/mcspring-build/mcspring-plugin-layout/pom.xml deleted file mode 100644 index d0b6971..0000000 --- a/mcspring-build/mcspring-plugin-layout/pom.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - mcspring-build - in.kyle.mcspring - 0.0.9 - - 4.0.0 - - mcspring-plugin-layout - - - - org.springframework.boot - spring-boot-loader-tools - - - in.kyle.mcspring - mcspring-jar-loader - ${project.version} - - - - - - - maven-dependency-plugin - - - prepare-package - - copy-dependencies - - - - ${project.build.outputDirectory}/META-INF/loader - - mcspring-jar-loader - true - - - - - - - diff --git a/mcspring-build/mcspring-plugin-layout/src/main/kotlin/in/kyle/mcspring/layout/McSpringLayout.java b/mcspring-build/mcspring-plugin-layout/src/main/kotlin/in/kyle/mcspring/layout/McSpringLayout.java deleted file mode 100644 index 32a2210..0000000 --- a/mcspring-build/mcspring-plugin-layout/src/main/kotlin/in/kyle/mcspring/layout/McSpringLayout.java +++ /dev/null @@ -1,43 +0,0 @@ -package in.kyle.mcspring.layout; - -import org.springframework.boot.loader.tools.CustomLoaderLayout; -import org.springframework.boot.loader.tools.Layout; -import org.springframework.boot.loader.tools.LibraryScope; -import org.springframework.boot.loader.tools.LoaderClassesWriter; - -import java.io.IOException; - -public class McSpringLayout implements Layout, CustomLoaderLayout { - - private static final String NESTED_LOADER_JAR = "META-INF/loader/mcspring-jar-loader.jar"; - - @Override - public String getLauncherClassName() { - return ""; - } - - @Override - public String getLibraryDestination(String libraryName, LibraryScope scope) { - if (scope == LibraryScope.COMPILE) { - return "BOOT-INF/lib/"; - } else { - return null; - } - } - - @Override - public String getClassesLocation() { - return ""; - } - - @Override - public boolean isExecutable() { - return true; - } - - @Override - public void writeLoadedClasses(LoaderClassesWriter writer) throws IOException { - writer.writeLoaderClasses(); - writer.writeLoaderClasses(NESTED_LOADER_JAR); - } -} diff --git a/mcspring-build/mcspring-plugin-layout/src/main/kotlin/in/kyle/mcspring/layout/McSpringLayoutFactory.java b/mcspring-build/mcspring-plugin-layout/src/main/kotlin/in/kyle/mcspring/layout/McSpringLayoutFactory.java deleted file mode 100644 index 68833ff..0000000 --- a/mcspring-build/mcspring-plugin-layout/src/main/kotlin/in/kyle/mcspring/layout/McSpringLayoutFactory.java +++ /dev/null @@ -1,18 +0,0 @@ -package in.kyle.mcspring.layout; - -import org.springframework.boot.loader.tools.Layout; -import org.springframework.boot.loader.tools.LayoutFactory; - -import java.io.File; - -public class McSpringLayoutFactory implements LayoutFactory { - - // required by spring-boot - @SuppressWarnings("unused") - private String name = "mcspring"; - - @Override - public Layout getLayout(File file) { - return new McSpringLayout(); - } -} diff --git a/mcspring-build/mcspring-plugin-manager/build.gradle.kts b/mcspring-build/mcspring-plugin-manager/build.gradle.kts index cb6b1da..8373e5e 100644 --- a/mcspring-build/mcspring-plugin-manager/build.gradle.kts +++ b/mcspring-build/mcspring-plugin-manager/build.gradle.kts @@ -1,4 +1,16 @@ +plugins { + id("in.kyle.mcspring") +} + dependencies { - api(project(":mcspring-api:mcspring-commands-dsl")) - testImplementation(project(":mcspring-api:mcspring-test")) + implementation(project(":mcspring-api:mcspring-base")) + implementation(project(":mcspring-api:mcspring-commands-dsl")) +} + +mcspring { + pluginAuthor = "kylepls" +} + +tasks.build { + dependsOn(tasks.named("buildServer")) } diff --git a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/McSpringPluginManager.kt b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/McSpringPluginManager.kt new file mode 100644 index 0000000..eabc82a --- /dev/null +++ b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/McSpringPluginManager.kt @@ -0,0 +1,8 @@ +package `in`.kyle.mcspring.manager + +import `in`.kyle.mcspring.annotation.SpringPlugin + +@SpringPlugin +open class McSpringPluginManager { + +} diff --git a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandAbout.kt b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandAbout.kt index 4c266b1..dc2e144 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandAbout.kt +++ b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandAbout.kt @@ -1,7 +1,7 @@ package `in`.kyle.mcspring.manager.commands -import `in`.kyle.mcspring.command.Command import `in`.kyle.mcspring.commands.dsl.commandExecutor +import `in`.kyle.mcspring.commands.dsl.mcspring.Command import org.bukkit.Server import org.springframework.boot.info.BuildProperties import org.springframework.core.SpringVersion diff --git a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandClassLoader.kt b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandClassLoader.kt index b0c0bbb..4b555f7 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandClassLoader.kt +++ b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandClassLoader.kt @@ -1,7 +1,7 @@ package `in`.kyle.mcspring.manager.commands -import `in`.kyle.mcspring.command.Command import `in`.kyle.mcspring.commands.dsl.commandExecutor +import `in`.kyle.mcspring.commands.dsl.mcspring.Command import org.springframework.stereotype.Component @Component diff --git a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandGamemode.kt b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandGamemode.kt index 438cf47..53a816a 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandGamemode.kt +++ b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandGamemode.kt @@ -1,7 +1,7 @@ package `in`.kyle.mcspring.manager.commands -import `in`.kyle.mcspring.command.Command import `in`.kyle.mcspring.commands.dsl.commandExecutor +import `in`.kyle.mcspring.commands.dsl.mcspring.Command import org.bukkit.GameMode import org.bukkit.entity.Player import org.springframework.stereotype.Component diff --git a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandHeal.kt b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandHeal.kt index addef8b..1ec8e94 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandHeal.kt +++ b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandHeal.kt @@ -1,8 +1,7 @@ package `in`.kyle.mcspring.manager.commands -import `in`.kyle.mcspring.command.Command import `in`.kyle.mcspring.commands.dsl.commandExecutor -import `in`.kyle.mcspring.commands.dsl.otherwise +import `in`.kyle.mcspring.commands.dsl.mcspring.Command import org.bukkit.entity.Player import org.springframework.stereotype.Component @@ -25,7 +24,7 @@ internal class CommandHeal { val health by doubleArg { default { 20.0 } parser { - between(0.0, 20.0) otherwise { message("Heal value must be between 0 and 20") } + between(0.0, 20.0) { message("Heal value must be between 0 and 20") } } invalid { message("Invalid health amount $it") } } diff --git a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandOp.kt b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandOp.kt index d8f9d98..4792848 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandOp.kt +++ b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandOp.kt @@ -1,7 +1,7 @@ package `in`.kyle.mcspring.manager.commands -import `in`.kyle.mcspring.command.Command import `in`.kyle.mcspring.commands.dsl.commandExecutor +import `in`.kyle.mcspring.commands.dsl.mcspring.Command import org.bukkit.command.CommandSender import org.bukkit.entity.Player import org.springframework.stereotype.Component diff --git a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandPlugin.kt b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandPlugin.kt index 7634868..8fc0421 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandPlugin.kt +++ b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandPlugin.kt @@ -1,8 +1,8 @@ package `in`.kyle.mcspring.manager.commands -import `in`.kyle.mcspring.command.Command import `in`.kyle.mcspring.manager.controller.PluginController import `in`.kyle.mcspring.commands.dsl.commandExecutor +import `in`.kyle.mcspring.commands.dsl.mcspring.Command import org.bukkit.plugin.Plugin import org.springframework.boot.autoconfigure.condition.ConditionalOnBean import org.springframework.stereotype.Component diff --git a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandReload.kt b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandReload.kt index cab5dc1..4fcf439 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandReload.kt +++ b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandReload.kt @@ -1,7 +1,7 @@ package `in`.kyle.mcspring.manager.commands -import `in`.kyle.mcspring.command.Command import `in`.kyle.mcspring.commands.dsl.commandExecutor +import `in`.kyle.mcspring.commands.dsl.mcspring.Command import org.bukkit.Bukkit import org.bukkit.plugin.Plugin import org.springframework.boot.autoconfigure.condition.ConditionalOnBean diff --git a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandSpeed.kt b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandSpeed.kt index b58fc6b..5b51e8d 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandSpeed.kt +++ b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandSpeed.kt @@ -1,8 +1,7 @@ package `in`.kyle.mcspring.manager.commands -import `in`.kyle.mcspring.command.Command import `in`.kyle.mcspring.commands.dsl.commandExecutor -import `in`.kyle.mcspring.commands.dsl.otherwise +import `in`.kyle.mcspring.commands.dsl.mcspring.Command import org.bukkit.entity.Player import org.springframework.stereotype.Component @@ -19,7 +18,7 @@ internal class CommandSpeed { val player = sender as Player val speed by doubleArg { parser { - between(0.0, 10.0) otherwise { message("Speed must be between 0 and 10") } + between(0.0, 10.0) { message("Speed must be between 0 and 10") } } invalid { message("Speed $it is not a valid speed") } missing { diff --git a/mcspring-build/mcspring-starter/pom.xml b/mcspring-build/mcspring-starter/pom.xml deleted file mode 100644 index 1801b02..0000000 --- a/mcspring-build/mcspring-starter/pom.xml +++ /dev/null @@ -1,216 +0,0 @@ - - - - mcspring-build - in.kyle.mcspring - 0.0.9 - - - pom - - - ../../mcspring-examples - ../mcspring-plugin-manager - - 4.0.0 - - mcspring-starter - - - - - in.kyle.mcspring - mcspring-base - 0.0.9 - - - in.kyle.mcspring - mcspring-subcommands - 0.0.9 - - - - - - - - - in.kyle.mcspring - mcspring-maven-plugin - 0.0.9 - - - process-classes - - generate-files - - - - - - org.springframework.boot - spring-boot-maven-plugin - ${spring.version} - - ignored - - mcspring - - - - - in.kyle.mcspring - mcspring-plugin-layout - - 0.0.9 - - - - - build-info - - repackage - build-info - - - - - - maven-antrun-plugin - 1.8 - - - package - - run - - - - - - - - - - - com.coderplus.maven.plugins - copy-rename-maven-plugin - 1.0.1 - - - init - package - - copy - - - - ${project.build.directory}/${project.artifactId}.jar - - - ${project.basedir}/spigot/plugins/${project.artifactId}.jar - - - - - copy-plugin - package - - copy - - - - ${project.build.directory}/${project.artifactId}.jar - - - ${project.basedir}/spigot/plugins/update/${project.artifactId}.jar - - - - - - - com.googlecode.maven-download-plugin - download-maven-plugin - 1.3.0 - - - generate-resources - - wget - - - https://cdn.getbukkit.org/spigot/spigot-1.15.2.jar - spigot.jar - ${project.basedir}/spigot - - - - - - - - - - - - true - - generate-yml - - UTF-8 - UTF-8 - 1.8 - 1.8 - - - - - org.springframework.boot - spring-boot-maven-plugin - - - org.apache.maven.plugins - maven-antrun-plugin - - - com.coderplus.maven.plugins - copy-rename-maven-plugin - - - com.googlecode.maven-download-plugin - download-maven-plugin - - - in.kyle.mcspring - mcspring-maven-plugin - - - ${project.artifactId} - - - - in.kyle.mcspring - mcspring-base - - - - org.spigotmc - spigot-api - ${spigot.version} - provided - - - - org.bukkit - bukkit - ${spigot.version} - provided - - - - - diff --git a/settings.gradle.kts b/settings.gradle.kts index 8d4b6fe..02f4a94 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,27 +1,13 @@ rootProject.name = "mcspring-parent" -include(":mcspring-build:mcspring-plugin-layout") -include(":mcspring-build:mcspring-archetype") -include(":mcspring-build:mcspring-starter:mcspring-examples:simple-factions") -include(":mcspring-build:mcspring-starter:mcspring-examples:simple-factions-addon") -include(":mcspring-build:mcspring-starter:mcspring-examples") -include(":mcspring-build:mcspring-starter:mcspring-plugin-manager") -include(":mcspring-build:mcspring-starter") -include(":mcspring-build:mcspring-maven-plugin") -include(":mcspring-build") +include(":mcspring-api") include(":mcspring-api:mcspring-commands-dsl") include(":mcspring-api:mcspring-base") include(":mcspring-api:mcspring-vault") -include(":mcspring-api:mcspring-jar-loader") -include(":mcspring-api:mcspring-test") include(":mcspring-api:mcspring-rx") include(":mcspring-api:mcspring-guis") include(":mcspring-api:mcspring-chat") include(":mcspring-api:mcspring-logging") -include(":mcspring-api:mcspring-common") -include(":mcspring-api") -project(":mcspring-build:mcspring-starter:mcspring-examples:simple-factions").projectDir = file("mcspring-examples/simple-factions") -project(":mcspring-build:mcspring-starter:mcspring-examples:simple-factions-addon").projectDir = file("mcspring-examples/simple-factions-addon") -project(":mcspring-build:mcspring-starter:mcspring-examples").projectDir = file("mcspring-examples") -project(":mcspring-build:mcspring-starter:mcspring-plugin-manager").projectDir = file("mcspring-build/mcspring-plugin-manager") +include(":mcspring-build") +include(":mcspring-build:mcspring-plugin-manager") From c4aa3a122253a3e732387668c276d5a1acaf9755 Mon Sep 17 00:00:00 2001 From: kylepls Date: Sun, 14 Jun 2020 21:16:01 -0600 Subject: [PATCH 25/54] Break out plugin to own project --- .gitignore | 1 + .maven.xml | 23 ------ .travis.yml | 28 ++++--- build.gradle.kts | 1 + buildSrc/build.gradle.kts | 17 +--- .../src/main/kotlin/mcspring-build.gradle.kts | 1 + .../main/kotlin/mcspring-publish.gradle.kts | 81 +++++++++++++++++++ .../mcspring-commands-dsl/build.gradle.kts | 1 + .../mcspring-gradle-plugin/build.gradle.kts | 44 ++++++++++ .../settings.gradle.kts | 1 + .../kotlin/in/kyle/mcspring/BuildPlugin.kt | 0 .../in/kyle/mcspring/McSpringExtension.kt | 0 .../in/kyle/mcspring/tasks/BuildPluginYml.kt | 0 .../in/kyle/mcspring/tasks/DownloadJar.kt | 2 - .../in/kyle/mcspring/tasks/SetupSpigot.kt | 0 settings.gradle.kts | 4 +- 16 files changed, 153 insertions(+), 51 deletions(-) delete mode 100644 .maven.xml create mode 100644 buildSrc/src/main/kotlin/mcspring-publish.gradle.kts create mode 100644 mcspring-build/mcspring-gradle-plugin/build.gradle.kts create mode 100644 mcspring-build/mcspring-gradle-plugin/settings.gradle.kts rename {buildSrc => mcspring-build/mcspring-gradle-plugin}/src/main/kotlin/in/kyle/mcspring/BuildPlugin.kt (100%) rename {buildSrc => mcspring-build/mcspring-gradle-plugin}/src/main/kotlin/in/kyle/mcspring/McSpringExtension.kt (100%) rename {buildSrc => mcspring-build/mcspring-gradle-plugin}/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt (100%) rename {buildSrc => mcspring-build/mcspring-gradle-plugin}/src/main/kotlin/in/kyle/mcspring/tasks/DownloadJar.kt (97%) rename {buildSrc => mcspring-build/mcspring-gradle-plugin}/src/main/kotlin/in/kyle/mcspring/tasks/SetupSpigot.kt (100%) diff --git a/.gitignore b/.gitignore index d6ed412..75baae1 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ dependency-reduced-pom.xml pom.xml.versionsBackup **/spigot/** !**/archetype-resources/spigot/** +gradle.properties diff --git a/.maven.xml b/.maven.xml deleted file mode 100644 index 9033461..0000000 --- a/.maven.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - ossrh - ${env.SONATYPE_USERNAME} - ${env.SONATYPE_PASSWORD} - - - - - - ossrh - - true - - - gpg - ${env.GPG_PASSPHRASE} - - - - diff --git a/.travis.yml b/.travis.yml index 0dcd791..452183d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,20 +1,28 @@ language: java jdk: openjdk8 -before_install: - - echo $GPG_SECRET_KEYS | base64 --decode | $GPG_EXECUTABLE --import - - echo $GPG_OWNERTRUST | base64 --decode | $GPG_EXECUTABLE --import-ownertrust - - export MAVEN_OPTS=-Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn -before_deploy: - - mvn help:evaluate -N -Dexpression=project.version|grep -v '\[' - - export project_version=$(mvn help:evaluate -N -Dexpression=project.version|grep -v '\[') +install: skip + +before_cache: + - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock + - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ + +cache: + directories: + - $HOME/.gradle/caches/ + - $HOME/.gradle/wrapper/ + deploy: provider: script skip_cleanup: true ## Build and release to maven central on tagged version - script: mvn deploy --settings .maven.xml -DskipTests=true -B -U -Prelease + script: + - ./gradlew bintrayPublish -Dorg.gradle.project.bintray.user=$BINTRAY_USER -Dorg.gradle.project.bintray.key=$BINTRAY_KEY +# TODO add in publishing for plugin +# - ./ -Dorg.gradle.project.gradle.publish.key=$PLUGIN_PORTAL_API_KEY -Dorg.gradle.project.gradle.publish.secret=$PLUGIN_PORTAL_API_SECRET on: tags: true branch: master name: $project_version -install: - mvn --settings .maven.xml clean install -DskipTests=true -Dgpg.skip -Dmaven.javadoc.skip=true -B -V + +script: + - ./gradlew build --scan -s diff --git a/build.gradle.kts b/build.gradle.kts index 2def065..108de07 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -9,4 +9,5 @@ allprojects { subprojects { apply(plugin = "mcspring-build") + apply(plugin = "mcspring-publish") } diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 229dbd7..09aa405 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -12,28 +12,15 @@ repositories { } dependencies { + implementation("org.springframework.boot:spring-boot-gradle-plugin:2.3.1.RELEASE") implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72") implementation("io.spring.gradle:dependency-management-plugin:1.0.9.RELEASE") implementation("org.jetbrains.dokka:dokka-gradle-plugin:0.10.1") + implementation("com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.5") implementation("gradle.plugin.com.eden:orchidPlugin:0.20.0") { repositories { maven("https://plugins.gradle.org/m2/") } } - - implementation("io.github.classgraph:classgraph:4.8.83") - implementation("org.yaml:snakeyaml:1.26") - implementation("com.github.jengelman.gradle.plugins:shadow:5.2.0") - implementation("org.springframework.boot:spring-boot-gradle-plugin:2.3.1.RELEASE") - - testImplementation(gradleTestKit()) } -gradlePlugin { - plugins { - register("mcspring-gradle-plugin") { - id = "in.kyle.mcspring" - implementationClass = "$id.BuildPlugin" - } - } -} diff --git a/buildSrc/src/main/kotlin/mcspring-build.gradle.kts b/buildSrc/src/main/kotlin/mcspring-build.gradle.kts index 6aafc56..68ee416 100644 --- a/buildSrc/src/main/kotlin/mcspring-build.gradle.kts +++ b/buildSrc/src/main/kotlin/mcspring-build.gradle.kts @@ -27,6 +27,7 @@ dependencyManagement { dependencies { compileOnly("org.spigotmc:spigot-api") + testCompileOnly("org.spigotmc:spigot-api") implementation(kotlin("stdlib")) diff --git a/buildSrc/src/main/kotlin/mcspring-publish.gradle.kts b/buildSrc/src/main/kotlin/mcspring-publish.gradle.kts new file mode 100644 index 0000000..6933a46 --- /dev/null +++ b/buildSrc/src/main/kotlin/mcspring-publish.gradle.kts @@ -0,0 +1,81 @@ +import com.jfrog.bintray.gradle.BintrayExtension +import java.sql.Date + +plugins { + kotlin("jvm") + `maven-publish` + id("com.jfrog.bintray") +} + +tasks.create("sourcesJar") { + archiveClassifier.set("sources") + from(sourceSets.getByName("main").allSource) + dependsOn(tasks.classes) +} + +val artifactName = project.name +val artifactGroup = project.group.toString() +val artifactVersion = project.version.toString() + +val pomUrl = "https://github.com/kylepls/mcspring" +val pomScmUrl = "https://github.com/kylepls/mcspring" +val pomIssueUrl = "https://github.com/kylepls/mcspring/issues" +val pomDesc = "https://github.com/kylepls/mcspring" + +val githubRepo = "kylepls/mcspring" +val githubReadme = "README.md" + +val pomLicenseName = "MIT" +val pomLicenseUrl = "https://opensource.org/licenses/mit-license.php" +val pomLicenseDist = "repo" + +val pomDeveloperId = "kylepls" +val pomDeveloperName = "Kyle" + +publishing { + publications { + create("mcspring") { + groupId = artifactGroup + artifactId = artifactName + version = artifactVersion + from(components["java"]) + artifact(tasks.findByName("sourcesJar")) + + pom.withXml { + asNode().apply { + appendNode("description", pomDesc) + appendNode("name", rootProject.name) + appendNode("url", pomUrl) + appendNode("licenses").appendNode("license").apply { + appendNode("name", pomLicenseName) + appendNode("url", pomLicenseUrl) + appendNode("distribution", pomLicenseDist) + } + appendNode("developers").appendNode("developer").apply { + appendNode("id", pomDeveloperId) + appendNode("name", pomDeveloperName) + } + appendNode("scm").apply { + appendNode("url", pomScmUrl) + } + } + } + } + } +} + +bintray { + user = project.findProperty("bintray.user")?.toString() ?: System.getProperty("bintray.user") + key = project.findProperty("bintray.key")?.toString() ?: System.getProperty("bintray.key") + setPublications("mcspring") + override = true + publish = true + + pkg(delegateClosureOf { + repo = "maven" + name = "mcspring" + userOrg = "mcspring" + setLabels("kotlin", "spigot", "spring") + vcsUrl = "https://github.com/kylepls/mcspring" + }) +} diff --git a/mcspring-api/mcspring-commands-dsl/build.gradle.kts b/mcspring-api/mcspring-commands-dsl/build.gradle.kts index 76cbd19..9115aff 100644 --- a/mcspring-api/mcspring-commands-dsl/build.gradle.kts +++ b/mcspring-api/mcspring-commands-dsl/build.gradle.kts @@ -1,3 +1,4 @@ dependencies { compileOnly(project(":mcspring-api:mcspring-base")) + testCompileOnly(project(":mcspring-api:mcspring-base")) } diff --git a/mcspring-build/mcspring-gradle-plugin/build.gradle.kts b/mcspring-build/mcspring-gradle-plugin/build.gradle.kts new file mode 100644 index 0000000..efd6450 --- /dev/null +++ b/mcspring-build/mcspring-gradle-plugin/build.gradle.kts @@ -0,0 +1,44 @@ +plugins { + `kotlin-dsl` + id("com.gradle.plugin-publish") version "0.12.0" + id("java-gradle-plugin") +} + +group = "in.kyle.mcspring" +version = "0.0.1" + +repositories { + jcenter() + mavenCentral() +} + +dependencies { + implementation("io.github.classgraph:classgraph:4.8.83") + implementation("org.yaml:snakeyaml:1.26") + implementation("com.github.jengelman.gradle.plugins:shadow:5.2.0") + implementation("org.springframework.boot:spring-boot-gradle-plugin:2.3.1.RELEASE") + + testImplementation(gradleTestKit()) +} + +gradlePlugin { + plugins { + register("mcspring-gradle-plugin") { + id = "in.kyle.mcspring" + implementationClass = "$id.BuildPlugin" + } + } +} + +pluginBundle { + website = "https://github.com/kylepls/mcspring" + vcsUrl = "https://github.com/kylepls/mcspring.git" + description = "Add Spring to Bukkit plugins" + tags = listOf("bukkit", "spring") + + plugins { + getByName("mcspring-gradle-plugin") { + displayName = "mcspring gradle plugin" + } + } +} diff --git a/mcspring-build/mcspring-gradle-plugin/settings.gradle.kts b/mcspring-build/mcspring-gradle-plugin/settings.gradle.kts new file mode 100644 index 0000000..8177156 --- /dev/null +++ b/mcspring-build/mcspring-gradle-plugin/settings.gradle.kts @@ -0,0 +1 @@ +rootProject.name = "mcspring-gradle-plugin" diff --git a/buildSrc/src/main/kotlin/in/kyle/mcspring/BuildPlugin.kt b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/BuildPlugin.kt similarity index 100% rename from buildSrc/src/main/kotlin/in/kyle/mcspring/BuildPlugin.kt rename to mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/BuildPlugin.kt diff --git a/buildSrc/src/main/kotlin/in/kyle/mcspring/McSpringExtension.kt b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/McSpringExtension.kt similarity index 100% rename from buildSrc/src/main/kotlin/in/kyle/mcspring/McSpringExtension.kt rename to mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/McSpringExtension.kt diff --git a/buildSrc/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt similarity index 100% rename from buildSrc/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt rename to mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt diff --git a/buildSrc/src/main/kotlin/in/kyle/mcspring/tasks/DownloadJar.kt b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/DownloadJar.kt similarity index 97% rename from buildSrc/src/main/kotlin/in/kyle/mcspring/tasks/DownloadJar.kt rename to mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/DownloadJar.kt index 746101b..0a6f9bf 100644 --- a/buildSrc/src/main/kotlin/in/kyle/mcspring/tasks/DownloadJar.kt +++ b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/DownloadJar.kt @@ -21,8 +21,6 @@ open class DownloadJar : DefaultTask() { return } - target.delete() - target.outputStream().use { os -> URI(props.spigotDownloadUrl).toURL().openStream().use { it.copyTo(os) diff --git a/buildSrc/src/main/kotlin/in/kyle/mcspring/tasks/SetupSpigot.kt b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/SetupSpigot.kt similarity index 100% rename from buildSrc/src/main/kotlin/in/kyle/mcspring/tasks/SetupSpigot.kt rename to mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/SetupSpigot.kt diff --git a/settings.gradle.kts b/settings.gradle.kts index 02f4a94..e69856c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,4 +1,4 @@ -rootProject.name = "mcspring-parent" +rootProject.name = "mcspring" include(":mcspring-api") include(":mcspring-api:mcspring-commands-dsl") @@ -11,3 +11,5 @@ include(":mcspring-api:mcspring-logging") include(":mcspring-build") include(":mcspring-build:mcspring-plugin-manager") + +includeBuild("./mcspring-build/mcspring-gradle-plugin") From 86255928d7b32958b17a3112db4f7f44e9f1b74c Mon Sep 17 00:00:00 2001 From: kylepls Date: Sun, 14 Jun 2020 21:24:43 -0600 Subject: [PATCH 26/54] Update travis and README.md --- .travis.yml | 7 +++---- README.md | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 452183d..f60e163 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,10 +13,9 @@ cache: deploy: provider: script - skip_cleanup: true - ## Build and release to maven central on tagged version + cleanup: false script: - - ./gradlew bintrayPublish -Dorg.gradle.project.bintray.user=$BINTRAY_USER -Dorg.gradle.project.bintray.key=$BINTRAY_KEY + - gradle bintrayPublish -Dorg.gradle.project.bintray.user=$BINTRAY_USER -Dorg.gradle.project.bintray.key=$BINTRAY_KEY # TODO add in publishing for plugin # - ./ -Dorg.gradle.project.gradle.publish.key=$PLUGIN_PORTAL_API_KEY -Dorg.gradle.project.gradle.publish.secret=$PLUGIN_PORTAL_API_SECRET on: @@ -25,4 +24,4 @@ deploy: name: $project_version script: - - ./gradlew build --scan -s + - gradle build --scan -s diff --git a/README.md b/README.md index d46be2b..6df1990 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# mcspring [![Build Status](https://travis-ci.org/kylepls/mcspring.svg?branch=master)](https://travis-ci.org/kylepls/mcspring) ![Maven Central](https://img.shields.io/maven-central/v/in.kyle.mcspring/mcspring) +# mcspring [![Build Status](https://travis-ci.org/kylepls/mcspring.svg?branch=master)](https://travis-ci.org/kylepls/mcspring) ![Bintray](https://img.shields.io/bintray/v/mcspring/maven/mcspring) Writing Bukkit plugins is a nightmare. I often lay awake in my bed late at night unable to sleep because Bukkit made events an annotation but commands are created by implementing a class. From eeafb1bee2142505fd1b056ba84c6b5bac732770 Mon Sep 17 00:00:00 2001 From: kylepls Date: Sun, 14 Jun 2020 21:34:22 -0600 Subject: [PATCH 27/54] Update travis --- .gitignore | 5 ++--- .travis.yml | 8 ++++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 75baae1..c911bd4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,7 @@ .idea *.iml -dependency-reduced-pom.xml -**/target/** -#**/build/** +**/out/** +**/build/** .gradle pom.xml.versionsBackup **/spigot/** diff --git a/.travis.yml b/.travis.yml index f60e163..97f3db9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,13 +15,13 @@ deploy: provider: script cleanup: false script: - - gradle bintrayPublish -Dorg.gradle.project.bintray.user=$BINTRAY_USER -Dorg.gradle.project.bintray.key=$BINTRAY_KEY -# TODO add in publishing for plugin -# - ./ -Dorg.gradle.project.gradle.publish.key=$PLUGIN_PORTAL_API_KEY -Dorg.gradle.project.gradle.publish.secret=$PLUGIN_PORTAL_API_SECRET + - ./gradlew bintrayPublish -Dorg.gradle.project.bintray.user=$BINTRAY_USER -Dorg.gradle.project.bintray.key=$BINTRAY_KEY + # TODO add in publishing for plugin + # - ./ -Dorg.gradle.project.gradle.publish.key=$PLUGIN_PORTAL_API_KEY -Dorg.gradle.project.gradle.publish.secret=$PLUGIN_PORTAL_API_SECRET on: tags: true branch: master name: $project_version script: - - gradle build --scan -s + - ./gradlew build --scan -s From 19cd27dcdd8a68ff747612bbe9624c1b0ac05050 Mon Sep 17 00:00:00 2001 From: kylepls Date: Sun, 14 Jun 2020 21:37:38 -0600 Subject: [PATCH 28/54] Update permission for travis --- gradlew | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 gradlew diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 From ee8572195eda5b2a2f9c34957c1b1d0c774bee45 Mon Sep 17 00:00:00 2001 From: kylepls Date: Sun, 14 Jun 2020 21:49:46 -0600 Subject: [PATCH 29/54] Fix failing tests --- .travis.yml | 9 ++++++--- buildSrc/src/main/kotlin/mcspring-build.gradle.kts | 2 +- mcspring-api/mcspring-commands-dsl/build.gradle.kts | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 97f3db9..c7c692d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,9 @@ language: java jdk: openjdk8 install: skip +before_install: + - chmod +x gradlew + before_cache: - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ @@ -11,6 +14,9 @@ cache: - $HOME/.gradle/caches/ - $HOME/.gradle/wrapper/ +script: + - ./gradlew build --scan -s + deploy: provider: script cleanup: false @@ -22,6 +28,3 @@ deploy: tags: true branch: master name: $project_version - -script: - - ./gradlew build --scan -s diff --git a/buildSrc/src/main/kotlin/mcspring-build.gradle.kts b/buildSrc/src/main/kotlin/mcspring-build.gradle.kts index 68ee416..8a8cb50 100644 --- a/buildSrc/src/main/kotlin/mcspring-build.gradle.kts +++ b/buildSrc/src/main/kotlin/mcspring-build.gradle.kts @@ -27,7 +27,7 @@ dependencyManagement { dependencies { compileOnly("org.spigotmc:spigot-api") - testCompileOnly("org.spigotmc:spigot-api") + testImplementation("org.spigotmc:spigot-api") implementation(kotlin("stdlib")) diff --git a/mcspring-api/mcspring-commands-dsl/build.gradle.kts b/mcspring-api/mcspring-commands-dsl/build.gradle.kts index 9115aff..df14890 100644 --- a/mcspring-api/mcspring-commands-dsl/build.gradle.kts +++ b/mcspring-api/mcspring-commands-dsl/build.gradle.kts @@ -1,4 +1,4 @@ dependencies { compileOnly(project(":mcspring-api:mcspring-base")) - testCompileOnly(project(":mcspring-api:mcspring-base")) + testImplementation(project(":mcspring-api:mcspring-base")) } From 22006cd729abecb9ab72d64a880f6c86c7f27e87 Mon Sep 17 00:00:00 2001 From: kylepls Date: Sun, 14 Jun 2020 22:02:03 -0600 Subject: [PATCH 30/54] Remove unused import --- .../mcspring/guis/invenotry/InventoryGui.kt | 1 - .../mcspring-gradle-plugin/build.gradle.kts | 17 ++++++----------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/invenotry/InventoryGui.kt b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/invenotry/InventoryGui.kt index dbb1c40..8368d45 100644 --- a/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/invenotry/InventoryGui.kt +++ b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/invenotry/InventoryGui.kt @@ -7,7 +7,6 @@ import `in`.kyle.mcspring.guis.item.ItemBuilder import `in`.kyle.mcspring.rx.observeEvent import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.Disposable -import jdk.jshell.JShell import org.bukkit.Bukkit import org.bukkit.entity.Player import org.bukkit.event.inventory.ClickType.* diff --git a/mcspring-build/mcspring-gradle-plugin/build.gradle.kts b/mcspring-build/mcspring-gradle-plugin/build.gradle.kts index efd6450..796f621 100644 --- a/mcspring-build/mcspring-gradle-plugin/build.gradle.kts +++ b/mcspring-build/mcspring-gradle-plugin/build.gradle.kts @@ -21,24 +21,19 @@ dependencies { testImplementation(gradleTestKit()) } -gradlePlugin { - plugins { - register("mcspring-gradle-plugin") { - id = "in.kyle.mcspring" - implementationClass = "$id.BuildPlugin" - } - } -} - pluginBundle { website = "https://github.com/kylepls/mcspring" vcsUrl = "https://github.com/kylepls/mcspring.git" - description = "Add Spring to Bukkit plugins" tags = listOf("bukkit", "spring") +} +gradlePlugin { plugins { - getByName("mcspring-gradle-plugin") { + register("mcspring-gradle-plugin") { + id = "in.kyle.mcspring" + implementationClass = "$id.BuildPlugin" displayName = "mcspring gradle plugin" + description = "Handles the generation of plugin.yml files and applies Spring package formatting" } } } From b86328bae87919a5a03540a4d4272bcc5b01ac13 Mon Sep 17 00:00:00 2001 From: kylepls Date: Sun, 14 Jun 2020 22:10:59 -0600 Subject: [PATCH 31/54] Add gradle enterprise plugin --- settings.gradle.kts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/settings.gradle.kts b/settings.gradle.kts index e69856c..78f1882 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,3 +1,7 @@ +plugins { + id("com.gradle.enterprise").version("3.3.4") +} + rootProject.name = "mcspring" include(":mcspring-api") @@ -13,3 +17,10 @@ include(":mcspring-build") include(":mcspring-build:mcspring-plugin-manager") includeBuild("./mcspring-build/mcspring-gradle-plugin") + +gradleEnterprise { + buildScan { + termsOfServiceUrl = "https://gradle.com/terms-of-service" + termsOfServiceAgree = "yes" + } +} From 9ccd5c8f0c50ea7180e0e9fdb6aacc08038a201f Mon Sep 17 00:00:00 2001 From: kylepls Date: Thu, 18 Jun 2020 22:36:46 -0600 Subject: [PATCH 32/54] Update command DSL tests and remove logging package --- .../src/main/kotlin/mcspring-build.gradle.kts | 9 +- .../mcspring/commands/dsl/CommandBuilder.kt | 38 ++--- .../mcspring/commands/dsl/CommandFacade.kt | 19 +++ .../mcspring/commands/dsl/CommandParsers.kt | 120 ---------------- .../dsl/mcspring/CommandDslRegistration.kt | 3 +- .../commands/dsl/parsers/BaseParser.kt | 44 ++++++ .../commands/dsl/parsers/BooleanParser.kt | 16 +++ .../commands/dsl/parsers/MapParser.kt | 9 ++ .../commands/dsl/parsers/PlayerParser.kt | 16 +++ .../commands/dsl/parsers/StringParser.kt | 20 +++ .../dsl/parsers/numbers/DoubleParser.kt | 9 ++ .../commands/dsl/parsers/numbers/IntParser.kt | 13 ++ .../dsl/parsers/numbers/NumberParser.kt | 27 ++++ .../commands/dsl/{ => util}/CommandUtils.kt | 5 +- .../commands/dsl/CommandTestSupport.kt | 14 +- .../mcspring/commands/dsl/TestArgBuilder.kt | 37 +++++ .../commands/dsl/TestCommandBuilder.kt | 46 ++++++ .../commands/dsl/TestSubcommandBuilder.kt | 44 ++++++ .../mcspring/commands/dsl/TestValueBuilder.kt | 51 +++++++ .../builders/TestBaseRequirementBuilder.kt | 62 -------- .../dsl/builders/TestIntRequirementBuilder.kt | 20 --- .../dsl/builders/TestOnRequirementBuilder.kt | 44 ------ .../builders/TestOptionRequirementBuilder.kt | 40 ------ .../builders/TestStringRequirementBuilder.kt | 47 ------ .../commands/dsl/parsers/TestBooleanParser.kt | 31 ++++ .../commands/dsl/parsers/TestMapParser.kt | 24 ++++ .../commands/dsl/parsers/TestStringParser.kt | 59 ++++++++ .../dsl/parsers/numbers/TestDoubleParser.kt | 18 +++ .../dsl/parsers/numbers/TestIntParser.kt | 34 +++++ .../dsl/parsers/numbers/TestNumberParser.kt | 134 ++++++++++++++++++ .../mcspring-logging/build.gradle.kts | 14 -- .../in/kyle/mcspring/logging/LogCall.kt | 12 -- .../in/kyle/mcspring/logging/LogCallAspect.kt | 31 ---- .../mcspring/logging/LoggingConfiguration.kt | 25 ---- .../in/kyle/mcspring/logging/TestComponent.kt | 12 -- .../src/main/resources/log4j2-mcspring.xml | 24 ---- .../src/main/resources/log4j2.xml | 18 --- .../in/kyle/mcspring/logging/TestLog.kt | 32 ----- 38 files changed, 689 insertions(+), 532 deletions(-) create mode 100644 mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandFacade.kt delete mode 100644 mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandParsers.kt create mode 100644 mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/BaseParser.kt create mode 100644 mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/BooleanParser.kt create mode 100644 mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/MapParser.kt create mode 100644 mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/PlayerParser.kt create mode 100644 mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/StringParser.kt create mode 100644 mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/DoubleParser.kt create mode 100644 mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/IntParser.kt create mode 100644 mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/NumberParser.kt rename mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/{ => util}/CommandUtils.kt (87%) create mode 100644 mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestArgBuilder.kt create mode 100644 mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestCommandBuilder.kt create mode 100644 mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestSubcommandBuilder.kt create mode 100644 mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestValueBuilder.kt delete mode 100644 mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestBaseRequirementBuilder.kt delete mode 100644 mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestIntRequirementBuilder.kt delete mode 100644 mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestOnRequirementBuilder.kt delete mode 100644 mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestOptionRequirementBuilder.kt delete mode 100644 mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestStringRequirementBuilder.kt create mode 100644 mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/TestBooleanParser.kt create mode 100644 mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/TestMapParser.kt create mode 100644 mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/TestStringParser.kt create mode 100644 mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/TestDoubleParser.kt create mode 100644 mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/TestIntParser.kt create mode 100644 mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/TestNumberParser.kt delete mode 100644 mcspring-api/mcspring-logging/build.gradle.kts delete mode 100644 mcspring-api/mcspring-logging/src/main/kotlin/in/kyle/mcspring/logging/LogCall.kt delete mode 100644 mcspring-api/mcspring-logging/src/main/kotlin/in/kyle/mcspring/logging/LogCallAspect.kt delete mode 100644 mcspring-api/mcspring-logging/src/main/kotlin/in/kyle/mcspring/logging/LoggingConfiguration.kt delete mode 100644 mcspring-api/mcspring-logging/src/main/kotlin/in/kyle/mcspring/logging/TestComponent.kt delete mode 100644 mcspring-api/mcspring-logging/src/main/resources/log4j2-mcspring.xml delete mode 100644 mcspring-api/mcspring-logging/src/main/resources/log4j2.xml delete mode 100644 mcspring-api/mcspring-logging/src/test/kotlin/in/kyle/mcspring/logging/TestLog.kt diff --git a/buildSrc/src/main/kotlin/mcspring-build.gradle.kts b/buildSrc/src/main/kotlin/mcspring-build.gradle.kts index 8a8cb50..c06111d 100644 --- a/buildSrc/src/main/kotlin/mcspring-build.gradle.kts +++ b/buildSrc/src/main/kotlin/mcspring-build.gradle.kts @@ -32,9 +32,16 @@ dependencies { implementation(kotlin("stdlib")) testImplementation("org.assertj:assertj-core:3.11.1") - testImplementation("org.amshove.kluent:kluent:1.61") testImplementation("org.mockito:mockito-core:2.+") + testImplementation("io.mockk:mockk:1.10.0") testImplementation("org.junit.jupiter:junit-jupiter:5.6.2") + + val kotestVersion = "4.1.0.RC2" + testImplementation("io.kotest:kotest-runner-junit5-jvm:$kotestVersion") + testImplementation("io.kotest:kotest-assertions-core-jvm:$kotestVersion") + testImplementation("io.kotest:kotest-property-jvm:$kotestVersion") + testImplementation("io.kotest:kotest-runner-console-jvm:$kotestVersion") + testImplementation("io.kotest:kotest-extensions-spring:$kotestVersion") } tasks.test { diff --git a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandBuilder.kt b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandBuilder.kt index a7b0774..206e5c1 100644 --- a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandBuilder.kt +++ b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandBuilder.kt @@ -1,5 +1,8 @@ package `in`.kyle.mcspring.commands.dsl +import `in`.kyle.mcspring.commands.dsl.parsers.* +import `in`.kyle.mcspring.commands.dsl.parsers.numbers.DoubleParser +import `in`.kyle.mcspring.commands.dsl.parsers.numbers.IntParser import org.bukkit.command.CommandSender import org.bukkit.command.ConsoleCommandSender import org.bukkit.entity.Player @@ -17,18 +20,11 @@ class CommandMeta { lateinit var executor: CommandExecutor fun preRegister() { - require(this::name.isInitialized) {"Command name not set"} - require(this::executor.isInitialized) {"Command executor not set for command $name"} + require(this::name.isInitialized) { "Command name not set" } + require(this::executor.isInitialized) { "Command executor not set for command $name" } } } -infix fun CommandMeta.executor(lambda: CommandBuilder.() -> Unit): CommandMeta { - executor = commandExecutor(lambda) - return this -} - -fun command(lambda: CommandMeta.() -> Unit) = CommandMeta().apply { lambda(this) } - data class CommandContext( val sender: CommandSender, val label: String, @@ -37,22 +33,15 @@ data class CommandContext( internal var argIndex: Int = 0, internal val runExecutors: Boolean = true ) { + val nextArg: String + get() { + return args.getOrNull( argIndex ) ?: error("Argument index out of bounds") + } fun nextArg() = args[argIndex] } data class CommandExecutor(val provider: (CommandContext) -> ParsedCommand) -fun commandExecutor(lambda: CommandBuilder.() -> Unit) = - CommandExecutor { context: CommandContext -> - CommandBuilder(context).let { - try { - it.lambda() - } catch (e: ContextReciever.BreakParseException) { - } - it.build() - } - } - @CommandParserBuilder class CommandBuilder(context: CommandContext) : ContextReciever(context) { @@ -88,12 +77,12 @@ class CommandBuilder(context: CommandContext) : ContextReciever(context) { parserSupplier: (String) -> P ) = arg(ValueBuilder(context, parserSupplier), lambda) - private fun > arg(builder: T, lambda: T.() -> Unit): Lazy { + private fun > arg(builder: T, lambda: T.() -> Unit): R { context.tabCompletions.clear() val arg = builder.apply(lambda).build() parsedArgs.add(arg) context.argIndex++ - return lazyOf(arg.returnValue) + return arg.returnValue ?: complete() } fun then(lambda: ContextReciever.() -> Unit) { @@ -115,7 +104,7 @@ abstract class ArgBuilder( open fun invalid(lambda: ContextReciever.(arg: String) -> Unit) { if (returnValue == null && context.runExecutors) { - lambda(context.nextArg()) + lambda(context.nextArg) complete() } } @@ -168,7 +157,8 @@ class ValueBuilder>( if (returnValue != null) { return ValueArg(returnValue!!) } else { - error("error parsing $context") + error("Error parsing $context, make sure to implement an `invalid` block in your " + + "command to catch failed argument parses.") } } } diff --git a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandFacade.kt b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandFacade.kt new file mode 100644 index 0000000..afcefac --- /dev/null +++ b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandFacade.kt @@ -0,0 +1,19 @@ +package `in`.kyle.mcspring.commands.dsl + +fun command(lambda: CommandMeta.() -> Unit) = CommandMeta().apply { lambda(this) } + +infix fun CommandMeta.executor(lambda: CommandBuilder.() -> Unit): CommandMeta { + executor = commandExecutor(lambda) + return this +} + +fun commandExecutor(lambda: CommandBuilder.() -> Unit) = + CommandExecutor { context: CommandContext -> + CommandBuilder(context).let { + try { + it.lambda() + } catch (e: ContextReciever.BreakParseException) { + } + it.build() + } + } diff --git a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandParsers.kt b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandParsers.kt deleted file mode 100644 index 35ce6ac..0000000 --- a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandParsers.kt +++ /dev/null @@ -1,120 +0,0 @@ -package `in`.kyle.mcspring.commands.dsl - -import org.bukkit.Bukkit -import org.bukkit.entity.Player - -class RunInvalidBlock : RuntimeException() - -typealias Error = (String) -> Unit - -@CommandParserBuilder -abstract class BaseParser( - context: CommandContext, - private val stringArg: String? -) : ContextReciever(context) { - - internal var returnValue: R? = null - - protected fun mapped(hints: Iterable = emptyList(), mapper: (String) -> R?) { - context.tabCompletions.addAll(hints) - if (returnValue == null) { - returnValue = stringArg?.let { mapper(it) } - } - } - - protected fun require( - hints: List = emptyList(), - predicate: (R) -> Boolean, - error: Error = {} - ) { - context.tabCompletions.addAll(hints) - val condition = predicate(returnValue!!) - if (returnValue != null && !condition) { - if (error == {}) { - returnValue = null - throw RunInvalidBlock() - } else if (context.runExecutors){ - error(stringArg!!) - complete() - } - } - } -} - -class MapParser(context: CommandContext, stringArg: String) : BaseParser(context, stringArg) { - fun map(vararg pairs: Pair) = map(mapOf(*pairs)) - - fun map(map: Map) = mapped(map.keys) { map[it] } -} - -class StringParser(context: CommandContext, stringArg: String) : BaseParser(context, stringArg) { - init { - returnValue = stringArg - } - - fun anyOf(vararg string: String, error: Error) = anyOf(string.toList(), error) - fun anyOf(strings: List, error: Error) = require(strings, { it in strings }, error) - fun anyString() = mapped { it } - fun anyStringMatching(regex: Regex, error: Error) = require(predicate = { it.matches(regex) }, error = error) - fun anyInt(error: Error) = require(predicate = { it.toIntOrNull() != null }, error = error) - fun anyDouble(error: Error) = require(predicate = { it.toDoubleOrNull() != null }, error = error) - fun anyFloat(error: Error) = require(predicate = { it.toFloatOrNull() != null }, error = error) - fun anyByte(error: Error) = require(predicate = { it.toByteOrNull() != null }, error = error) - fun anyShort(error: Error) = require(predicate = { it.toShortOrNull() != null }, error = error) - fun anyBoolean(error: Error) = anyOf("true", "false", error = error) -} - -abstract class NumberParser(context: CommandContext, stringArg: String) : BaseParser(context, stringArg) - where T : Number, - T : Comparable { - - init { - returnValue = parse(stringArg) - } - - internal abstract fun toNumber(s: String): T? - internal abstract fun zero(): T - internal abstract fun parse(s: String): T? - - fun between(lower: T, upper: T, error: Error) = require(predicate = { it > lower && it < upper }, error = error) - fun greaterThan(value: T, error: Error) = require(predicate = { it > value }, error = error) - fun greaterThanEqual(value: T, error: Error) = require(predicate = { it >= value }, error = error) - fun lessThan(value: T, error: Error) = require(predicate = { it < value }, error = error) - fun lessThanEqual(value: T, error: Error) = require(predicate = { it <= value }, error = error) - fun positive(error: Error) = require(predicate = { it > zero() }, error = error) - fun nonZero(value: T, error: Error) = require(predicate = { it != zero() }, error = error) -} - -class IntParser(context: CommandContext, stringArg: String) : NumberParser(context, stringArg) { - override fun zero() = 0 - override fun toNumber(s: String): Int? = s.toIntOrNull() - fun even(error: Error) = require(predicate = { it % 2 == 0 && it != 0 }, error = error) - fun odd(error: Error) = require(predicate = { it % 2 == 1 && it != 0 }, error = error) - fun between(range: IntRange, error: Error) = require(predicate = { it in range }, error = error) - override fun parse(s: String) = s.toIntOrNull() -} - -class DoubleParser(context: CommandContext, stringArg: String) : NumberParser(context, stringArg) { - override fun zero() = 0.0 - override fun toNumber(s: String): Double? = s.toDoubleOrNull() - override fun parse(s: String) = s.toDoubleOrNull() -} - -class BooleanParser(context: CommandContext, stringArg: String) : BaseParser(context, stringArg) { - init { - val booleans = listOf("true", "false") - context.tabCompletions.addAll(booleans) - returnValue = stringArg.toBoolean().takeIf { stringArg in booleans } - } -} - -class PlayerParser( - context: CommandContext, - stringArg: String -) : BaseParser(context, stringArg) { - init { - val players = Bukkit.getOnlinePlayers() - context.tabCompletions.addAll(players.map { it.name }) - returnValue = Bukkit.getPlayer(stringArg) - } -} diff --git a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/mcspring/CommandDslRegistration.kt b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/mcspring/CommandDslRegistration.kt index 0f12961..5ece209 100644 --- a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/mcspring/CommandDslRegistration.kt +++ b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/mcspring/CommandDslRegistration.kt @@ -2,11 +2,10 @@ package `in`.kyle.mcspring.commands.dsl.mcspring import `in`.kyle.mcspring.commands.dsl.CommandExecutor import `in`.kyle.mcspring.commands.dsl.CommandMeta -import `in`.kyle.mcspring.commands.dsl.CommandUtils +import `in`.kyle.mcspring.commands.dsl.util.CommandUtils import `in`.kyle.mcspring.commands.dsl.util.CommandMapWrapper import `in`.kyle.mcspring.util.SpringScanner import org.bukkit.plugin.Plugin -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean import org.springframework.context.ApplicationContext import org.springframework.context.ApplicationContextAware import org.springframework.context.annotation.Bean diff --git a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/BaseParser.kt b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/BaseParser.kt new file mode 100644 index 0000000..d438bb9 --- /dev/null +++ b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/BaseParser.kt @@ -0,0 +1,44 @@ +package `in`.kyle.mcspring.commands.dsl.parsers + +import `in`.kyle.mcspring.commands.dsl.CommandContext +import `in`.kyle.mcspring.commands.dsl.CommandParserBuilder +import `in`.kyle.mcspring.commands.dsl.ContextReciever + +class RunInvalidBlock : RuntimeException() + +typealias ConditionFail = (String) -> Unit + +@CommandParserBuilder +abstract class BaseParser( + context: CommandContext, + private val stringArg: String? +) : ContextReciever(context) { + + internal var returnValue: R? = null + + protected fun mapped(hints: Iterable = emptyList(), mapper: (String) -> R?) { + context.tabCompletions.addAll(hints) + if (returnValue == null) { + returnValue = stringArg?.let { mapper(it) } + } + } + + protected fun require( + hints: List = emptyList(), + predicate: (R) -> Boolean, + conditionFail: ConditionFail = {} + ) { + context.tabCompletions.addAll(hints) + val condition = predicate(returnValue!!) + if (returnValue != null && !condition) { + if (conditionFail == {}) { + returnValue = null + throw RunInvalidBlock() + } else if (context.runExecutors) { + conditionFail(stringArg!!) + complete() + } + } + } +} + diff --git a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/BooleanParser.kt b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/BooleanParser.kt new file mode 100644 index 0000000..9b9010a --- /dev/null +++ b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/BooleanParser.kt @@ -0,0 +1,16 @@ +package `in`.kyle.mcspring.commands.dsl.parsers + +import `in`.kyle.mcspring.commands.dsl.CommandContext + + +class BooleanParser(context: CommandContext, stringArg: String) : BaseParser(context, stringArg) { + init { + val booleans = listOf("true", "false") + context.tabCompletions.addAll(booleans) + + val lowered = stringArg.toLowerCase() + if (lowered in booleans) { + returnValue = lowered.toBoolean() + } + } +} diff --git a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/MapParser.kt b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/MapParser.kt new file mode 100644 index 0000000..946936f --- /dev/null +++ b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/MapParser.kt @@ -0,0 +1,9 @@ +package `in`.kyle.mcspring.commands.dsl.parsers + +import `in`.kyle.mcspring.commands.dsl.CommandContext + +class MapParser(context: CommandContext, stringArg: String) : BaseParser(context, stringArg) { + fun map(vararg pairs: Pair) = map(mapOf(*pairs)) + + fun map(map: Map) = mapped(map.keys) { map[it] } +} diff --git a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/PlayerParser.kt b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/PlayerParser.kt new file mode 100644 index 0000000..d5eb470 --- /dev/null +++ b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/PlayerParser.kt @@ -0,0 +1,16 @@ +package `in`.kyle.mcspring.commands.dsl.parsers + +import `in`.kyle.mcspring.commands.dsl.CommandContext +import org.bukkit.Bukkit +import org.bukkit.entity.Player + +class PlayerParser( + context: CommandContext, + stringArg: String +) : BaseParser(context, stringArg) { + init { + val players = Bukkit.getOnlinePlayers() + context.tabCompletions.addAll(players.map { it.name }) + returnValue = Bukkit.getPlayer(stringArg) + } +} diff --git a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/StringParser.kt b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/StringParser.kt new file mode 100644 index 0000000..293ee18 --- /dev/null +++ b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/StringParser.kt @@ -0,0 +1,20 @@ +package `in`.kyle.mcspring.commands.dsl.parsers + +import `in`.kyle.mcspring.commands.dsl.CommandContext + +class StringParser(context: CommandContext, stringArg: String) : BaseParser(context, stringArg) { + init { + returnValue = stringArg + } + + fun anyOf(vararg string: String, conditionFail: ConditionFail) = anyOf(string.toList(), conditionFail) + fun anyOf(strings: List, conditionFail: ConditionFail) = require(strings, { it in strings }, conditionFail) + fun anyString() = mapped { it } + fun anyStringMatching(regex: Regex, conditionFail: ConditionFail) = require(predicate = { it.matches(regex) }, conditionFail = conditionFail) + fun anyInt(conditionFail: ConditionFail) = require(predicate = { it.toIntOrNull() != null }, conditionFail = conditionFail) + fun anyDouble(conditionFail: ConditionFail) = require(predicate = { it.toDoubleOrNull() != null }, conditionFail = conditionFail) + fun anyFloat(conditionFail: ConditionFail) = require(predicate = { it.toFloatOrNull() != null }, conditionFail = conditionFail) + fun anyByte(conditionFail: ConditionFail) = require(predicate = { it.toByteOrNull() != null }, conditionFail = conditionFail) + fun anyShort(conditionFail: ConditionFail) = require(predicate = { it.toShortOrNull() != null }, conditionFail = conditionFail) + fun anyBoolean(conditionFail: ConditionFail) = anyOf("true", "false", conditionFail = conditionFail) +} diff --git a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/DoubleParser.kt b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/DoubleParser.kt new file mode 100644 index 0000000..100d5cb --- /dev/null +++ b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/DoubleParser.kt @@ -0,0 +1,9 @@ +package `in`.kyle.mcspring.commands.dsl.parsers.numbers + +import `in`.kyle.mcspring.commands.dsl.CommandContext + +class DoubleParser(context: CommandContext, stringArg: String) : NumberParser(context, stringArg) { + override fun zero() = 0.0 + override fun toNumber(s: String): Double? = s.toDoubleOrNull() + override fun parse(s: String) = s.toDoubleOrNull() +} diff --git a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/IntParser.kt b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/IntParser.kt new file mode 100644 index 0000000..323065f --- /dev/null +++ b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/IntParser.kt @@ -0,0 +1,13 @@ +package `in`.kyle.mcspring.commands.dsl.parsers.numbers + +import `in`.kyle.mcspring.commands.dsl.CommandContext +import `in`.kyle.mcspring.commands.dsl.parsers.ConditionFail + +class IntParser(context: CommandContext, stringArg: String) : NumberParser(context, stringArg) { + override fun zero() = 0 + override fun toNumber(s: String): Int? = s.toIntOrNull() + fun even(conditionFail: ConditionFail) = require(predicate = { it % 2 == 0 && it != 0 }, conditionFail = conditionFail) + fun odd(conditionFail: ConditionFail) = require(predicate = { it % 2 == 1 && it != 0 }, conditionFail = conditionFail) + fun between(range: IntRange, conditionFail: ConditionFail) = require(predicate = { it in range }, conditionFail = conditionFail) + override fun parse(s: String) = s.toIntOrNull() +} diff --git a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/NumberParser.kt b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/NumberParser.kt new file mode 100644 index 0000000..f600210 --- /dev/null +++ b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/NumberParser.kt @@ -0,0 +1,27 @@ +package `in`.kyle.mcspring.commands.dsl.parsers.numbers + +import `in`.kyle.mcspring.commands.dsl.parsers.BaseParser +import `in`.kyle.mcspring.commands.dsl.CommandContext +import `in`.kyle.mcspring.commands.dsl.parsers.ConditionFail + +abstract class NumberParser(context: CommandContext, stringArg: String) : BaseParser(context, stringArg) + where T : Number, + T : Comparable { + + init { + returnValue = parse(stringArg) + } + + internal abstract fun toNumber(s: String): T? + internal abstract fun zero(): T + internal abstract fun parse(s: String): T? + + fun between(lower: T, upper: T, conditionFail: ConditionFail) = require(predicate = { it > lower && it < upper }, conditionFail = conditionFail) + fun greaterThan(value: T, conditionFail: ConditionFail) = require(predicate = { it > value }, conditionFail = conditionFail) + fun greaterThanEqual(value: T, conditionFail: ConditionFail) = require(predicate = { it >= value }, conditionFail = conditionFail) + fun lessThan(value: T, conditionFail: ConditionFail) = require(predicate = { it < value }, conditionFail = conditionFail) + fun lessThanEqual(value: T, conditionFail: ConditionFail) = require(predicate = { it <= value }, conditionFail = conditionFail) + fun positive(conditionFail: ConditionFail) = require(predicate = { it > zero() }, conditionFail = conditionFail) + fun negative(conditionFail: ConditionFail) = require(predicate = { it < zero() }, conditionFail = conditionFail) + fun nonZero(conditionFail: ConditionFail) = require(predicate = { it != zero() }, conditionFail = conditionFail) +} diff --git a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandUtils.kt b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/util/CommandUtils.kt similarity index 87% rename from mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandUtils.kt rename to mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/util/CommandUtils.kt index c84d6c6..931dc97 100644 --- a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandUtils.kt +++ b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/util/CommandUtils.kt @@ -1,6 +1,7 @@ -package `in`.kyle.mcspring.commands.dsl +package `in`.kyle.mcspring.commands.dsl.util -import `in`.kyle.mcspring.commands.dsl.util.CommandMapWrapper +import `in`.kyle.mcspring.commands.dsl.CommandContext +import `in`.kyle.mcspring.commands.dsl.CommandMeta import org.bukkit.command.Command import org.bukkit.command.CommandSender import org.bukkit.plugin.Plugin diff --git a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/CommandTestSupport.kt b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/CommandTestSupport.kt index 194c5b2..2445d07 100644 --- a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/CommandTestSupport.kt +++ b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/CommandTestSupport.kt @@ -13,10 +13,18 @@ object CommandTestSupport { return messages.joinToString("\n") } - fun makeContext(command: String): Pair> { + fun makeContext( + command: String, + runExecutors: Boolean = true + ): Pair> { val (player, messages) = makeTestPlayer() val args = command.split(" ").filter { it.isNotBlank() } - val context = CommandContext(player, args.getOrElse(0) { "" }, args.toMutableList()) + val context = CommandContext( + player, + args.getOrElse(0) { "" }, + args.toMutableList(), + runExecutors = runExecutors + ) return Pair(context, messages) } @@ -28,4 +36,6 @@ object CommandTestSupport { .sendMessage(ArgumentMatchers.anyString()) return Pair(sender, messages) } + + fun makeBuilder(command: String) = CommandBuilder(makeContext(command).first) } diff --git a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestArgBuilder.kt b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestArgBuilder.kt new file mode 100644 index 0000000..efa134b --- /dev/null +++ b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestArgBuilder.kt @@ -0,0 +1,37 @@ +package `in`.kyle.mcspring.commands.dsl + +import `in`.kyle.mcspring.commands.dsl.CommandTestSupport.makeContext +import io.kotest.assertions.fail +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.style.FreeSpec + +class TestArgBuilder : FreeSpec({ + + class Test(context: CommandContext) : ArgBuilder(context) { + override fun build(): ValueArg { + return null as ValueArg + } + } + + "invalid block should run properly" - { + "invalid block runs when invalid" - { + val test = Test(makeContext("test").first) + test.returnValue = null + + class TestException : RuntimeException() + shouldThrow { + test.invalid { + throw TestException() + } + } + } + + "invalid block does not run when valid" - { + val test = Test(makeContext("test", runExecutors = false).first) + test.returnValue = null + test.invalid { + fail("should not run") + } + } + } +}) diff --git a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestCommandBuilder.kt b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestCommandBuilder.kt new file mode 100644 index 0000000..9d86089 --- /dev/null +++ b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestCommandBuilder.kt @@ -0,0 +1,46 @@ +package `in`.kyle.mcspring.commands.dsl + +import `in`.kyle.mcspring.commands.dsl.CommandTestSupport.runCommand +import io.kotest.assertions.fail +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.style.FreeSpec +import io.kotest.matchers.shouldBe +import io.mockk.mockk +import java.lang.RuntimeException + +class TestCommandBuilder : FreeSpec({ + + "require should work" - { + "require(true) should not fail" - { + runCommand("test") { + require({ true }) { fail("should not run") } + } + } + + "require(false) should fail" - { + runCommand("test") { + require({ false }) { message("should run") } + } shouldBe "should run" + } + } + + "then should only run while executors are enabled" - { + "test disabled" - { + CommandBuilder(CommandContext(mockk(), "test", listOf(), mutableListOf(), runExecutors = false)).apply { + then { + fail("should not run") + } + } + } + "test enabled" - { + runCommand("test") { + class TestException : RuntimeException() + shouldThrow { + then { + throw TestException() + } + } + } + } + } +}) diff --git a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestSubcommandBuilder.kt b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestSubcommandBuilder.kt new file mode 100644 index 0000000..378ad05 --- /dev/null +++ b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestSubcommandBuilder.kt @@ -0,0 +1,44 @@ +package `in`.kyle.mcspring.commands.dsl + +import `in`.kyle.mcspring.commands.dsl.CommandTestSupport.makeContext +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.style.FreeSpec +import io.kotest.matchers.shouldBe + +class TestSubcommandBuilder : FreeSpec({ + + "should run missing block" - { + val context = makeContext("").first + val builder = SubcommandBuilder(context) + + builder.apply { + shouldThrow { + missing {} + } + } + } + + "should run invalid block" - { + val context = makeContext("arg1").first + val builder = SubcommandBuilder(context) + + builder.apply { + shouldThrow { + invalid {} + } + } + } + + "should run on block" - { + val context = makeContext("sub1").first + val builder = SubcommandBuilder(context) + + builder.apply { + shouldThrow { + on("sub1") { + context.argIndex shouldBe 1 + } + } + } + } +}) diff --git a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestValueBuilder.kt b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestValueBuilder.kt new file mode 100644 index 0000000..a124863 --- /dev/null +++ b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestValueBuilder.kt @@ -0,0 +1,51 @@ +package `in`.kyle.mcspring.commands.dsl + +import `in`.kyle.mcspring.commands.dsl.CommandTestSupport.makeContext +import `in`.kyle.mcspring.commands.dsl.parsers.numbers.IntParser +import io.kotest.assertions.failure +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.assertions.throwables.shouldThrowMessage +import io.kotest.core.spec.style.FreeSpec +import io.kotest.matchers.shouldBe + +class TestValueBuilder : FreeSpec({ + + "default should set the return value" - { + val context = makeContext("one").first + val builder = ValueBuilder(context) { IntParser(context, it) } + + builder.apply { + default { 1 } + default { 2 } + } + + builder.returnValue shouldBe 1 + } + + "default should not overwrite" - { + val context = makeContext("one").first + val builder = ValueBuilder(context) { IntParser(context, it) } + builder.returnValue = 99 + + builder.apply { + default { 1 } + } + + builder.returnValue shouldBe 99 + } + + + "missing block should run when missing arg" - { + val context = makeContext("").first + val builder = ValueBuilder(context) { IntParser(context, it) } + + builder.apply { + class Test : RuntimeException() + shouldThrow { + missing { + throw Test() + } + } + } + } +}) diff --git a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestBaseRequirementBuilder.kt b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestBaseRequirementBuilder.kt deleted file mode 100644 index 486d4e8..0000000 --- a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestBaseRequirementBuilder.kt +++ /dev/null @@ -1,62 +0,0 @@ -package `in`.kyle.mcspring.commands.dsl.builders - -import `in`.kyle.mcspring.commands.dsl.CommandBuilder -import `in`.kyle.mcspring.commands.dsl.CommandTestSupport.makeContext -import `in`.kyle.mcspring.commands.dsl.commandExecutor -import org.amshove.kluent.shouldBeEqualTo -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.fail - -class TestBaseRequirementBuilder { - - @Test - fun testRequirePass() { - testCommand("test") { - require({ true }) { fail("should not run") } - } - } - - @Test - fun testRequireFail() { - testCommand("test") { - require({ false }) { message("should run") } - } shouldBeEqualTo "should run" - } - - @Test - fun testDefaultSet() { - testCommand("") { - val test by stringArg { - default { "test" } - default { "other" } - } - test shouldBeEqualTo "test" - } - } - - @Test - fun testMissing() { - testCommand("") { - val test by stringArg { - missing { message("should run") } - invalid { fail("should not run") } - } - } shouldBeEqualTo "should run" - } - - @Test - fun testInvalid() { - testCommand("test") { - val test by intArg { - missing { fail("should not run") } - invalid { message("should run") } - } - } shouldBeEqualTo "should run" - } - - private fun testCommand(commandString: String, lambda: CommandBuilder.() -> Unit): String { - val (context, messages) = makeContext(commandString) - commandExecutor(lambda).provider(context) - return messages.joinToString(" ") - } -} diff --git a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestIntRequirementBuilder.kt b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestIntRequirementBuilder.kt deleted file mode 100644 index 36a7beb..0000000 --- a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestIntRequirementBuilder.kt +++ /dev/null @@ -1,20 +0,0 @@ -package `in`.kyle.mcspring.commands.dsl.builders - -import `in`.kyle.mcspring.commands.dsl.CommandTestSupport.runCommand -import org.amshove.kluent.shouldBeEqualTo -import org.junit.jupiter.api.Test -import org.assertj.core.api.Assertions.fail - -class TestIntRequirementBuilder { - - @Test - fun testInt() { - runCommand("123") { - val arg0 by intArg { - invalid { fail("should not run") } - missing { fail("should not run") } - } - arg0 shouldBeEqualTo 123 - } - } -} diff --git a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestOnRequirementBuilder.kt b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestOnRequirementBuilder.kt deleted file mode 100644 index 8cf0bcb..0000000 --- a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestOnRequirementBuilder.kt +++ /dev/null @@ -1,44 +0,0 @@ -package `in`.kyle.mcspring.commands.dsl.builders - -import `in`.kyle.mcspring.commands.dsl.CommandTestSupport.runCommand -import org.amshove.kluent.shouldBeEqualTo -import org.assertj.core.api.Assertions.fail -import org.junit.jupiter.api.Test - -class TestOnRequirementBuilder { - - @Test - fun testSubcommandMissing() { - runCommand("") { - subcommand { - missing { message("should run") } - invalid { fail("should not run") } - } - fail("should not run") - } shouldBeEqualTo "should run" - } - - @Test - fun testSubcommandInvalid() { - runCommand("arg1") { - subcommand { - missing { fail("should not run") } - invalid { message("should run") } - } - fail("should not run") - } shouldBeEqualTo "should run" - } - - @Test - fun testSubcommandSimple() { - runCommand("sub1") { - subcommand { - on("sub1") { - context.argIndex shouldBeEqualTo 1 - } - missing { fail("should not run") } - invalid { fail("should not run") } - } - } - } -} diff --git a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestOptionRequirementBuilder.kt b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestOptionRequirementBuilder.kt deleted file mode 100644 index aa7010e..0000000 --- a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestOptionRequirementBuilder.kt +++ /dev/null @@ -1,40 +0,0 @@ -package `in`.kyle.mcspring.commands.dsl.builders - -class TestOptionRequirementBuilder { - -// @Test -// fun testOptionSimple() { -// runCommand("a") { -// with { -// options("a", mapping = true) -// invalid { fail("should not run") } -// missing { fail("should not run") } -// } shouldBe true -// context.isNotComplete() shouldBeEqualTo true -// } shouldBeEqualTo "" -// } -// -// @Test -// fun testOptionInvalid() { -// runCommand("asd") { -// with { -// options("a", mapping = true) -// invalid { message("should run") } -// missing { fail("should not run") } -// } shouldBe null -// context.complete shouldBeEqualTo true -// } shouldBeEqualTo "should run" -// } -// -// @Test -// fun testOptionMissing() { -// runCommand("") { -// with { -// options("a", mapping = true) -// invalid { fail("should not run") } -// missing { message("should run") } -// } shouldBe null -// context.complete shouldBeEqualTo true -// } shouldBeEqualTo "should run" -// } -} diff --git a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestStringRequirementBuilder.kt b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestStringRequirementBuilder.kt deleted file mode 100644 index f45ebe7..0000000 --- a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/builders/TestStringRequirementBuilder.kt +++ /dev/null @@ -1,47 +0,0 @@ -package `in`.kyle.mcspring.commands.dsl.builders - -import `in`.kyle.mcspring.commands.dsl.CommandTestSupport.runCommand -import org.amshove.kluent.shouldBeEqualTo -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.fail - -class TestStringRequirementBuilder { - - @Test - fun testSingleString() { - runCommand("test") { - val arg0 by stringArg { } - arg0 shouldBeEqualTo "test" - } - } - - @Test - fun testStringMissing() { - runCommand("") { - val arg0 by stringArg { - missing { message("missing arg") } - invalid { fail("should not run") } - } - fail("should not run") - } shouldBeEqualTo "missing arg" - } - - @Test - fun testStringMulti() { - runCommand("arg1 arg2") { - val arg0 by stringArg { - missing { fail("should not run") } - invalid { fail("should not run") } - } - - arg0 shouldBeEqualTo "arg1" - context.argIndex shouldBeEqualTo 1 - - val arg1 by stringArg { - missing { fail("should not run") } - invalid { fail("should not run") } - } - arg1 shouldBeEqualTo "arg2" - } - } -} diff --git a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/TestBooleanParser.kt b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/TestBooleanParser.kt new file mode 100644 index 0000000..774a462 --- /dev/null +++ b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/TestBooleanParser.kt @@ -0,0 +1,31 @@ +package `in`.kyle.mcspring.commands.dsl.parsers + +import `in`.kyle.mcspring.commands.dsl.CommandTestSupport.makeBuilder +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.style.FreeSpec +import io.kotest.matchers.shouldBe +import io.kotest.property.Arb +import io.kotest.property.arbitrary.filterNot +import io.kotest.property.arbitrary.stringPattern +import io.kotest.property.checkAll + +class TestBooleanParser : FreeSpec({ + + "true should parse" - { + makeBuilder("true").booleanArg { } shouldBe true + makeBuilder("TrUe").booleanArg { } shouldBe true + } + + "false should parse" - { + makeBuilder("false").booleanArg { } shouldBe false + makeBuilder("FaLsE").booleanArg { } shouldBe false + } + + "no other string should parse" - { + Arb.stringPattern("[a-zA-Z]{1,256}").filterNot { it in arrayOf("true", "false") }.checkAll { + shouldThrow { + makeBuilder(it).booleanArg { } + } + } + } +}) diff --git a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/TestMapParser.kt b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/TestMapParser.kt new file mode 100644 index 0000000..9cb5f13 --- /dev/null +++ b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/TestMapParser.kt @@ -0,0 +1,24 @@ +package `in`.kyle.mcspring.commands.dsl.parsers + +import `in`.kyle.mcspring.commands.dsl.CommandTestSupport.makeBuilder +import io.kotest.core.spec.style.FreeSpec +import io.kotest.matchers.shouldBe + +class TestMapParser : FreeSpec({ + + "test map vararg" - { + makeBuilder("one").mapArg { + parser { + map("one" to 1) + } + } shouldBe 1 + } + + "test map map" - { + makeBuilder("one").mapArg { + parser { + map(mapOf("one" to 1)) + } + } shouldBe 1 + } +}) diff --git a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/TestStringParser.kt b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/TestStringParser.kt new file mode 100644 index 0000000..be8dd58 --- /dev/null +++ b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/TestStringParser.kt @@ -0,0 +1,59 @@ +package `in`.kyle.mcspring.commands.dsl.parsers + +import `in`.kyle.mcspring.commands.dsl.CommandTestSupport.runCommand +import io.kotest.core.spec.style.FreeSpec +import io.kotest.matchers.shouldBe +import io.kotest.property.Arb +import io.kotest.property.arbitrary.stringPattern +import io.kotest.property.checkAll +import org.junit.jupiter.api.fail + +class TestStringParser : FreeSpec({ + + "simple string parses" - { + runCommand("test") { + val arg0 = stringArg { } + arg0 shouldBe "test" + } + } + + "missing called on empty string" - { + runCommand("") { + val arg0 = stringArg { + missing { message("missing arg") } + invalid { fail("should not run") } + } + fail("should not run") + } shouldBe "missing arg" + } + + "multiple strings are parses correctly" - { + runCommand("arg1 arg2") { + val arg0 = stringArg { + missing { fail("should not run") } + invalid { fail("should not run") } + } + + arg0 shouldBe "arg1" + context.argIndex shouldBe 1 + + val arg1 = stringArg { + missing { fail("should not run") } + invalid { fail("should not run") } + } + arg1 shouldBe "arg2" + } + } + + "test all strings parse correctly" - { + Arb.stringPattern("[^ ]{1,256}").checkAll { + runCommand(it) { + val arg1 = stringArg { + missing { fail("should not run") } + invalid { fail("should not run") } + } + arg1 shouldBe it + } + } + } +}) diff --git a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/TestDoubleParser.kt b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/TestDoubleParser.kt new file mode 100644 index 0000000..efe1871 --- /dev/null +++ b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/TestDoubleParser.kt @@ -0,0 +1,18 @@ +package `in`.kyle.mcspring.commands.dsl.parsers.numbers + +import `in`.kyle.mcspring.commands.dsl.CommandTestSupport.makeBuilder +import io.kotest.core.spec.style.FreeSpec +import io.kotest.matchers.shouldBe +import io.kotest.property.checkAll + +class TestDoubleParser : FreeSpec({ + "should parse simple double" - { + makeBuilder("0.0001").doubleArg { } shouldBe 0.0001 + } + + "should parse all doubles" - { + checkAll { + makeBuilder(it.toString()).doubleArg { } shouldBe it + } + } +}) diff --git a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/TestIntParser.kt b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/TestIntParser.kt new file mode 100644 index 0000000..d61fd68 --- /dev/null +++ b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/TestIntParser.kt @@ -0,0 +1,34 @@ +package `in`.kyle.mcspring.commands.dsl.parsers.numbers + +import `in`.kyle.mcspring.commands.dsl.CommandTestSupport.runCommand +import io.kotest.core.spec.style.FreeSpec +import io.kotest.matchers.shouldBe +import io.kotest.property.checkAll +import org.assertj.core.api.Assertions.fail + +class TestIntParser : FreeSpec({ + + "should parse int" - { + "should parse simple int" - { + runCommand("123") { + val arg0 = intArg { + invalid { fail("should not run") } + missing { fail("should not run") } + } + arg0 shouldBe 123 + } + } + + "should parse any int" - { + checkAll { + runCommand("$it") { + val testArg = intArg { + invalid { fail("should not run") } + missing { fail("should not run") } + } + testArg shouldBe it + } + } + } + } +}) diff --git a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/TestNumberParser.kt b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/TestNumberParser.kt new file mode 100644 index 0000000..094047c --- /dev/null +++ b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/TestNumberParser.kt @@ -0,0 +1,134 @@ +package `in`.kyle.mcspring.commands.dsl.parsers.numbers + +import `in`.kyle.mcspring.commands.dsl.CommandTestSupport.makeBuilder +import `in`.kyle.mcspring.commands.dsl.ContextReciever.BreakParseException +import io.kotest.assertions.fail +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.style.FreeSpec +import io.kotest.matchers.shouldBe + +class TestNumberParser : FreeSpec({ + + "between should work" - { + "between pass" - { + makeArg { + between(0, 100) { fail("should not run") } + } shouldBe 10 + } + "between fail" - { + makeArg { + shouldThrow { + between(0, 1) {} + } + } + } + } + + "greaterThan should work" - { + "gt pass" - { + makeArg { + greaterThan(0) { fail("should not run") } + } shouldBe 10 + } + "gt fail" - { + makeArg { + shouldThrow { + greaterThan(100) {} + } + } + } + } + + "greaterThanEqual should work" - { + "gte pass" - { + makeArg { + greaterThanEqual(10) { fail("should not run") } + } shouldBe 10 + } + "gte fail" - { + makeArg { + shouldThrow { + greaterThanEqual(100) {} + } + } + } + } + + "lessThan should work" - { + "lt pass" - { + makeArg { + lessThan(100) { fail("should not run") } + } shouldBe 10 + } + "lt fail" - { + makeArg { + shouldThrow { + lessThan(0) {} + } + } + } + } + + "lessThanEqual should work" - { + "lte pass" - { + makeArg { + lessThanEqual(10) { fail("should not run") } + } shouldBe 10 + } + "lte fail" - { + makeArg { + shouldThrow { + lessThanEqual(0) {} + } + } + } + } + + "positive should work" - { + "positive pass" - { + makeArg { + positive { fail("should not run") } + } shouldBe 10 + } + "positive fail" - { + makeArg(-10) { + shouldThrow { + positive {} + } + } + } + } + + "negative should work" - { + "negative pass" - { + makeArg(-1) { + negative { fail("should not run") } + } shouldBe -1 + } + "negative fail" - { + makeArg { + shouldThrow { + negative {} + } + } + } + } + + "nonzero should work" - { + "nonzero pass" - { + makeArg { + nonZero { fail("should not run") } + } shouldBe 10 + } + "nonzero fail" - { + makeArg(value = 0) { + shouldThrow { + nonZero {} + } + } + } + } +}) + +private fun makeArg(value: Int = 10, lambda: IntParser.() -> Unit) = + makeBuilder(value.toString()).intArg { parser(lambda) } diff --git a/mcspring-api/mcspring-logging/build.gradle.kts b/mcspring-api/mcspring-logging/build.gradle.kts deleted file mode 100644 index 2e5d25f..0000000 --- a/mcspring-api/mcspring-logging/build.gradle.kts +++ /dev/null @@ -1,14 +0,0 @@ -dependencies { - implementation("org.springframework.boot:spring-boot-starter-aop:") - implementation("org.springframework.boot:spring-boot-starter-log4j2:") - - testImplementation("org.springframework.boot:spring-boot-starter-test") - testImplementation("org.springframework.boot:spring-boot-starter-log4j2:") - testImplementation("org.springframework.boot:spring-boot-starter-aop:") -} - -configurations { - all { - exclude(group = "org.springframework.boot", module = "spring-boot-starter-logging") - } -} diff --git a/mcspring-api/mcspring-logging/src/main/kotlin/in/kyle/mcspring/logging/LogCall.kt b/mcspring-api/mcspring-logging/src/main/kotlin/in/kyle/mcspring/logging/LogCall.kt deleted file mode 100644 index 5a36dfa..0000000 --- a/mcspring-api/mcspring-logging/src/main/kotlin/in/kyle/mcspring/logging/LogCall.kt +++ /dev/null @@ -1,12 +0,0 @@ -package `in`.kyle.mcspring.logging - -import java.lang.annotation.Inherited - -@Target(AnnotationTarget.FUNCTION) -@Retention(AnnotationRetention.RUNTIME) -@Inherited -annotation class LogCall( - val logger: String = "mcspring", - val logParameters: Boolean = false -) - diff --git a/mcspring-api/mcspring-logging/src/main/kotlin/in/kyle/mcspring/logging/LogCallAspect.kt b/mcspring-api/mcspring-logging/src/main/kotlin/in/kyle/mcspring/logging/LogCallAspect.kt deleted file mode 100644 index 88d0a58..0000000 --- a/mcspring-api/mcspring-logging/src/main/kotlin/in/kyle/mcspring/logging/LogCallAspect.kt +++ /dev/null @@ -1,31 +0,0 @@ -package `in`.kyle.mcspring.logging - -import org.apache.logging.log4j.LogManager -import org.aspectj.lang.JoinPoint -import org.aspectj.lang.annotation.Aspect -import org.aspectj.lang.annotation.Before -import org.aspectj.lang.reflect.MethodSignature -import org.springframework.stereotype.Service - -@Aspect -@Service -class LogCallAspect { - - @Before("@within(in.kyle.mcspring.logging.LogCall) || @annotation(in.kyle.mcspring.logging.LogCall)") - fun logCall(joinPoint: JoinPoint) { - println("swag") - val sig = joinPoint.signature as MethodSignature - val annotation = sig.method.getAnnotation(LogCall::class.java) - val logger = LogManager.getLogger(annotation.logger) - val parameters = if (annotation.logParameters) { - joinPoint.args.zip(sig.method.parameters).joinToString(", ") { "${it.second.name}=${it.first}" } - } else { - "" - } - logger.trace("Call ${sig.method.name} $parameters") - println("swag") - println("swag") - println("swag") - println("swag") - } -} diff --git a/mcspring-api/mcspring-logging/src/main/kotlin/in/kyle/mcspring/logging/LoggingConfiguration.kt b/mcspring-api/mcspring-logging/src/main/kotlin/in/kyle/mcspring/logging/LoggingConfiguration.kt deleted file mode 100644 index da190db..0000000 --- a/mcspring-api/mcspring-logging/src/main/kotlin/in/kyle/mcspring/logging/LoggingConfiguration.kt +++ /dev/null @@ -1,25 +0,0 @@ -package `in`.kyle.mcspring.logging - -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration -import org.springframework.context.annotation.EnableAspectJAutoProxy -import org.springframework.core.io.support.PathMatchingResourcePatternResolver - - -@Configuration -@EnableAspectJAutoProxy -open class LoggingConfiguration { - @Bean("mcspring-logger") - open fun logger(): Logger { - registerConfigurations() - return LoggerFactory.getLogger("mcspring") - } - - private fun registerConfigurations() { - val loader = PathMatchingResourcePatternResolver() - val resources = loader.getResources("classpath*:log4j2-*.xml") - System.setProperty("log4j.configurationFile", resources.joinToString(separator = ",") { it.filename!! }) - } -} diff --git a/mcspring-api/mcspring-logging/src/main/kotlin/in/kyle/mcspring/logging/TestComponent.kt b/mcspring-api/mcspring-logging/src/main/kotlin/in/kyle/mcspring/logging/TestComponent.kt deleted file mode 100644 index dedfd7e..0000000 --- a/mcspring-api/mcspring-logging/src/main/kotlin/in/kyle/mcspring/logging/TestComponent.kt +++ /dev/null @@ -1,12 +0,0 @@ -package `in`.kyle.mcspring.logging - -import org.springframework.stereotype.Component - -@Component -open class TestComponent { - - @LogCall - fun test() { - println("called") - } -} diff --git a/mcspring-api/mcspring-logging/src/main/resources/log4j2-mcspring.xml b/mcspring-api/mcspring-logging/src/main/resources/log4j2-mcspring.xml deleted file mode 100644 index 2a201e0..0000000 --- a/mcspring-api/mcspring-logging/src/main/resources/log4j2-mcspring.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - %d{yyyy-MM-dd HH:mm:ss} %5p : %m%n - - - - - - - - - - - - - - - - diff --git a/mcspring-api/mcspring-logging/src/main/resources/log4j2.xml b/mcspring-api/mcspring-logging/src/main/resources/log4j2.xml deleted file mode 100644 index 9c851fe..0000000 --- a/mcspring-api/mcspring-logging/src/main/resources/log4j2.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - [%-5level] %d{yyyy-MM-dd HH:mm:ss} [%t] %c{1} - %msg%n - > - - - - - - - - - - diff --git a/mcspring-api/mcspring-logging/src/test/kotlin/in/kyle/mcspring/logging/TestLog.kt b/mcspring-api/mcspring-logging/src/test/kotlin/in/kyle/mcspring/logging/TestLog.kt deleted file mode 100644 index 8238bb8..0000000 --- a/mcspring-api/mcspring-logging/src/test/kotlin/in/kyle/mcspring/logging/TestLog.kt +++ /dev/null @@ -1,32 +0,0 @@ -package `in`.kyle.mcspring.logging - -import org.junit.jupiter.api.Test -import org.slf4j.LoggerFactory -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.SpringBootConfiguration -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.context.ApplicationContext -import org.springframework.context.annotation.ComponentScan -import org.springframework.context.annotation.Import - -@SpringBootTest -@Import(LogCallAspect::class) -@ComponentScan(basePackageClasses = [LogCall::class]) -open class TestLog { - - @Autowired - lateinit var test: TestComponent - - @Autowired - lateinit var ctx: ApplicationContext - - @Test - fun test() { - test.test() - LoggerFactory.getLogger("mcspring").info("wtf") - } - - @SpringBootConfiguration - open class Config { - } -} From d3fce5fdbcd9d43d6330eb99e396752fb6936f3a Mon Sep 17 00:00:00 2001 From: kylepls Date: Sat, 20 Jun 2020 12:31:41 -0600 Subject: [PATCH 33/54] More tests --- .../src/main/kotlin/mcspring-build.gradle.kts | 1 - .../dsl/mcspring/CommandDslRegistration.kt | 3 +- .../mcspring-gradle-plugin/build.gradle.kts | 36 +++++-- .../kotlin/in/kyle/mcspring/BuildPlugin.kt | 10 +- .../in/kyle/mcspring/McSpringExtension.kt | 11 +- .../in/kyle/mcspring/tasks/BuildPluginYml.kt | 33 +++--- .../in/kyle/mcspring/tasks/SetupSpigot.kt | 1 + .../src/main/resources/README.md | 0 .../src/main/resources/bukkit.yml | 0 .../src/main/resources/eula.txt | 0 .../src/main/resources/server.properties | 0 .../src/main/resources/spigot.yml | 0 .../in/kyle/mcspring/PluginTestSupport.kt | 38 +++++++ .../in/kyle/mcspring/TestBuildPlugin.kt | 23 ++++ .../kyle/mcspring/tasks/TestBuildPluginYml.kt | 101 ++++++++++++++++++ .../in/kyle/mcspring/tasks/TestSetupSpigot.kt | 30 ++++++ .../mcspring-plugin-manager/build.gradle.kts | 1 + settings.gradle.kts | 1 - 18 files changed, 259 insertions(+), 30 deletions(-) rename {buildSrc => mcspring-build/mcspring-gradle-plugin}/src/main/resources/README.md (100%) rename {buildSrc => mcspring-build/mcspring-gradle-plugin}/src/main/resources/bukkit.yml (100%) rename {buildSrc => mcspring-build/mcspring-gradle-plugin}/src/main/resources/eula.txt (100%) rename {buildSrc => mcspring-build/mcspring-gradle-plugin}/src/main/resources/server.properties (100%) rename {buildSrc => mcspring-build/mcspring-gradle-plugin}/src/main/resources/spigot.yml (100%) create mode 100644 mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/PluginTestSupport.kt create mode 100644 mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/TestBuildPlugin.kt create mode 100644 mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestBuildPluginYml.kt create mode 100644 mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestSetupSpigot.kt diff --git a/buildSrc/src/main/kotlin/mcspring-build.gradle.kts b/buildSrc/src/main/kotlin/mcspring-build.gradle.kts index c06111d..c8ecf67 100644 --- a/buildSrc/src/main/kotlin/mcspring-build.gradle.kts +++ b/buildSrc/src/main/kotlin/mcspring-build.gradle.kts @@ -31,7 +31,6 @@ dependencies { implementation(kotlin("stdlib")) - testImplementation("org.assertj:assertj-core:3.11.1") testImplementation("org.mockito:mockito-core:2.+") testImplementation("io.mockk:mockk:1.10.0") testImplementation("org.junit.jupiter:junit-jupiter:5.6.2") diff --git a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/mcspring/CommandDslRegistration.kt b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/mcspring/CommandDslRegistration.kt index 5ece209..21485b4 100644 --- a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/mcspring/CommandDslRegistration.kt +++ b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/mcspring/CommandDslRegistration.kt @@ -26,7 +26,8 @@ class CommandDslRegistration( } private fun registerAnnotatedCommands() { - scanner.scanMethods(Command::class.java).filterKeys { it !in registeredCommands } + scanner.scanMethods(Command::class.java) + .filterKeys { it !in registeredCommands } .filterKeys { it.returnType == CommandExecutor::class.java } .forEach { (key, value) -> val command = key.getAnnotation(Command::class.java) diff --git a/mcspring-build/mcspring-gradle-plugin/build.gradle.kts b/mcspring-build/mcspring-gradle-plugin/build.gradle.kts index 796f621..c24010b 100644 --- a/mcspring-build/mcspring-gradle-plugin/build.gradle.kts +++ b/mcspring-build/mcspring-gradle-plugin/build.gradle.kts @@ -12,13 +12,19 @@ repositories { mavenCentral() } -dependencies { - implementation("io.github.classgraph:classgraph:4.8.83") - implementation("org.yaml:snakeyaml:1.26") - implementation("com.github.jengelman.gradle.plugins:shadow:5.2.0") - implementation("org.springframework.boot:spring-boot-gradle-plugin:2.3.1.RELEASE") +val createClasspathManifest = tasks.create("createClasspathManifest") { + val outputDir = buildDir.resolve(name) - testImplementation(gradleTestKit()) + inputs.files(sourceSets.main.get().runtimeClasspath) + outputs.dir(outputDir) + logger.info("Dir to $outputDir") + + doLast { + outputDir.mkdirs() + val target = outputDir.resolve("plugin-classpath.txt") + target.writeText(sourceSets.main.get().runtimeClasspath.joinToString("\n")) + logger.info("Wrote to $target") + } } pluginBundle { @@ -37,3 +43,21 @@ gradlePlugin { } } } + +dependencies { + implementation("io.github.classgraph:classgraph:4.8.83") + implementation("org.yaml:snakeyaml:1.26") + implementation("com.github.jengelman.gradle.plugins:shadow:5.2.0") + implementation("org.springframework.boot:spring-boot-gradle-plugin:2.3.1.RELEASE") + + testImplementation(gradleTestKit()) + testImplementation("org.junit.jupiter:junit-jupiter:5.6.2") + + val kotestVersion = "4.1.0.RC2" + testImplementation("io.kotest:kotest-runner-junit5-jvm:$kotestVersion") + testImplementation("io.kotest:kotest-assertions-core-jvm:$kotestVersion") + testImplementation("io.kotest:kotest-property-jvm:$kotestVersion") + testImplementation("io.kotest:kotest-runner-console-jvm:$kotestVersion") + + testRuntimeOnly(files(createClasspathManifest)) +} diff --git a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/BuildPlugin.kt b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/BuildPlugin.kt index 3c48254..eba955d 100644 --- a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/BuildPlugin.kt +++ b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/BuildPlugin.kt @@ -48,7 +48,6 @@ class BuildPlugin : Plugin { private fun registerCopyToJarToSpigot(project: Project) { val props = project.extensions.findByType()!! project.tasks.create("copyJarToSpigot") { -// group = "mcspring" description = "Copy the built plugin jar to the Spigot directory" from(project.buildDir.resolve("libs")) @@ -61,20 +60,22 @@ class BuildPlugin : Plugin { private fun registerSetupSpigot(project: Project) { project.tasks.register("setupSpigot") { -// group = "mcspring" description = "Adds default Spigot configuration settings" } } private fun registerDownloadJar(project: Project) { project.tasks.register("downloadJar") { -// group = "mcspring" description = "Download the Bukkit jar" } } private fun registerBuildPluginJar(project: Project) { - val convention = project.convention.getPlugin(JavaPluginConvention::class.java) + val convention = try { + project.convention.getPlugin(JavaPluginConvention::class.java) + } catch (e: IllegalStateException) { + error("Kotlin/Java/Other plugin not found. Make sure to add one of these.") + } val mainSourceSet: SourceSet = convention.sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME) SpringBootExtension(project).buildInfo() @@ -133,7 +134,6 @@ class BuildPlugin : Plugin { private fun registerBuildPluginYml(project: Project) { project.tasks.register("buildPluginYml") { description = "Generates a and plugin.yml" -// group = "mcspring" } } } diff --git a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/McSpringExtension.kt b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/McSpringExtension.kt index 91a7889..68a82a5 100644 --- a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/McSpringExtension.kt +++ b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/McSpringExtension.kt @@ -1,6 +1,5 @@ package `in`.kyle.mcspring -import `in`.kyle.mcspring.tasks.BuildPluginYml import org.gradle.api.Project open class McSpringExtension(project: Project) { @@ -10,10 +9,10 @@ open class McSpringExtension(project: Project) { var spigotDirectory: String = project.projectDir.resolve("spigot").absolutePath var pluginMainPackage: String = "${project.group}.${project.name}" - var pluginName: String = project.name - var pluginVersion: String = project.version.toString().takeIf { it != "unspecified" } ?: "0.0.1" + var pluginName: String? = null + var pluginVersion: String? = null var pluginDescription: String? = project.description - var pluginLoad: BuildPluginYml.Load? = null + var pluginLoad: Any? = null var pluginAuthor: String? = null var pluginAuthors: List? = null var pluginWebsite: String? = null @@ -23,4 +22,8 @@ open class McSpringExtension(project: Project) { val pluginMainClassName: String get() = "$pluginMainPackage.SpringPlugin" + + enum class Load { + STARTUP, POSTWORLD + } } diff --git a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt index 2bb47cc..7457a77 100644 --- a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt +++ b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt @@ -19,15 +19,22 @@ open class BuildPluginYml : DefaultTask() { @TaskAction fun buildYml() { val props = project.extensions.findByType() - requireNotNull(props) {"mcspring not defined in build.gradle"} + requireNotNull(props) { "mcspring not defined in build.gradle" } with(props) { - attributes["name"] = pluginName - attributes["version"] = pluginVersion + attributes["name"] = pluginName ?: project.name + attributes["version"] = pluginVersion ?: project.version attributes["main"] = "$pluginMainPackage.SpringJavaPlugin" pluginDescription?.apply { attributes["description"] = this } - pluginLoad?.apply { attributes["load"] = this } + pluginLoad?.apply { + val value = when (this) { + is McSpringExtension.Load -> this.name.toLowerCase() + is String -> this + else -> error("Invalid pluginLoad value $this") + } + attributes["load"] = value + } pluginAuthor?.apply { attributes["author"] = this } pluginAuthors?.apply { attributes["authors"] = this } pluginWebsite?.apply { attributes["website"] = this } @@ -36,7 +43,6 @@ open class BuildPluginYml : DefaultTask() { pluginLoadBefore?.apply { attributes["loadbefore"] = this } // TODO commands - // TODO permissions addDependencies(project.configurations["runtime"].files) val outputFile = project.buildDir.resolve("resources").resolve("main") @@ -55,12 +61,15 @@ open class BuildPluginYml : DefaultTask() { scan.allClasses.filter { it.isStandardClass && it.hasAnnotation(annotation) } .map { it.getAnnotationInfo(annotation).parameterValues } .flatMap { (it["plugins"].value as Array).toList() } - - attributes["softdepend"] = getPluginDependencies("in.kyle.mcspring.annotation.SoftPluginDepend") - attributes["depend"] = getPluginDependencies("in.kyle.mcspring.annotation.PluginDepend") - } - - enum class Load { - STARTUP, POSTWORLD + getPluginDependencies("in.kyle.mcspring.annotation.SoftPluginDepend").apply { + if (this.isNotEmpty()) { + attributes["softdepend"] = this + } + } + getPluginDependencies("in.kyle.mcspring.annotation.PluginDepend").apply { + if (this.isNotEmpty()) { + attributes["depend"] = this + } + } } } diff --git a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/SetupSpigot.kt b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/SetupSpigot.kt index db2ac2d..6bbe2eb 100644 --- a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/SetupSpigot.kt +++ b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/SetupSpigot.kt @@ -28,6 +28,7 @@ open class SetupSpigot : DefaultTask() { target.parentFile.mkdirs() target.outputStream().use { os -> javaClass.getResourceAsStream(path).use { + requireNotNull(it) {"Could not find $path"} it.copyTo(os) } } diff --git a/buildSrc/src/main/resources/README.md b/mcspring-build/mcspring-gradle-plugin/src/main/resources/README.md similarity index 100% rename from buildSrc/src/main/resources/README.md rename to mcspring-build/mcspring-gradle-plugin/src/main/resources/README.md diff --git a/buildSrc/src/main/resources/bukkit.yml b/mcspring-build/mcspring-gradle-plugin/src/main/resources/bukkit.yml similarity index 100% rename from buildSrc/src/main/resources/bukkit.yml rename to mcspring-build/mcspring-gradle-plugin/src/main/resources/bukkit.yml diff --git a/buildSrc/src/main/resources/eula.txt b/mcspring-build/mcspring-gradle-plugin/src/main/resources/eula.txt similarity index 100% rename from buildSrc/src/main/resources/eula.txt rename to mcspring-build/mcspring-gradle-plugin/src/main/resources/eula.txt diff --git a/buildSrc/src/main/resources/server.properties b/mcspring-build/mcspring-gradle-plugin/src/main/resources/server.properties similarity index 100% rename from buildSrc/src/main/resources/server.properties rename to mcspring-build/mcspring-gradle-plugin/src/main/resources/server.properties diff --git a/buildSrc/src/main/resources/spigot.yml b/mcspring-build/mcspring-gradle-plugin/src/main/resources/spigot.yml similarity index 100% rename from buildSrc/src/main/resources/spigot.yml rename to mcspring-build/mcspring-gradle-plugin/src/main/resources/spigot.yml diff --git a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/PluginTestSupport.kt b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/PluginTestSupport.kt new file mode 100644 index 0000000..ce20796 --- /dev/null +++ b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/PluginTestSupport.kt @@ -0,0 +1,38 @@ +package `in`.kyle.mcspring + +import org.gradle.testkit.runner.BuildResult +import org.gradle.testkit.runner.GradleRunner +import java.io.File + +fun writeBaseGradleConfig(file: File) { + file += """ + |plugins { + | id("org.jetbrains.kotlin.jvm") version "1.3.72" + | id("in.kyle.mcspring") + |} + | + |repositories { + | jcenter() + |} + """.trimMargin() +} + +fun runGradle(folder: File, vararg args: String): BuildResult { + return GradleRunner.create() + .withProjectDir(folder) + .withArguments(*args, "--stacktrace") + .withPluginClasspath(getPluginClasspath()) + .withDebug(true) + .build() +} + +private fun getPluginClasspath(): List { + val pluginClasspathResource = {}::class.java.classLoader.getResource("plugin-classpath.txt") + ?: error("Did not find plugin classpath resource, run `testClasses` build task.") + return pluginClasspathResource.readText().lines().map { File(it) } +} + +operator fun File.plusAssign(string: String) = appendText("\n$string") + +operator fun File.div(string: String) = resolve(string) + diff --git a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/TestBuildPlugin.kt b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/TestBuildPlugin.kt new file mode 100644 index 0000000..a915f42 --- /dev/null +++ b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/TestBuildPlugin.kt @@ -0,0 +1,23 @@ +package `in`.kyle.mcspring + +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.style.FreeSpec +import org.gradle.testkit.runner.UnexpectedBuildFailure + +class TestBuildPlugin : FreeSpec({ + + "should throw exception if missing source plugin" - { + val folder = createTempDir() + val buildFile = folder.resolve("build.gradle.kts") + + buildFile += """ + |plugins { + | id("in.kyle.mcspring") + |} + """.trimMargin() + + shouldThrow { + runGradle(folder, "build") + } + } +}) diff --git a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestBuildPluginYml.kt b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestBuildPluginYml.kt new file mode 100644 index 0000000..a72b932 --- /dev/null +++ b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestBuildPluginYml.kt @@ -0,0 +1,101 @@ +package `in`.kyle.mcspring.tasks + +import `in`.kyle.mcspring.div +import `in`.kyle.mcspring.plusAssign +import `in`.kyle.mcspring.runGradle +import `in`.kyle.mcspring.writeBaseGradleConfig +import io.kotest.assertions.assertSoftly +import io.kotest.core.spec.style.FreeSpec +import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder +import io.kotest.matchers.shouldBe +import io.kotest.matchers.string.shouldEndWith +import org.gradle.testkit.runner.TaskOutcome +import org.yaml.snakeyaml.Yaml + +class TestBuildPluginYml : FreeSpec({ + + "should write minimum required info" - { + val folder = createTempDir() + val buildFile = folder / "build.gradle.kts" + writeBaseGradleConfig(buildFile) + buildFile += """ + version = "29" + """.trimIndent() + + val result = runGradle(folder, "buildPluginYml") + val task = result.task(":buildPluginYml")!! + task.outcome shouldBe TaskOutcome.SUCCESS + + val pluginYml = folder / "build" / "resources" / "main" / "plugin.yml" + val yml = Yaml().load>(pluginYml.inputStream()) + + assertSoftly { + yml.keys shouldContainExactlyInAnyOrder listOf("name", "version", "main") + + yml["name"].toString() shouldEndWith ".tmp" + yml["version"] shouldBe "29" + yml["main"].toString() shouldEndWith ".SpringJavaPlugin" + } + } + + "should write all user-specified info properly" - { + val folder = createTempDir() + val buildFile = folder / "build.gradle" + writeBaseGradleConfig(buildFile) + // For some reason this doesn't work with the Kotlin dsl??? + buildFile += """ + |mcspring { + | pluginDescription = "test description" + | pluginLoad = "startup" + | pluginAuthor = "kyle" + | pluginWebsite = "github" + | pluginDatabase = true + | pluginPrefix = "prefix" + | pluginAuthors = ["kyle1", "kyle2"] + | pluginLoadBefore = ["other"] + |} + """.trimMargin() + + val result = runGradle(folder, "buildPluginYml") + val task = result.task(":buildPluginYml")!! + task.outcome shouldBe TaskOutcome.SUCCESS + + val pluginYml = folder / "build" / "resources" / "main" / "plugin.yml" + val yml = Yaml().load>(pluginYml.inputStream()) + + assertSoftly { + yml["description"] shouldBe "test description" + yml["load"] shouldBe "startup" + yml["author"] shouldBe "kyle" + (yml["authors"] as List) shouldContainExactlyInAnyOrder listOf("kyle1", "kyle2") + yml["website"] shouldBe "github" + yml["database"] shouldBe true + yml["prefix"] shouldBe "prefix" + yml["loadbefore"] shouldBe listOf("other") + } + } + + "should write dependency tags" - { + val folder = createTempDir() + val buildFile = folder / "build.gradle.kts" + writeBaseGradleConfig(buildFile) + + val srcFile = folder / "src" / "main" / "kotlin" / "Example.kt" + srcFile += """ + |import `in`.kyle.mcspring.annotation.PluginDepend + |@PluginDepend("plugin") + |class Example + """.trimMargin() + + val result = runGradle(folder, "buildPluginYml") + val task = result.task(":buildPluginYml")!! + task.outcome shouldBe TaskOutcome.SUCCESS + + val pluginYml = folder / "build" / "resources" / "main" / "plugin.yml" + val yml = Yaml().load>(pluginYml.inputStream()) + + assertSoftly { + yml["depend"] shouldBe "plugin" + } + } +}) diff --git a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestSetupSpigot.kt b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestSetupSpigot.kt new file mode 100644 index 0000000..af47d84 --- /dev/null +++ b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestSetupSpigot.kt @@ -0,0 +1,30 @@ +package `in`.kyle.mcspring.tasks + +import `in`.kyle.mcspring.div +import `in`.kyle.mcspring.plusAssign +import `in`.kyle.mcspring.runGradle +import `in`.kyle.mcspring.writeBaseGradleConfig +import io.kotest.core.spec.style.FreeSpec +import io.kotest.matchers.file.shouldNotBeEmpty +import io.kotest.matchers.shouldBe +import org.gradle.testkit.runner.TaskOutcome + +class TestSetupSpigot : FreeSpec({ + + "setup spigot should make required files" - { + val folder = createTempDir() + val buildFile = folder / "build.gradle.kts" + writeBaseGradleConfig(buildFile) + + val result = runGradle(folder, "setupSpigot") + val task = result.task(":setupSpigot")!! + task.outcome shouldBe TaskOutcome.SUCCESS + + val spigot = folder/"spigot" + (spigot/"bukkit.yml").shouldNotBeEmpty() + (spigot/"eula.txt").shouldNotBeEmpty() + (spigot/"README.md").shouldNotBeEmpty() + (spigot/"server.properties").shouldNotBeEmpty() + (spigot/"spigot.yml").shouldNotBeEmpty() + } +}) diff --git a/mcspring-build/mcspring-plugin-manager/build.gradle.kts b/mcspring-build/mcspring-plugin-manager/build.gradle.kts index 8373e5e..d5203f1 100644 --- a/mcspring-build/mcspring-plugin-manager/build.gradle.kts +++ b/mcspring-build/mcspring-plugin-manager/build.gradle.kts @@ -14,3 +14,4 @@ mcspring { tasks.build { dependsOn(tasks.named("buildServer")) } + diff --git a/settings.gradle.kts b/settings.gradle.kts index 78f1882..01c19ac 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -11,7 +11,6 @@ include(":mcspring-api:mcspring-vault") include(":mcspring-api:mcspring-rx") include(":mcspring-api:mcspring-guis") include(":mcspring-api:mcspring-chat") -include(":mcspring-api:mcspring-logging") include(":mcspring-build") include(":mcspring-build:mcspring-plugin-manager") From 7da88056914b25a1a286764af09e83431ecc181c Mon Sep 17 00:00:00 2001 From: kylepls Date: Sun, 21 Jun 2020 01:30:37 -0600 Subject: [PATCH 34/54] Added tests to gradle plugin and restructured project --- build.gradle.kts | 2 + .../src/main/kotlin/mcspring-build.gradle.kts | 22 +-- .../main/kotlin/mcspring-publish.gradle.kts | 3 + mcspring-api/mcspring-base/build.gradle.kts | 4 + .../mcspring/javaplugin/SpringJavaPlugin.java | 3 +- .../in/kyle/mcspring/chat/TestMessage.kt | 12 +- .../commands/dsl/parsers/TestStringParser.kt | 2 +- .../dsl/parsers/numbers/TestIntParser.kt | 2 +- .../mcspring-gradle-plugin/build.gradle.kts | 5 + .../settings.gradle.kts | 1 - .../kotlin/in/kyle/mcspring/BuildPlugin.kt | 129 ++++------------- .../kotlin/in/kyle/mcspring/Extensions.kt | 23 ++++ .../in/kyle/mcspring/tasks/BuildPluginJar.kt | 75 ++++++++++ .../in/kyle/mcspring/tasks/BuildPluginYml.kt | 130 +++++++++++++----- .../in/kyle/mcspring/PluginTestSupport.kt | 7 +- .../kyle/mcspring/tasks/TestBuildPluginJar.kt | 73 ++++++++++ .../kyle/mcspring/tasks/TestBuildPluginYml.kt | 102 +++++++++++++- .../in/kyle/mcspring/tasks/TestDownloadJar.kt | 25 ++++ .../in/kyle/mcspring/tasks/TestSetupSpigot.kt | 16 ++- .../manager/commands/CommandClassLoader.kt | 2 +- .../manager/commands/CommandGamemode.kt | 2 +- .../mcspring/manager/commands/CommandHeal.kt | 4 +- .../mcspring/manager/commands/CommandOp.kt | 2 +- .../manager/commands/CommandPlugin.kt | 4 +- .../mcspring/manager/commands/CommandSpeed.kt | 2 +- settings.gradle.kts | 6 +- 26 files changed, 466 insertions(+), 192 deletions(-) rename mcspring-api/mcspring-base/src/main/java/{ => org/springframework/boot/loader}/in/kyle/mcspring/javaplugin/SpringJavaPlugin.java (92%) delete mode 100644 mcspring-build/mcspring-gradle-plugin/settings.gradle.kts create mode 100644 mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/Extensions.kt create mode 100644 mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginJar.kt create mode 100644 mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestBuildPluginJar.kt create mode 100644 mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestDownloadJar.kt diff --git a/build.gradle.kts b/build.gradle.kts index 108de07..3974385 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -11,3 +11,5 @@ subprojects { apply(plugin = "mcspring-build") apply(plugin = "mcspring-publish") } + +defaultTasks("build") diff --git a/buildSrc/src/main/kotlin/mcspring-build.gradle.kts b/buildSrc/src/main/kotlin/mcspring-build.gradle.kts index c8ecf67..00d2fd7 100644 --- a/buildSrc/src/main/kotlin/mcspring-build.gradle.kts +++ b/buildSrc/src/main/kotlin/mcspring-build.gradle.kts @@ -1,7 +1,6 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { - id("io.spring.dependency-management") kotlin("jvm") } @@ -9,25 +8,14 @@ repositories { jcenter() mavenCentral() mavenLocal() -} - -dependencyManagement { - dependencies { - dependency("org.spigotmc:spigot-api:1.15.2-R0.1-SNAPSHOT") { - repositories { - maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") - maven("https://oss.sonatype.org/content/repositories/snapshots") - } - } - } - imports { - mavenBom(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES) - } + maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") + maven("https://oss.sonatype.org/content/repositories/snapshots") } dependencies { - compileOnly("org.spigotmc:spigot-api") - testImplementation("org.spigotmc:spigot-api") + val spigotVersion = "1.15.2-R0.1-SNAPSHOT" + compileOnly("org.spigotmc:spigot-api:$spigotVersion") + testImplementation("org.spigotmc:spigot-api:$spigotVersion") implementation(kotlin("stdlib")) diff --git a/buildSrc/src/main/kotlin/mcspring-publish.gradle.kts b/buildSrc/src/main/kotlin/mcspring-publish.gradle.kts index 6933a46..71bd5a6 100644 --- a/buildSrc/src/main/kotlin/mcspring-publish.gradle.kts +++ b/buildSrc/src/main/kotlin/mcspring-publish.gradle.kts @@ -79,3 +79,6 @@ bintray { vcsUrl = "https://github.com/kylepls/mcspring" }) } + +tasks.findByName("build")!!.dependsOn(tasks.findByName("publishToMavenLocal")) + diff --git a/mcspring-api/mcspring-base/build.gradle.kts b/mcspring-api/mcspring-base/build.gradle.kts index fdd6c23..3e262b3 100644 --- a/mcspring-api/mcspring-base/build.gradle.kts +++ b/mcspring-api/mcspring-base/build.gradle.kts @@ -4,12 +4,16 @@ plugins { dependencies { // compileOnly("org.apache.logging.log4j:log4j-core:2.12.1") + api(platform("org.springframework.boot:spring-boot-dependencies:2.3.1.RELEASE")) api("org.springframework.boot:spring-boot-loader") { exclude("org.springframework.boot", "spring-boot-dependencies") } api("org.springframework.boot:spring-boot-starter") { exclude("org.springframework.boot", "spring-boot-starter-logging") exclude("org.springframework.boot", "spring-boot-dependencies") + exclude("org.springframework", "spring-jcl") + exclude("org.springframework", "spring-expression") + exclude("org.yaml", "snakeyaml") } implementation("io.github.classgraph:classgraph:4.8.83") diff --git a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/javaplugin/SpringJavaPlugin.java b/mcspring-api/mcspring-base/src/main/java/org/springframework/boot/loader/in/kyle/mcspring/javaplugin/SpringJavaPlugin.java similarity index 92% rename from mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/javaplugin/SpringJavaPlugin.java rename to mcspring-api/mcspring-base/src/main/java/org/springframework/boot/loader/in/kyle/mcspring/javaplugin/SpringJavaPlugin.java index 3483859..e3e86ca 100644 --- a/mcspring-api/mcspring-base/src/main/java/in/kyle/mcspring/javaplugin/SpringJavaPlugin.java +++ b/mcspring-api/mcspring-base/src/main/java/org/springframework/boot/loader/in/kyle/mcspring/javaplugin/SpringJavaPlugin.java @@ -1,4 +1,5 @@ -package in.kyle.mcspring.javaplugin; +// Package name for hiding the class in the final jar +package org.springframework.boot.loader.in.kyle.mcspring.javaplugin; import org.bukkit.plugin.java.JavaPlugin; import org.springframework.boot.loader.JarLauncher; diff --git a/mcspring-api/mcspring-chat/src/test/kotlin/in/kyle/mcspring/chat/TestMessage.kt b/mcspring-api/mcspring-chat/src/test/kotlin/in/kyle/mcspring/chat/TestMessage.kt index 4b86e71..59f1f39 100644 --- a/mcspring-api/mcspring-chat/src/test/kotlin/in/kyle/mcspring/chat/TestMessage.kt +++ b/mcspring-api/mcspring-chat/src/test/kotlin/in/kyle/mcspring/chat/TestMessage.kt @@ -6,6 +6,7 @@ import `in`.kyle.mcspring.chat.StringSupport.hover import `in`.kyle.mcspring.chat.StringSupport.suggest import `in`.kyle.mcspring.chat.StringSupport.toTextComponent import `in`.kyle.mcspring.chat.TextComponentSupport.plus +import io.kotest.matchers.shouldBe import net.md_5.bungee.api.ChatColor import net.md_5.bungee.api.chat.ClickEvent import net.md_5.bungee.api.chat.ClickEvent.Action.* @@ -13,7 +14,6 @@ import net.md_5.bungee.api.chat.ComponentBuilder import net.md_5.bungee.api.chat.HoverEvent import net.md_5.bungee.api.chat.HoverEvent.Action.SHOW_TEXT import net.md_5.bungee.api.chat.TextComponent -import org.amshove.kluent.shouldBeEqualTo import org.bukkit.ChatColor.RED import org.junit.jupiter.api.Test @@ -21,7 +21,7 @@ class TestMessage { @Test fun testHover() { - "test" hover "hover" shouldBeEqualTo + "test" hover "hover" shouldBe TextComponent("test").apply { hoverEvent = HoverEvent(SHOW_TEXT, arrayOf(TextComponent("hover"))) } @@ -29,7 +29,7 @@ class TestMessage { @Test fun testColor() { - "test" color RED shouldBeEqualTo + "test" color RED shouldBe ComponentBuilder("test").color(ChatColor.RED).create()[0] } @@ -37,13 +37,13 @@ class TestMessage { fun testPlus() { val component = "hello ".toTextComponent() + "world" // ofc the equality checking doesn't work... - component.toString() shouldBeEqualTo + component.toString() shouldBe TextComponent(TextComponent("hello "), TextComponent("world")).toString() } @Test fun testCommand() { - "test" command "/test" shouldBeEqualTo + "test" command "/test" shouldBe TextComponent("test").apply { clickEvent = ClickEvent(RUN_COMMAND, "/test") } @@ -51,7 +51,7 @@ class TestMessage { @Test fun testSuggest() { - "test" suggest "/test" shouldBeEqualTo + "test" suggest "/test" shouldBe TextComponent("test").apply { clickEvent = ClickEvent(SUGGEST_COMMAND, "/test") } diff --git a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/TestStringParser.kt b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/TestStringParser.kt index be8dd58..9fea91c 100644 --- a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/TestStringParser.kt +++ b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/TestStringParser.kt @@ -46,7 +46,7 @@ class TestStringParser : FreeSpec({ } "test all strings parse correctly" - { - Arb.stringPattern("[^ ]{1,256}").checkAll { + Arb.stringPattern("[a-zA-Z0-9]{1,256}").checkAll { runCommand(it) { val arg1 = stringArg { missing { fail("should not run") } diff --git a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/TestIntParser.kt b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/TestIntParser.kt index d61fd68..de35318 100644 --- a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/TestIntParser.kt +++ b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/TestIntParser.kt @@ -1,10 +1,10 @@ package `in`.kyle.mcspring.commands.dsl.parsers.numbers import `in`.kyle.mcspring.commands.dsl.CommandTestSupport.runCommand +import io.kotest.assertions.fail import io.kotest.core.spec.style.FreeSpec import io.kotest.matchers.shouldBe import io.kotest.property.checkAll -import org.assertj.core.api.Assertions.fail class TestIntParser : FreeSpec({ diff --git a/mcspring-build/mcspring-gradle-plugin/build.gradle.kts b/mcspring-build/mcspring-gradle-plugin/build.gradle.kts index c24010b..cac2ab6 100644 --- a/mcspring-build/mcspring-gradle-plugin/build.gradle.kts +++ b/mcspring-build/mcspring-gradle-plugin/build.gradle.kts @@ -49,9 +49,14 @@ dependencies { implementation("org.yaml:snakeyaml:1.26") implementation("com.github.jengelman.gradle.plugins:shadow:5.2.0") implementation("org.springframework.boot:spring-boot-gradle-plugin:2.3.1.RELEASE") + implementation(project(":mcspring-api:mcspring-base")) + implementation(project(":mcspring-api:mcspring-commands-dsl")) testImplementation(gradleTestKit()) testImplementation("org.junit.jupiter:junit-jupiter:5.6.2") + implementation("io.github.classgraph:classgraph:4.8.83") + testImplementation(project(":mcspring-api:mcspring-base")) + testImplementation(project(":mcspring-api:mcspring-commands-dsl")) val kotestVersion = "4.1.0.RC2" testImplementation("io.kotest:kotest-runner-junit5-jvm:$kotestVersion") diff --git a/mcspring-build/mcspring-gradle-plugin/settings.gradle.kts b/mcspring-build/mcspring-gradle-plugin/settings.gradle.kts deleted file mode 100644 index 8177156..0000000 --- a/mcspring-build/mcspring-gradle-plugin/settings.gradle.kts +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = "mcspring-gradle-plugin" diff --git a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/BuildPlugin.kt b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/BuildPlugin.kt index eba955d..29bbfe3 100644 --- a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/BuildPlugin.kt +++ b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/BuildPlugin.kt @@ -1,139 +1,62 @@ package `in`.kyle.mcspring +import `in`.kyle.mcspring.tasks.BuildPluginJar import `in`.kyle.mcspring.tasks.BuildPluginYml import `in`.kyle.mcspring.tasks.DownloadJar import `in`.kyle.mcspring.tasks.SetupSpigot -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import org.gradle.api.Plugin import org.gradle.api.Project -import org.gradle.api.plugins.JavaPluginConvention import org.gradle.api.tasks.Copy -import org.gradle.api.tasks.SourceSet -import org.gradle.api.tasks.bundling.ZipEntryCompression import org.gradle.kotlin.dsl.create -import org.gradle.kotlin.dsl.findByType import org.gradle.kotlin.dsl.register -import org.springframework.boot.gradle.dsl.SpringBootExtension -import org.springframework.boot.gradle.tasks.bundling.BootJar import java.io.File -import java.util.concurrent.Callable class BuildPlugin : Plugin { override fun apply(project: Project) { project.extensions.create("mcspring", project) - registerBuildPluginYml(project) - registerBuildPluginJar(project) - registerDownloadJar(project) - registerSetupSpigot(project) - registerCopyToJarToSpigot(project) - registerBuildServer(project) - } - private fun registerBuildServer(project: Project) { - project.tasks.register("buildServer") { - group = "mcspring" - description = "Performs a complete sever assembly (this is what you're looking for)" - - dependsOn( - "downloadJar", - "setupSpigot", - "buildPluginYml", - "pluginJar", - "copyJarToSpigot" - ) + project.tasks.register("buildPluginYml") { + description = "Generates a and plugin.yml" + dependsOn(project.tasks.findByName("classes")) } - } - private fun registerCopyToJarToSpigot(project: Project) { - val props = project.extensions.findByType()!! - project.tasks.create("copyJarToSpigot") { - description = "Copy the built plugin jar to the Spigot directory" - - from(project.buildDir.resolve("libs")) - val pluginsDir = File(props.spigotDirectory).resolve("plugins") - pluginsDir.mkdirs() - mustRunAfter("pluginJar") - into(pluginsDir) + project.tasks.register("downloadJar") { + description = "Download the Bukkit jar" } - } - private fun registerSetupSpigot(project: Project) { project.tasks.register("setupSpigot") { description = "Adds default Spigot configuration settings" } - } - private fun registerDownloadJar(project: Project) { - project.tasks.register("downloadJar") { - description = "Download the Bukkit jar" + project.tasks.register("buildPluginJar") { + description = "Builds the plugin jar with dependencies" } - } - - private fun registerBuildPluginJar(project: Project) { - val convention = try { - project.convention.getPlugin(JavaPluginConvention::class.java) - } catch (e: IllegalStateException) { - error("Kotlin/Java/Other plugin not found. Make sure to add one of these.") - } - val mainSourceSet: SourceSet = convention.sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME) - - SpringBootExtension(project).buildInfo() - - val springJar = project.tasks.create("springJar") { - mainClassName = "ignored" + project.tasks.create("copyJarToSpigot") { + description = "Copy the built plugin jar to the Spigot directory" + val props = project.extensions.mcspring + from(project.buildDir / "libs") + val pluginsDir = File(props.spigotDirectory) / "plugins" doFirst { - project.buildDir.resolve("libs").listFiles()?.forEach { it.delete() } + pluginsDir.mkdirs() } - classpath(Callable { mainSourceSet.runtimeClasspath }) - - archiveFileName.set("spring.jar") - dependsOn(project.tasks.named("classes")) + mustRunAfter("buildServer") + into(pluginsDir) } - val builtSpringJar = project.buildDir.resolve("libs").resolve("spring.jar") - project.tasks.create("pluginJar") { - description = "Generates a plugin jar" -// group = "mcspring" - - entryCompression = ZipEntryCompression.STORED - from(builtSpringJar) - archiveClassifier.set("") - - from(Callable { - mainSourceSet.runtimeClasspath.filter { - "mcspring-base" in it.name - }.map { - project.zipTree(it).matching { - include { - it.isDirectory || "javaplugin" in it.path - } - } - } - }) - - relocate("BOOT-INF/classes/", "") - doFirst { - val props = project.extensions.findByType()!! - relocate("in.kyle.mcspring.javaplugin", props.pluginMainPackage) - } - - includeEmptyDirs = false - dependsOn(springJar) - - mustRunAfter("buildPluginYml") - doLast { - builtSpringJar.delete() - } + project.tasks.register("buildServer") { + group = "mcspring" + description = "Performs a complete sever assembly (this is what you're looking for)" + dependsOn( + "downloadJar", + "setupSpigot", + "buildPluginYml", + "buildPluginJar", + "copyJarToSpigot" + ) } project.tasks.named("jar") { onlyIf { false } } } - - private fun registerBuildPluginYml(project: Project) { - project.tasks.register("buildPluginYml") { - description = "Generates a and plugin.yml" - } - } } diff --git a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/Extensions.kt b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/Extensions.kt new file mode 100644 index 0000000..cac93bd --- /dev/null +++ b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/Extensions.kt @@ -0,0 +1,23 @@ +package `in`.kyle.mcspring + +import org.gradle.api.Project +import org.gradle.api.plugins.ExtensionContainer +import org.gradle.api.plugins.JavaPluginConvention +import org.gradle.api.tasks.SourceSet +import org.gradle.kotlin.dsl.findByType +import java.io.File + +fun Project.getMainSourceSet(): SourceSet { + val convention = try { + project.convention.getPlugin(JavaPluginConvention::class.java) + } catch (e: IllegalStateException) { + error("Kotlin/Java/Other plugin not found. Make sure to add one of these.") + } + return convention.sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME) +} + +operator fun File.div(string: String) = resolve(string) + +val ExtensionContainer.mcspring: McSpringExtension + get() = findByType()!! + diff --git a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginJar.kt b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginJar.kt new file mode 100644 index 0000000..4b57b18 --- /dev/null +++ b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginJar.kt @@ -0,0 +1,75 @@ +package `in`.kyle.mcspring.tasks + +import `in`.kyle.mcspring.div +import `in`.kyle.mcspring.getMainSourceSet +import `in`.kyle.mcspring.mcspring +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import org.gradle.api.tasks.TaskAction +import org.gradle.api.tasks.bundling.ZipEntryCompression +import org.gradle.kotlin.dsl.create +import org.springframework.boot.gradle.dsl.SpringBootExtension +import org.springframework.boot.gradle.tasks.bundling.BootJar +import java.util.concurrent.Callable + +open class BuildPluginJar : ShadowJar() { + + private val springJarTask by lazy { + project.tasks.create("springJar") { + doFirst { + val libs = project.buildDir / "libs" + libs.deleteRecursively() + libs.mkdirs() + } + + mainClassName = "ignored" + classpath(Callable { project.getMainSourceSet().runtimeClasspath }) + + archiveFileName.set("spring.jar") + dependsOn(project.tasks.named("classes")) + } + } + + private val javaPluginMain = Callable { + project.getMainSourceSet().runtimeClasspath.filter { + "mcspring-base" in it.name + }.map { file -> + project.zipTree(file).matching { + include { + it.isDirectory || "in/kyle/mcspring/javaplugin" in it.path + } + } + } + } + + init { + SpringBootExtension(project).buildInfo() + setup() + } + + private fun setup() { + entryCompression = ZipEntryCompression.STORED + includeEmptyDirs = false + archiveClassifier.set("") + + val springJar = project.buildDir / "libs" / "spring.jar" + from(springJar) + from(javaPluginMain) + + relocate("BOOT-INF/classes/", "") + + doLast { + springJar.delete() + } + + dependsOn("buildPluginYml", springJarTask) + } + + @TaskAction + override fun copy() { + relocate( + "org.springframework.boot.loader.in.kyle.mcspring.javaplugin", + project.extensions.mcspring.pluginMainPackage + ) + super.copy() + } +} diff --git a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt index 7457a77..e719492 100644 --- a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt +++ b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt @@ -1,15 +1,21 @@ package `in`.kyle.mcspring.tasks -import `in`.kyle.mcspring.McSpringExtension +import `in`.kyle.mcspring.annotation.PluginDepend +import `in`.kyle.mcspring.annotation.SoftPluginDepend +import `in`.kyle.mcspring.commands.dsl.mcspring.Command +import `in`.kyle.mcspring.div +import `in`.kyle.mcspring.getMainSourceSet +import `in`.kyle.mcspring.mcspring import io.github.classgraph.ClassGraph import org.apache.log4j.Logger import org.gradle.api.DefaultTask import org.gradle.api.tasks.TaskAction -import org.gradle.kotlin.dsl.findByType -import org.gradle.kotlin.dsl.get import org.yaml.snakeyaml.Yaml import java.io.File import java.net.URLClassLoader +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter +import kotlin.reflect.KClass open class BuildPluginYml : DefaultTask() { @@ -18,39 +24,47 @@ open class BuildPluginYml : DefaultTask() { @TaskAction fun buildYml() { - val props = project.extensions.findByType() - requireNotNull(props) { "mcspring not defined in build.gradle" } + val props = project.extensions.mcspring - with(props) { + props.apply { attributes["name"] = pluginName ?: project.name attributes["version"] = pluginVersion ?: project.version attributes["main"] = "$pluginMainPackage.SpringJavaPlugin" - pluginDescription?.apply { attributes["description"] = this } - pluginLoad?.apply { - val value = when (this) { - is McSpringExtension.Load -> this.name.toLowerCase() - is String -> this - else -> error("Invalid pluginLoad value $this") - } - attributes["load"] = value - } - pluginAuthor?.apply { attributes["author"] = this } - pluginAuthors?.apply { attributes["authors"] = this } - pluginWebsite?.apply { attributes["website"] = this } - pluginDatabase?.apply { attributes["database"] = this } - pluginPrefix?.apply { attributes["prefix"] = this } - pluginLoadBefore?.apply { attributes["loadbefore"] = this } - - // TODO commands - addDependencies(project.configurations["runtime"].files) - - val outputFile = project.buildDir.resolve("resources").resolve("main") - outputFile.mkdirs() - val resolve = outputFile.resolve("plugin.yml") - logger.info("Building to $resolve") - resolve.writeText(Yaml().dump(attributes)) + fun writeNonNull(key: String, value: Any?) = value?.apply { attributes[key] = this } + + writeNonNull("description", pluginDescription) + writeNonNull("load", pluginLoad?.toString()?.toLowerCase()) + writeNonNull("author", pluginAuthor) + writeNonNull("authors", pluginAuthors) + writeNonNull("website", pluginWebsite) + writeNonNull("database", pluginDatabase) + writeNonNull("prefix", pluginPrefix) + writeNonNull("loadbefore", pluginLoadBefore) } + + project.getMainSourceSet().runtimeClasspath.files.apply { + addDependencies(this) + addCommands(this) + } + + writeYmlFile() + } + + private fun writeYmlFile() { + val outputFile = project.buildDir / "resources" / "main" + outputFile.mkdirs() + val pluginYml = outputFile / "plugin.yml" + logger.info("Building to $pluginYml") + + val formatter = DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm:SS") + val nowFormatted = LocalDateTime.now().format(formatter) + pluginYml.appendText(""" + # File auto generated by mcspring on $nowFormatted + # https://github.com/kylepls/mcspring + + """.trimIndent()) + pluginYml.appendText(Yaml().dumpAsMap(attributes)) } private fun addDependencies(files: Set) { @@ -61,15 +75,55 @@ open class BuildPluginYml : DefaultTask() { scan.allClasses.filter { it.isStandardClass && it.hasAnnotation(annotation) } .map { it.getAnnotationInfo(annotation).parameterValues } .flatMap { (it["plugins"].value as Array).toList() } - getPluginDependencies("in.kyle.mcspring.annotation.SoftPluginDepend").apply { - if (this.isNotEmpty()) { - attributes["softdepend"] = this - } - } - getPluginDependencies("in.kyle.mcspring.annotation.PluginDepend").apply { - if (this.isNotEmpty()) { - attributes["depend"] = this + + fun addAnnotationAttributeList(string: String, clazz: KClass<*>) = + getPluginDependencies(clazz.qualifiedName!!) + .takeIf { it.isNotEmpty() } + ?.apply { attributes[string] = this } + + addAnnotationAttributeList("softdepend", SoftPluginDepend::class) + addAnnotationAttributeList("depend", PluginDepend::class) + } + + private fun addCommands(files: Set) { + val classLoader = URLClassLoader(files.map { it.toURI().toURL() }.toTypedArray()) + val scan = ClassGraph() + .verbose() + .overrideClassLoaders(classLoader) + .enableAnnotationInfo() + .enableMethodInfo() + .enableClassInfo() + .scan() + + fun getAnnotations(annotation: KClass<*>) = + scan.getClassesWithMethodAnnotation(annotation.qualifiedName!!) + .flatMap { it.methodInfo.filter { it.hasAnnotation(annotation.qualifiedName!!) } } + .map { it.getAnnotationInfo(annotation.qualifiedName!!) } + .map { it.parameterValues } + + val annotations = getAnnotations(Command::class) + val commands = annotations.map { + val meta = mutableMapOf() + + meta["description"] = it.getValue("description") + meta["aliases"] = it.getValue("aliases") + meta["permission"] = it.getValue("permission") + meta["permission-message"] = it.getValue("permissionMessage") + meta["usage"] = it.getValue("usage") + + val name = it["value"].value + val filteredMeta = meta.filterValues { value -> + when (value) { + is Array<*> -> value.isNotEmpty() + is String -> value.isNotEmpty() + else -> true + } } + Pair(name, filteredMeta) + }.associate { it } + + if (commands.isNotEmpty()) { + attributes["commands"] = commands } } } diff --git a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/PluginTestSupport.kt b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/PluginTestSupport.kt index ce20796..1bd18dc 100644 --- a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/PluginTestSupport.kt +++ b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/PluginTestSupport.kt @@ -20,7 +20,7 @@ fun writeBaseGradleConfig(file: File) { fun runGradle(folder: File, vararg args: String): BuildResult { return GradleRunner.create() .withProjectDir(folder) - .withArguments(*args, "--stacktrace") + .withArguments(*args, "--stacktrace", "--info") .withPluginClasspath(getPluginClasspath()) .withDebug(true) .build() @@ -32,7 +32,10 @@ private fun getPluginClasspath(): List { return pluginClasspathResource.readText().lines().map { File(it) } } -operator fun File.plusAssign(string: String) = appendText("\n$string") +operator fun File.plusAssign(string: String) { + parentFile.mkdirs() + appendText("\n$string") +} operator fun File.div(string: String) = resolve(string) diff --git a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestBuildPluginJar.kt b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestBuildPluginJar.kt new file mode 100644 index 0000000..f4e8332 --- /dev/null +++ b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestBuildPluginJar.kt @@ -0,0 +1,73 @@ +package `in`.kyle.mcspring.tasks + +import `in`.kyle.mcspring.div +import `in`.kyle.mcspring.plusAssign +import `in`.kyle.mcspring.runGradle +import `in`.kyle.mcspring.writeBaseGradleConfig +import io.github.classgraph.ClassGraph +import io.kotest.assertions.assertSoftly +import io.kotest.core.spec.style.FreeSpec +import io.kotest.inspectors.forAll +import io.kotest.matchers.collections.shouldHaveSize +import io.kotest.matchers.shouldBe +import org.gradle.testkit.runner.TaskOutcome +import java.net.URLClassLoader + +class TestBuildPluginJar : FreeSpec({ + + "should create proper plugin jar" - { + val folder = createTempDir() + val buildFile = folder / "build.gradle" + writeBaseGradleConfig(buildFile) + buildFile += """ + |repositories { + | mavenCentral() + | mavenLocal() + |} + |dependencies { + | implementation "org.jetbrains.kotlin:kotlin-stdlib" + | implementation "in.kyle.mcspring:mcspring-base:+" + |} + |mcspring { + | pluginMainPackage = "main" + |} + """.trimMargin() + + val srcFile = folder / "src" / "main" / "kotlin" / "Base.kt" + srcFile += "fun test() { }" + + val result = runGradle(folder, "buildPluginJar") + val task = result.task(":buildPluginJar")!! + task.outcome shouldBe TaskOutcome.SUCCESS + + val pluginJar = (folder / "build" / "libs").listFiles()?.firstOrNull() + ?: error("Plugin jar not generated") + + val classLoader = URLClassLoader(arrayOf(pluginJar.toURI().toURL())) + val scan = ClassGraph() + .enableClassInfo() + .overrideClassLoaders(classLoader) + .scan() + + "should have plugin.yml" - { + scan.getResourcesWithPath("plugin.yml") shouldHaveSize 1 + } + + "all jar files should be in lib directory" - { + scan.getResourcesWithExtension("jar").map { it.path }.forAll { + it.startsWith("BOOT-INF/lib/") + } + } + + "plugin main should be relocated" - { + assertSoftly { + scan.allClasses.filter { it.packageName == "main" } + .map { it.name } shouldBe listOf("main.SpringJavaPlugin") + } + } + + "should have source files" - { + scan.allClasses.filter { it.name == "BaseKt" } shouldHaveSize 1 + } + } +}) diff --git a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestBuildPluginYml.kt b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestBuildPluginYml.kt index a72b932..5eb3f9f 100644 --- a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestBuildPluginYml.kt +++ b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestBuildPluginYml.kt @@ -6,7 +6,6 @@ import `in`.kyle.mcspring.runGradle import `in`.kyle.mcspring.writeBaseGradleConfig import io.kotest.assertions.assertSoftly import io.kotest.core.spec.style.FreeSpec -import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder import io.kotest.matchers.shouldBe import io.kotest.matchers.string.shouldEndWith import org.gradle.testkit.runner.TaskOutcome @@ -30,8 +29,7 @@ class TestBuildPluginYml : FreeSpec({ val yml = Yaml().load>(pluginYml.inputStream()) assertSoftly { - yml.keys shouldContainExactlyInAnyOrder listOf("name", "version", "main") - + yml.size shouldBe 3 yml["name"].toString() shouldEndWith ".tmp" yml["version"] shouldBe "29" yml["main"].toString() shouldEndWith ".SpringJavaPlugin" @@ -67,7 +65,7 @@ class TestBuildPluginYml : FreeSpec({ yml["description"] shouldBe "test description" yml["load"] shouldBe "startup" yml["author"] shouldBe "kyle" - (yml["authors"] as List) shouldContainExactlyInAnyOrder listOf("kyle1", "kyle2") + yml["authors"] shouldBe listOf("kyle1", "kyle2") yml["website"] shouldBe "github" yml["database"] shouldBe true yml["prefix"] shouldBe "prefix" @@ -79,11 +77,21 @@ class TestBuildPluginYml : FreeSpec({ val folder = createTempDir() val buildFile = folder / "build.gradle.kts" writeBaseGradleConfig(buildFile) + buildFile += """ + |repositories { + | mavenCentral() + | mavenLocal() + |} + |dependencies { + | implementation("in.kyle.mcspring:mcspring-base:+") + |} + """.trimMargin() val srcFile = folder / "src" / "main" / "kotlin" / "Example.kt" srcFile += """ - |import `in`.kyle.mcspring.annotation.PluginDepend + |import `in`.kyle.mcspring.annotation.* |@PluginDepend("plugin") + |@SoftPluginDepend("plugin2") |class Example """.trimMargin() @@ -95,7 +103,89 @@ class TestBuildPluginYml : FreeSpec({ val yml = Yaml().load>(pluginYml.inputStream()) assertSoftly { - yml["depend"] shouldBe "plugin" + yml["depend"] shouldBe listOf("plugin") + yml["softdepend"] shouldBe listOf("plugin2") } } + + "should write commands" - { + val folder = createTempDir() + val buildFile = folder / "build.gradle.kts" + writeBaseGradleConfig(buildFile) + buildFile += """ + |repositories { + | mavenCentral() + | mavenLocal() + |} + |dependencies { + | implementation(kotlin("stdlib")) + | implementation("in.kyle.mcspring:mcspring-base:+") + | implementation("in.kyle.mcspring:mcspring-commands-dsl:+") + |} + """.trimMargin() + + val srcFile = folder / "src" / "main" / "kotlin" / "Command.kt" + srcFile += """ + |import `in`.kyle.mcspring.commands.dsl.mcspring.Command + |@Command( + | value = "test", + | aliases = ["t"], + | description = "a test command", + | usage = "/test", + | permission = "test", + | permissionMessage = "no permission" + |) + |fun test() { } + """.trimMargin() + + val result = runGradle(folder, "buildPluginYml") + val task = result.task(":buildPluginYml")!! + task.outcome shouldBe TaskOutcome.SUCCESS + + val pluginYml = folder / "build" / "resources" / "main" / "plugin.yml" + val yml = Yaml().load>(pluginYml.inputStream()) + + yml["commands"] shouldBe mapOf( + "test" to mapOf( + "description" to "a test command", + "aliases" to listOf("t"), + "permission" to "test", + "permission-message" to "no permission", + "usage" to "/test" + ) + ) + } + + "should add minimal info to commands" - { + val folder = createTempDir() + val buildFile = folder / "build.gradle.kts" + writeBaseGradleConfig(buildFile) + buildFile += """ + |repositories { + | mavenCentral() + | mavenLocal() + |} + |dependencies { + | implementation(kotlin("stdlib")) + | implementation("in.kyle.mcspring:mcspring-base:+") + | implementation("in.kyle.mcspring:mcspring-commands-dsl:+") + |} + """.trimMargin() + + val srcFile = folder / "src" / "main" / "kotlin" / "Command.kt" + srcFile += """ + |import `in`.kyle.mcspring.commands.dsl.mcspring.Command + |@Command("test") + |fun test() { } + """.trimMargin() + + val result = runGradle(folder, "buildPluginYml") + val task = result.task(":buildPluginYml")!! + task.outcome shouldBe TaskOutcome.SUCCESS + + val pluginYml = folder / "build" / "resources" / "main" / "plugin.yml" + val yml = Yaml().load>(pluginYml.inputStream()) + + yml["commands"] shouldBe mapOf("test" to emptyMap()) + } }) diff --git a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestDownloadJar.kt b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestDownloadJar.kt new file mode 100644 index 0000000..f49d1d2 --- /dev/null +++ b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestDownloadJar.kt @@ -0,0 +1,25 @@ +package `in`.kyle.mcspring.tasks + +import `in`.kyle.mcspring.div +import `in`.kyle.mcspring.runGradle +import `in`.kyle.mcspring.writeBaseGradleConfig +import io.kotest.core.spec.style.FreeSpec +import io.kotest.matchers.file.shouldNotBeEmpty +import io.kotest.matchers.shouldBe +import org.gradle.testkit.runner.TaskOutcome + +class TestDownloadJar : FreeSpec({ + + "should download the Spigot jar" - { + val folder = createTempDir() + val buildFile = folder / "build.gradle.kts" + writeBaseGradleConfig(buildFile) + + val result = runGradle(folder, "downloadJar") + val task = result.task(":downloadJar")!! + task.outcome shouldBe TaskOutcome.SUCCESS + + val spigot = folder / "spigot" / "spigot.jar" + spigot.shouldNotBeEmpty() + } +}) diff --git a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestSetupSpigot.kt b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestSetupSpigot.kt index af47d84..cb034c4 100644 --- a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestSetupSpigot.kt +++ b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestSetupSpigot.kt @@ -4,6 +4,7 @@ import `in`.kyle.mcspring.div import `in`.kyle.mcspring.plusAssign import `in`.kyle.mcspring.runGradle import `in`.kyle.mcspring.writeBaseGradleConfig +import io.kotest.assertions.assertSoftly import io.kotest.core.spec.style.FreeSpec import io.kotest.matchers.file.shouldNotBeEmpty import io.kotest.matchers.shouldBe @@ -15,16 +16,21 @@ class TestSetupSpigot : FreeSpec({ val folder = createTempDir() val buildFile = folder / "build.gradle.kts" writeBaseGradleConfig(buildFile) + val spigotJar = folder / "spigot" / "spigot.jar" + spigotJar.parentFile.mkdirs() + spigotJar.createNewFile() val result = runGradle(folder, "setupSpigot") val task = result.task(":setupSpigot")!! task.outcome shouldBe TaskOutcome.SUCCESS val spigot = folder/"spigot" - (spigot/"bukkit.yml").shouldNotBeEmpty() - (spigot/"eula.txt").shouldNotBeEmpty() - (spigot/"README.md").shouldNotBeEmpty() - (spigot/"server.properties").shouldNotBeEmpty() - (spigot/"spigot.yml").shouldNotBeEmpty() + assertSoftly { + (spigot / "bukkit.yml").shouldNotBeEmpty() + (spigot / "eula.txt").shouldNotBeEmpty() + (spigot / "README.md").shouldNotBeEmpty() + (spigot / "server.properties").shouldNotBeEmpty() + (spigot / "spigot.yml").shouldNotBeEmpty() + } } }) diff --git a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandClassLoader.kt b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandClassLoader.kt index 4b555f7..48c015c 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandClassLoader.kt +++ b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandClassLoader.kt @@ -14,7 +14,7 @@ internal class CommandClassLoader { usage = "/classloader " ) fun classLoader() = commandExecutor { - val className by stringArg { + val className = stringArg { missing { message("Usage: /$label ") } diff --git a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandGamemode.kt b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandGamemode.kt index 53a816a..6320eaf 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandGamemode.kt +++ b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandGamemode.kt @@ -20,7 +20,7 @@ internal class CommandGamemode { fun gamemode() = commandExecutor { requirePlayer { message("Only players can run this command.") } - val gameMode by mapArg { + val gameMode = mapArg { parser { map(GameMode.values().associateBy { it.name.toLowerCase() }) map(GameMode.values().associateBy { it.value.toString() }) diff --git a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandHeal.kt b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandHeal.kt index 1ec8e94..c39a1b1 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandHeal.kt +++ b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandHeal.kt @@ -16,12 +16,12 @@ internal class CommandHeal { fun heal() = commandExecutor { requirePlayer { message("Sender must be a player") } - val target by playerArg { + val target = playerArg { default { sender as Player } invalid { message("Target player $it not found") } } - val health by doubleArg { + val health = doubleArg { default { 20.0 } parser { between(0.0, 20.0) { message("Heal value must be between 0 and 20") } diff --git a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandOp.kt b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandOp.kt index 4792848..be293e0 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandOp.kt +++ b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandOp.kt @@ -15,7 +15,7 @@ internal class CommandOp { usage = "/op ?" ) fun op() = commandExecutor { - val target by playerArg { + val target = playerArg { default { sender as? Player } invalid { message("Player $it not found") } } diff --git a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandPlugin.kt b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandPlugin.kt index 8fc0421..ff43da1 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandPlugin.kt +++ b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandPlugin.kt @@ -32,7 +32,7 @@ internal class CommandPlugin(private val pluginController: PluginController) { } private fun load() = commandExecutor { - val path by mapArg { + val path = mapArg { parser { map(pluginController.loadablePlugins) } @@ -42,7 +42,7 @@ internal class CommandPlugin(private val pluginController: PluginController) { } private fun unload() = commandExecutor { - val plugin by mapArg { + val plugin = mapArg { parser { map(pluginController.plugins.associateBy({ it.name }, { it })) } diff --git a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandSpeed.kt b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandSpeed.kt index 5b51e8d..fb48f99 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandSpeed.kt +++ b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandSpeed.kt @@ -16,7 +16,7 @@ internal class CommandSpeed { fun speed() = commandExecutor { requirePlayer { message("Sender must be a player") } val player = sender as Player - val speed by doubleArg { + val speed = doubleArg { parser { between(0.0, 10.0) { message("Speed must be between 0 and 10") } } diff --git a/settings.gradle.kts b/settings.gradle.kts index 01c19ac..ce61f00 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -13,9 +13,9 @@ include(":mcspring-api:mcspring-guis") include(":mcspring-api:mcspring-chat") include(":mcspring-build") -include(":mcspring-build:mcspring-plugin-manager") - -includeBuild("./mcspring-build/mcspring-gradle-plugin") +include(":mcspring-build:mcspring-gradle-plugin") +//include(":mcspring-build:mcspring-plugin-manager") +//includeBuild("./mcspring-build/mcspring-gradle-plugin") gradleEnterprise { buildScan { From 10355d7027ad6208db8ccb961f43fe836248c32d Mon Sep 17 00:00:00 2001 From: kylepls Date: Sun, 21 Jun 2020 01:49:51 -0600 Subject: [PATCH 35/54] Add more memory to gradle for travis --- .gitignore | 1 - gradle.properties | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 gradle.properties diff --git a/.gitignore b/.gitignore index c911bd4..398c0da 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,3 @@ pom.xml.versionsBackup **/spigot/** !**/archetype-resources/spigot/** -gradle.properties diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..821e127 --- /dev/null +++ b/gradle.properties @@ -0,0 +1 @@ +org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 From 0a3280fc68b399f967def2c0176d73fc8803fe99 Mon Sep 17 00:00:00 2001 From: kylepls Date: Sun, 21 Jun 2020 12:13:36 -0600 Subject: [PATCH 36/54] add additional test gradle params --- gradle.properties | 1 - .../src/test/kotlin/in/kyle/mcspring/PluginTestSupport.kt | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 gradle.properties diff --git a/gradle.properties b/gradle.properties deleted file mode 100644 index 821e127..0000000 --- a/gradle.properties +++ /dev/null @@ -1 +0,0 @@ -org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 diff --git a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/PluginTestSupport.kt b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/PluginTestSupport.kt index 1bd18dc..9c8e427 100644 --- a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/PluginTestSupport.kt +++ b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/PluginTestSupport.kt @@ -20,7 +20,7 @@ fun writeBaseGradleConfig(file: File) { fun runGradle(folder: File, vararg args: String): BuildResult { return GradleRunner.create() .withProjectDir(folder) - .withArguments(*args, "--stacktrace", "--info") + .withArguments(*args, "--stacktrace", "--info", "--scan", "-s") .withPluginClasspath(getPluginClasspath()) .withDebug(true) .build() From 9a9302fb87c01f09e3c7129d6f45b8fd0d31cd32 Mon Sep 17 00:00:00 2001 From: kylepls Date: Sun, 21 Jun 2020 12:53:24 -0600 Subject: [PATCH 37/54] give gradle more memory --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index c7c692d..74b8da4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,10 @@ cache: - $HOME/.gradle/caches/ - $HOME/.gradle/wrapper/ +env: + global: + - GRADLE_OPTS="-Xms128m -Xmx2g" + script: - ./gradlew build --scan -s From b55e9d25931ae0c35c9870f47f5d52abafa6a1b9 Mon Sep 17 00:00:00 2001 From: kylepls Date: Sun, 21 Jun 2020 13:20:25 -0600 Subject: [PATCH 38/54] gradle ee settings --- .../kotlin/in/kyle/mcspring/PluginTestSupport.kt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/PluginTestSupport.kt b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/PluginTestSupport.kt index 9c8e427..22e948d 100644 --- a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/PluginTestSupport.kt +++ b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/PluginTestSupport.kt @@ -15,6 +15,18 @@ fun writeBaseGradleConfig(file: File) { | jcenter() |} """.trimMargin() + val settings = file.parentFile / "settings.gradle.kts" + settings += """ + |plugins { + | id("com.gradle.enterprise").version("3.3.4") + |} + |gradleEnterprise { + | buildScan { + | termsOfServiceUrl = "https://gradle.com/terms-of-service" + | termsOfServiceAgree = "yes" + | } + |} + """.trimMargin() } fun runGradle(folder: File, vararg args: String): BuildResult { From 0e6a1aaee11cbb0d6c8df4a3221fc65845ed3a3c Mon Sep 17 00:00:00 2001 From: kylepls Date: Sun, 21 Jun 2020 13:36:32 -0600 Subject: [PATCH 39/54] Add more memory to child gradle processes --- .../src/test/kotlin/in/kyle/mcspring/PluginTestSupport.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/PluginTestSupport.kt b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/PluginTestSupport.kt index 22e948d..29f622c 100644 --- a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/PluginTestSupport.kt +++ b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/PluginTestSupport.kt @@ -27,6 +27,10 @@ fun writeBaseGradleConfig(file: File) { | } |} """.trimMargin() + val gradleProperties = file.parentFile / "gradle.properties" + gradleProperties += """ + org.gradle.jvmargs=-Xmx1024m + """.trimIndent() } fun runGradle(folder: File, vararg args: String): BuildResult { From bc0a27aa6f340ff882afb382f6528868a801f237 Mon Sep 17 00:00:00 2001 From: kylepls Date: Sun, 21 Jun 2020 15:10:43 -0600 Subject: [PATCH 40/54] Add more memory to child gradle processes --- mcspring-build/mcspring-gradle-plugin/build.gradle.kts | 4 ++++ .../src/test/kotlin/in/kyle/mcspring/PluginTestSupport.kt | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/mcspring-build/mcspring-gradle-plugin/build.gradle.kts b/mcspring-build/mcspring-gradle-plugin/build.gradle.kts index cac2ab6..430e6c6 100644 --- a/mcspring-build/mcspring-gradle-plugin/build.gradle.kts +++ b/mcspring-build/mcspring-gradle-plugin/build.gradle.kts @@ -66,3 +66,7 @@ dependencies { testRuntimeOnly(files(createClasspathManifest)) } + +tasks.withType() { + maxHeapSize = "1g" +} diff --git a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/PluginTestSupport.kt b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/PluginTestSupport.kt index 29f622c..5ba5d43 100644 --- a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/PluginTestSupport.kt +++ b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/PluginTestSupport.kt @@ -36,7 +36,13 @@ fun writeBaseGradleConfig(file: File) { fun runGradle(folder: File, vararg args: String): BuildResult { return GradleRunner.create() .withProjectDir(folder) - .withArguments(*args, "--stacktrace", "--info", "--scan", "-s") + .withArguments( + *args, + "--stacktrace", + "--info", + "--scan", + "-s" + ) .withPluginClasspath(getPluginClasspath()) .withDebug(true) .build() From 67a0787cf7aedcd7a4dace0035f814fe7ce65be1 Mon Sep 17 00:00:00 2001 From: kylepls Date: Sun, 21 Jun 2020 15:29:36 -0600 Subject: [PATCH 41/54] More memory???? --- mcspring-build/mcspring-gradle-plugin/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mcspring-build/mcspring-gradle-plugin/build.gradle.kts b/mcspring-build/mcspring-gradle-plugin/build.gradle.kts index 430e6c6..58db86e 100644 --- a/mcspring-build/mcspring-gradle-plugin/build.gradle.kts +++ b/mcspring-build/mcspring-gradle-plugin/build.gradle.kts @@ -68,5 +68,5 @@ dependencies { } tasks.withType() { - maxHeapSize = "1g" + maxHeapSize = "2g" } From aa79d5083207cb8f72b7cd5501036c8666318bd8 Mon Sep 17 00:00:00 2001 From: kylepls Date: Sun, 21 Jun 2020 15:31:12 -0600 Subject: [PATCH 42/54] Remove env flags? --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 74b8da4..67628fc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,9 +14,9 @@ cache: - $HOME/.gradle/caches/ - $HOME/.gradle/wrapper/ -env: - global: - - GRADLE_OPTS="-Xms128m -Xmx2g" +#env: +# global: +# - GRADLE_OPTS="-Xms128m -Xmx2g" script: - ./gradlew build --scan -s From 9beafc8770ae1a16b7e03c53359f96bb710f5174 Mon Sep 17 00:00:00 2001 From: kylepls Date: Sun, 21 Jun 2020 15:47:32 -0600 Subject: [PATCH 43/54] Close all ClassGraph resources --- .../kotlin/in/kyle/mcspring/SpringLoader.kt | 17 +++++-- .../in/kyle/mcspring/tasks/BuildPluginYml.kt | 50 +++++++++++-------- .../kyle/mcspring/tasks/TestBuildPluginJar.kt | 31 ++++++------ 3 files changed, 56 insertions(+), 42 deletions(-) diff --git a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/SpringLoader.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/SpringLoader.kt index e9e4cdd..16d2b83 100644 --- a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/SpringLoader.kt +++ b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/SpringLoader.kt @@ -18,6 +18,10 @@ class SpringLoader( private var context: ConfigurableApplicationContext? = null private var logger = javaPlugin.logger + private val scanThreads by lazy { + Runtime.getRuntime().availableProcessors() + } + fun onEnable() { try { initSpring() @@ -28,12 +32,15 @@ class SpringLoader( } private fun initSpring() { - val mains = ClassGraph() + val scanResult = ClassGraph() .enableAnnotationInfo() - .scan(4) - .allStandardClasses - .filter { it.hasAnnotation("in.kyle.mcspring.annotation.SpringPlugin") } - .map { it.name } + .scan(scanThreads) + val mains = scanResult.use { + scanResult + .allStandardClasses + .filter { it.hasAnnotation("in.kyle.mcspring.annotation.SpringPlugin") } + .map { it.name } + } require(mains.size == 1) { "There should only be 1 main class on the classpath: $mains" } logger.info("Using main class: $mains") val config = Class.forName(mains[0]) diff --git a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt index e719492..656d41f 100644 --- a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt +++ b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt @@ -69,39 +69,45 @@ open class BuildPluginYml : DefaultTask() { private fun addDependencies(files: Set) { val classLoader = URLClassLoader(files.map { it.toURI().toURL() }.toTypedArray()) - val scan = ClassGraph().verbose().overrideClassLoaders(classLoader).enableAnnotationInfo().scan() - - fun getPluginDependencies(annotation: String) = - scan.allClasses.filter { it.isStandardClass && it.hasAnnotation(annotation) } - .map { it.getAnnotationInfo(annotation).parameterValues } - .flatMap { (it["plugins"].value as Array).toList() } - - fun addAnnotationAttributeList(string: String, clazz: KClass<*>) = - getPluginDependencies(clazz.qualifiedName!!) - .takeIf { it.isNotEmpty() } - ?.apply { attributes[string] = this } - - addAnnotationAttributeList("softdepend", SoftPluginDepend::class) - addAnnotationAttributeList("depend", PluginDepend::class) + val scanResult = ClassGraph() + .overrideClassLoaders(classLoader) + .enableAnnotationInfo() + .scan() + scanResult.use { + fun getPluginDependencies(annotation: String) = + scanResult.allClasses.filter { it.isStandardClass && it.hasAnnotation(annotation) } + .map { it.getAnnotationInfo(annotation).parameterValues } + .flatMap { (it["plugins"].value as Array).toList() } + + fun addAnnotationAttributeList(string: String, clazz: KClass<*>) = + getPluginDependencies(clazz.qualifiedName!!) + .takeIf { it.isNotEmpty() } + ?.apply { attributes[string] = this } + + addAnnotationAttributeList("softdepend", SoftPluginDepend::class) + addAnnotationAttributeList("depend", PluginDepend::class) + } } private fun addCommands(files: Set) { val classLoader = URLClassLoader(files.map { it.toURI().toURL() }.toTypedArray()) - val scan = ClassGraph() - .verbose() + val scanResult = ClassGraph() .overrideClassLoaders(classLoader) .enableAnnotationInfo() .enableMethodInfo() .enableClassInfo() .scan() - fun getAnnotations(annotation: KClass<*>) = - scan.getClassesWithMethodAnnotation(annotation.qualifiedName!!) - .flatMap { it.methodInfo.filter { it.hasAnnotation(annotation.qualifiedName!!) } } - .map { it.getAnnotationInfo(annotation.qualifiedName!!) } - .map { it.parameterValues } + val annotations = scanResult.use { + fun getAnnotations(annotation: KClass<*>) = + scanResult.getClassesWithMethodAnnotation(annotation.qualifiedName!!) + .flatMap { it.methodInfo.filter { it.hasAnnotation(annotation.qualifiedName!!) } } + .map { it.getAnnotationInfo(annotation.qualifiedName!!) } + .map { it.parameterValues } + + getAnnotations(Command::class) + } - val annotations = getAnnotations(Command::class) val commands = annotations.map { val meta = mutableMapOf() diff --git a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestBuildPluginJar.kt b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestBuildPluginJar.kt index f4e8332..7797000 100644 --- a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestBuildPluginJar.kt +++ b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestBuildPluginJar.kt @@ -48,26 +48,27 @@ class TestBuildPluginJar : FreeSpec({ .enableClassInfo() .overrideClassLoaders(classLoader) .scan() + scan.use { + "should have plugin.yml" - { + scan.getResourcesWithPath("plugin.yml") shouldHaveSize 1 + } - "should have plugin.yml" - { - scan.getResourcesWithPath("plugin.yml") shouldHaveSize 1 - } - - "all jar files should be in lib directory" - { - scan.getResourcesWithExtension("jar").map { it.path }.forAll { - it.startsWith("BOOT-INF/lib/") + "all jar files should be in lib directory" - { + scan.getResourcesWithExtension("jar").map { it.path }.forAll { + it.startsWith("BOOT-INF/lib/") + } } - } - "plugin main should be relocated" - { - assertSoftly { - scan.allClasses.filter { it.packageName == "main" } - .map { it.name } shouldBe listOf("main.SpringJavaPlugin") + "plugin main should be relocated" - { + assertSoftly { + scan.allClasses.filter { it.packageName == "main" } + .map { it.name } shouldBe listOf("main.SpringJavaPlugin") + } } - } - "should have source files" - { - scan.allClasses.filter { it.name == "BaseKt" } shouldHaveSize 1 + "should have source files" - { + scan.allClasses.filter { it.name == "BaseKt" } shouldHaveSize 1 + } } } }) From 87081a89474b9840de1a30856ab5de6400e7e280 Mon Sep 17 00:00:00 2001 From: kylepls Date: Sun, 21 Jun 2020 15:53:37 -0600 Subject: [PATCH 44/54] Try removing heap size for tests --- .travis.yml | 4 ---- mcspring-build/mcspring-gradle-plugin/build.gradle.kts | 4 ---- 2 files changed, 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 67628fc..c7c692d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,10 +14,6 @@ cache: - $HOME/.gradle/caches/ - $HOME/.gradle/wrapper/ -#env: -# global: -# - GRADLE_OPTS="-Xms128m -Xmx2g" - script: - ./gradlew build --scan -s diff --git a/mcspring-build/mcspring-gradle-plugin/build.gradle.kts b/mcspring-build/mcspring-gradle-plugin/build.gradle.kts index 58db86e..cac2ab6 100644 --- a/mcspring-build/mcspring-gradle-plugin/build.gradle.kts +++ b/mcspring-build/mcspring-gradle-plugin/build.gradle.kts @@ -66,7 +66,3 @@ dependencies { testRuntimeOnly(files(createClasspathManifest)) } - -tasks.withType() { - maxHeapSize = "2g" -} From e894a5970c794255f1a8cfe2e11e2e33cc7a755b Mon Sep 17 00:00:00 2001 From: kylepls Date: Sat, 27 Jun 2020 13:13:27 -0600 Subject: [PATCH 45/54] Update commands --- .../main/kotlin/mcspring-publish.gradle.kts | 7 +- mcspring-api/mcspring-base/build.gradle.kts | 10 +- .../kotlin/in/kyle/mcspring/SpringLoader.kt | 23 +-- .../main/resources/META-INF/spring.factories | 1 + .../mcspring/commands/dsl/CommandBuilder.kt | 68 ++++--- .../mcspring/commands/dsl/CommandFacade.kt | 14 +- .../mcspring/commands/dsl/CommandModel.kt | 21 +- .../commands/dsl/parsers/BaseParser.kt | 2 +- .../commands/dsl/parsers/BooleanParser.kt | 6 +- .../commands/dsl/parsers/MapParser.kt | 2 +- .../commands/dsl/parsers/PlayerParser.kt | 6 +- .../commands/dsl/parsers/StringParser.kt | 2 +- .../dsl/parsers/numbers/DoubleParser.kt | 2 +- .../commands/dsl/parsers/numbers/IntParser.kt | 2 +- .../dsl/parsers/numbers/NumberParser.kt | 4 +- .../commands/dsl/util/CommandMapWrapper.kt | 31 ++- .../commands/dsl/util/CommandUtils.kt | 65 ++++++- .../commands/dsl/CommandTestSupport.kt | 12 +- .../mcspring/commands/dsl/TestArgBuilder.kt | 5 +- .../commands/dsl/TestCommandBuilder.kt | 27 ++- .../commands/dsl/TestSubcommandBuilder.kt | 22 ++- .../commands/dsl/TestTabCompletions.kt | 66 +++++++ .../mcspring/commands/dsl/TestValueBuilder.kt | 11 +- .../commands/dsl/parsers/TestMapParser.kt | 33 ++++ .../commands/dsl/parsers/TestStringParser.kt | 20 +- .../dsl/parsers/numbers/TestIntParser.kt | 2 + .../mcspring-gradle-plugin/build.gradle.kts | 2 +- .../kotlin/in/kyle/mcspring/BuildPlugin.kt | 1 - .../in/kyle/mcspring/McSpringExtension.kt | 5 +- .../in/kyle/mcspring/tasks/BuildPluginYml.kt | 124 ++++++++++-- .../kotlin/in/kyle/mcspring/GradleContext.kt | 76 ++++++++ .../in/kyle/mcspring/PluginTestSupport.kt | 29 --- .../kyle/mcspring/tasks/TestBuildPluginJar.kt | 34 +--- .../kyle/mcspring/tasks/TestBuildPluginYml.kt | 142 ++++---------- .../in/kyle/mcspring/tasks/TestDownloadJar.kt | 11 +- .../in/kyle/mcspring/tasks/TestSetupSpigot.kt | 15 +- .../mcspring-plugin-manager/build.gradle.kts | 17 -- .../mcspring/manager/McSpringPluginManager.kt | 8 - mcspring-examples/build.gradle.kts | 22 +++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 58695 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 + mcspring-examples/gradlew | 183 ++++++++++++++++++ mcspring-examples/gradlew.bat | 100 ++++++++++ .../mcspring-plugin-manager/build.gradle.kts | 9 + .../mcspring/manager/McSpringPluginManager.kt | 6 + .../mcspring/manager/commands/CommandAbout.kt | 3 +- .../manager/commands/CommandClassLoader.kt | 3 +- .../manager/commands/CommandGamemode.kt | 15 +- .../mcspring/manager/commands/CommandHeal.kt | 3 +- .../mcspring/manager/commands/CommandOp.kt | 3 +- .../manager/commands/CommandPlugin.kt | 7 +- .../manager/commands/CommandReload.kt | 3 +- .../mcspring/manager/commands/CommandSpeed.kt | 3 +- .../controller/BukkitPluginUnloader.kt | 0 .../manager/controller/LogFileController.kt | 0 .../manager/controller/PluginController.kt | 0 .../commands/TestCommandClassLoader.kt | 19 ++ .../manager/commands/TestCommandGamemode.kt | 28 +++ .../manager/commands/TestCommandSpeed.kt | 34 ++++ mcspring-examples/settings.gradle.kts | 12 ++ .../simple-factions-addon/build.gradle.kts | 3 +- .../simple-factions/build.gradle.kts | 4 +- settings.gradle.kts | 2 - 63 files changed, 1036 insertions(+), 359 deletions(-) create mode 100644 mcspring-api/mcspring-base/src/main/resources/META-INF/spring.factories create mode 100644 mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestTabCompletions.kt create mode 100644 mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/GradleContext.kt delete mode 100644 mcspring-build/mcspring-plugin-manager/build.gradle.kts delete mode 100644 mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/McSpringPluginManager.kt create mode 100644 mcspring-examples/build.gradle.kts create mode 100644 mcspring-examples/gradle/wrapper/gradle-wrapper.jar create mode 100644 mcspring-examples/gradle/wrapper/gradle-wrapper.properties create mode 100644 mcspring-examples/gradlew create mode 100644 mcspring-examples/gradlew.bat create mode 100644 mcspring-examples/mcspring-plugin-manager/build.gradle.kts create mode 100644 mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/McSpringPluginManager.kt rename {mcspring-build => mcspring-examples}/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandAbout.kt (92%) rename {mcspring-build => mcspring-examples}/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandClassLoader.kt (92%) rename {mcspring-build => mcspring-examples}/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandGamemode.kt (83%) rename {mcspring-build => mcspring-examples}/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandHeal.kt (93%) rename {mcspring-build => mcspring-examples}/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandOp.kt (92%) rename {mcspring-build => mcspring-examples}/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandPlugin.kt (94%) rename {mcspring-build => mcspring-examples}/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandReload.kt (89%) rename {mcspring-build => mcspring-examples}/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandSpeed.kt (94%) rename {mcspring-build => mcspring-examples}/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/BukkitPluginUnloader.kt (100%) rename {mcspring-build => mcspring-examples}/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/LogFileController.kt (100%) rename {mcspring-build => mcspring-examples}/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/PluginController.kt (100%) create mode 100644 mcspring-examples/mcspring-plugin-manager/src/test/kotlin_disabled/in/kyle/mcspring/manager/commands/TestCommandClassLoader.kt create mode 100644 mcspring-examples/mcspring-plugin-manager/src/test/kotlin_disabled/in/kyle/mcspring/manager/commands/TestCommandGamemode.kt create mode 100644 mcspring-examples/mcspring-plugin-manager/src/test/kotlin_disabled/in/kyle/mcspring/manager/commands/TestCommandSpeed.kt create mode 100644 mcspring-examples/settings.gradle.kts diff --git a/buildSrc/src/main/kotlin/mcspring-publish.gradle.kts b/buildSrc/src/main/kotlin/mcspring-publish.gradle.kts index 71bd5a6..1a3e966 100644 --- a/buildSrc/src/main/kotlin/mcspring-publish.gradle.kts +++ b/buildSrc/src/main/kotlin/mcspring-publish.gradle.kts @@ -1,5 +1,4 @@ import com.jfrog.bintray.gradle.BintrayExtension -import java.sql.Date plugins { kotlin("jvm") @@ -7,7 +6,7 @@ plugins { id("com.jfrog.bintray") } -tasks.create("sourcesJar") { +val sourcesJar by tasks.creating(Jar::class) { archiveClassifier.set("sources") from(sourceSets.getByName("main").allSource) dependsOn(tasks.classes) @@ -39,7 +38,9 @@ publishing { artifactId = artifactName version = artifactVersion from(components["java"]) - artifact(tasks.findByName("sourcesJar")) + artifact(sourcesJar) { + classifier = "sources" + } pom.withXml { asNode().apply { diff --git a/mcspring-api/mcspring-base/build.gradle.kts b/mcspring-api/mcspring-base/build.gradle.kts index 3e262b3..4ca660e 100644 --- a/mcspring-api/mcspring-base/build.gradle.kts +++ b/mcspring-api/mcspring-base/build.gradle.kts @@ -2,6 +2,10 @@ plugins { id("org.jetbrains.dokka") } +repositories { + mavenCentral() +} + dependencies { // compileOnly("org.apache.logging.log4j:log4j-core:2.12.1") api(platform("org.springframework.boot:spring-boot-dependencies:2.3.1.RELEASE")) @@ -9,12 +13,8 @@ dependencies { exclude("org.springframework.boot", "spring-boot-dependencies") } api("org.springframework.boot:spring-boot-starter") { - exclude("org.springframework.boot", "spring-boot-starter-logging") +// exclude("org.springframework.boot", "spring-boot-starter-logging") exclude("org.springframework.boot", "spring-boot-dependencies") - exclude("org.springframework", "spring-jcl") - exclude("org.springframework", "spring-expression") exclude("org.yaml", "snakeyaml") } - - implementation("io.github.classgraph:classgraph:4.8.83") } diff --git a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/SpringLoader.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/SpringLoader.kt index 16d2b83..35635bb 100644 --- a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/SpringLoader.kt +++ b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/SpringLoader.kt @@ -1,12 +1,12 @@ package `in`.kyle.mcspring -import io.github.classgraph.ClassGraph import org.bukkit.plugin.java.JavaPlugin import org.springframework.boot.Banner import org.springframework.boot.builder.SpringApplicationBuilder import org.springframework.boot.loader.JarLauncher import org.springframework.context.ConfigurableApplicationContext import org.springframework.core.io.DefaultResourceLoader +import org.yaml.snakeyaml.Yaml import java.net.URL import java.net.URLClassLoader @@ -16,7 +16,7 @@ class SpringLoader( ) { private var context: ConfigurableApplicationContext? = null - private var logger = javaPlugin.logger + private val logger by lazy { javaPlugin.logger } private val scanThreads by lazy { Runtime.getRuntime().availableProcessors() @@ -32,18 +32,13 @@ class SpringLoader( } private fun initSpring() { - val scanResult = ClassGraph() - .enableAnnotationInfo() - .scan(scanThreads) - val mains = scanResult.use { - scanResult - .allStandardClasses - .filter { it.hasAnnotation("in.kyle.mcspring.annotation.SpringPlugin") } - .map { it.name } - } - require(mains.size == 1) { "There should only be 1 main class on the classpath: $mains" } - logger.info("Using main class: $mains") - val config = Class.forName(mains[0]) + val pluginYmlResource = javaPlugin.getResource("plugin.yml") ?: error("plugin.yml not found???") + @Suppress("UNCHECKED_CAST") + val yaml = Yaml().loadAs(pluginYmlResource, Map::class.java) as Map + val main = yaml["spring-boot-main"] as? String + ?: error("Spring boot main not found in plugin.yml") + + val config = Class.forName(main) val builder = SpringApplicationBuilder() var sources = arrayOf(config, SpringSpigotSupport::class.java) diff --git a/mcspring-api/mcspring-base/src/main/resources/META-INF/spring.factories b/mcspring-api/mcspring-base/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000..9e737e5 --- /dev/null +++ b/mcspring-api/mcspring-base/src/main/resources/META-INF/spring.factories @@ -0,0 +1 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=in.kyle.mcspring.SpringSpigotSupport diff --git a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandBuilder.kt b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandBuilder.kt index 206e5c1..754e302 100644 --- a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandBuilder.kt +++ b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandBuilder.kt @@ -19,7 +19,7 @@ class CommandMeta { var aliases: List = listOf() lateinit var executor: CommandExecutor - fun preRegister() { + fun validate() { require(this::name.isInitialized) { "Command name not set" } require(this::executor.isInitialized) { "Command executor not set for command $name" } } @@ -33,11 +33,8 @@ data class CommandContext( internal var argIndex: Int = 0, internal val runExecutors: Boolean = true ) { - val nextArg: String - get() { - return args.getOrNull( argIndex ) ?: error("Argument index out of bounds") - } - fun nextArg() = args[argIndex] + val nextArg: String? + get() = args.getOrNull(argIndex) } data class CommandExecutor(val provider: (CommandContext) -> ParsedCommand) @@ -64,7 +61,7 @@ class CommandBuilder(context: CommandContext) : ContextReciever(context) { fun require(value: Boolean, lambda: ContextReciever.() -> Unit) { if (!value) { lambda(this) - complete() + commandMissing() } } @@ -74,7 +71,7 @@ class CommandBuilder(context: CommandContext) : ContextReciever(context) { private fun > valueArg( lambda: ValueBuilder.() -> Unit, - parserSupplier: (String) -> P + parserSupplier: (String?) -> P ) = arg(ValueBuilder(context, parserSupplier), lambda) private fun > arg(builder: T, lambda: T.() -> Unit): R { @@ -82,14 +79,17 @@ class CommandBuilder(context: CommandContext) : ContextReciever(context) { val arg = builder.apply(lambda).build() parsedArgs.add(arg) context.argIndex++ - return arg.returnValue ?: complete() + if (arg.returnValue == null) { + commandMissing() + } + return arg.returnValue } fun then(lambda: ContextReciever.() -> Unit) { if (context.runExecutors) { lambda(this) - complete() } + commandComplete() } fun build(): ParsedCommand = ParsedCommand(parsedArgs) @@ -103,9 +103,12 @@ abstract class ArgBuilder( internal open var returnValue: R? = null open fun invalid(lambda: ContextReciever.(arg: String) -> Unit) { - if (returnValue == null && context.runExecutors) { - lambda(context.nextArg) - complete() + val nextArg = context.nextArg + if (returnValue == null && nextArg != null) { + if (context.runExecutors) { + lambda(nextArg) + } + commandInvalid() } } @@ -114,7 +117,7 @@ abstract class ArgBuilder( class ValueBuilder>( context: CommandContext, - private val parserSupplier: (String) -> T + private val parserSupplier: (String?) -> T ) : ArgBuilder(context) { private var hasRunParser = false @@ -128,16 +131,18 @@ class ValueBuilder>( fun parser(lambda: T.() -> Unit) { hasRunParser = true if (returnValue == null) { - val parser = parserSupplier(context.nextArg()) + val parser = parserSupplier(context.nextArg) parser.lambda() returnValue = parser.returnValue } } fun missing(lambda: ContextReciever.() -> Unit) { - if (!hasNextArg() && context.runExecutors) { - lambda() - complete() + if (!hasNextArg()) { + if (context.runExecutors) { + lambda() + } + commandMissing() } } @@ -154,11 +159,15 @@ class ValueBuilder>( override fun build(): ValueArg { runBaseParse() - if (returnValue != null) { - return ValueArg(returnValue!!) + val temp = returnValue + if (temp != null) { + return ValueArg(temp) } else { - error("Error parsing $context, make sure to implement an `invalid` block in your " + - "command to catch failed argument parses.") + error(""" + Error parsing $context, make sure to implement the following in your DSL: + missing - run when the argument is missing. E.g.: Missing the argument in /tp + invalid - run when the argument is invalid. E.g.: "askdf" being passed for an int arg. + """.trimIndent()) } } } @@ -169,7 +178,7 @@ class SubcommandBuilder(context: CommandContext) : ArgBuilder(context) { fun on(vararg values: String, commandExecutor: CommandExecutor) = on({ it in values }, values.toList(), commandExecutor) fun on(vararg values: String, command: CommandBuilder.() -> Unit) { - on({ it in values }, values.toList(), commandExecutor(command)) + on({ it in values }, values.toList(), command(command)) } fun on( @@ -178,12 +187,13 @@ class SubcommandBuilder(context: CommandContext) : ArgBuilder(context) { commandExecutor: CommandExecutor ) { context.tabCompletions.addAll(tabCompletions) - val argString = context.nextArg() + val argString = context.nextArg ?: return + if (predicate(argString)) { returnValue = argString context.argIndex++ subCommands[tabCompletions.toList()] = commandExecutor.provider(context) - complete() + commandComplete() } else { val fakeContext = context.copy(runExecutors = false) val parsed = commandExecutor.provider(fakeContext) @@ -192,9 +202,11 @@ class SubcommandBuilder(context: CommandContext) : ArgBuilder(context) { } fun missing(lambda: SubcommandMissingBuilder.() -> Unit) { - if (!hasNextArg() && context.runExecutors) { - lambda(SubcommandMissingBuilder(subCommands, context)) - complete() + if (!hasNextArg()) { + if (context.runExecutors) { + lambda(SubcommandMissingBuilder(subCommands, context)) + } + commandComplete() } } diff --git a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandFacade.kt b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandFacade.kt index afcefac..7b2e09f 100644 --- a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandFacade.kt +++ b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandFacade.kt @@ -1,19 +1,17 @@ package `in`.kyle.mcspring.commands.dsl -fun command(lambda: CommandMeta.() -> Unit) = CommandMeta().apply { lambda(this) } +fun commandMeta(lambda: CommandMeta.() -> Unit) = CommandMeta().apply { lambda(this) } -infix fun CommandMeta.executor(lambda: CommandBuilder.() -> Unit): CommandMeta { - executor = commandExecutor(lambda) + +infix fun CommandMeta.commandExecutor(lambda: CommandBuilder.() -> Unit): CommandMeta { + executor = command(lambda) return this } -fun commandExecutor(lambda: CommandBuilder.() -> Unit) = +fun command(lambda: CommandBuilder.() -> Unit) = CommandExecutor { context: CommandContext -> CommandBuilder(context).let { - try { - it.lambda() - } catch (e: ContextReciever.BreakParseException) { - } + it.lambda() it.build() } } diff --git a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandModel.kt b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandModel.kt index fd21875..df08d90 100644 --- a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandModel.kt +++ b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/CommandModel.kt @@ -13,11 +13,28 @@ open class ContextReciever(val context: CommandContext) { } } + fun tabCompletions(vararg strings: String) = context.tabCompletions.addAll(strings) + fun tabCompletions(strings: List) = context.tabCompletions.addAll(strings) + fun hasNextArg() = context.argIndex < context.args.size - fun complete(): Nothing = throw BreakParseException() + fun commandMissing() { + throw BreakParseException.ParseMissingException() + } + + fun commandInvalid() { + throw BreakParseException.ParseInvalidException() + } - class BreakParseException : RuntimeException() + fun commandComplete() { + throw BreakParseException.ParseCompletedException() + } + + sealed class BreakParseException : RuntimeException() { + class ParseCompletedException : BreakParseException() + class ParseMissingException : BreakParseException() + class ParseInvalidException : BreakParseException() + } } data class ParsedCommand( diff --git a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/BaseParser.kt b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/BaseParser.kt index d438bb9..fbc13f3 100644 --- a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/BaseParser.kt +++ b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/BaseParser.kt @@ -36,7 +36,7 @@ abstract class BaseParser( throw RunInvalidBlock() } else if (context.runExecutors) { conditionFail(stringArg!!) - complete() + commandInvalid() } } } diff --git a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/BooleanParser.kt b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/BooleanParser.kt index 9b9010a..53fcc19 100644 --- a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/BooleanParser.kt +++ b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/BooleanParser.kt @@ -3,14 +3,14 @@ package `in`.kyle.mcspring.commands.dsl.parsers import `in`.kyle.mcspring.commands.dsl.CommandContext -class BooleanParser(context: CommandContext, stringArg: String) : BaseParser(context, stringArg) { +class BooleanParser(context: CommandContext, stringArg: String?) : BaseParser(context, stringArg) { init { val booleans = listOf("true", "false") context.tabCompletions.addAll(booleans) - val lowered = stringArg.toLowerCase() + val lowered = stringArg?.toLowerCase() if (lowered in booleans) { - returnValue = lowered.toBoolean() + returnValue = lowered!!.toBoolean() } } } diff --git a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/MapParser.kt b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/MapParser.kt index 946936f..dc6cb68 100644 --- a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/MapParser.kt +++ b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/MapParser.kt @@ -2,7 +2,7 @@ package `in`.kyle.mcspring.commands.dsl.parsers import `in`.kyle.mcspring.commands.dsl.CommandContext -class MapParser(context: CommandContext, stringArg: String) : BaseParser(context, stringArg) { +class MapParser(context: CommandContext, stringArg: String?) : BaseParser(context, stringArg) { fun map(vararg pairs: Pair) = map(mapOf(*pairs)) fun map(map: Map) = mapped(map.keys) { map[it] } diff --git a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/PlayerParser.kt b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/PlayerParser.kt index d5eb470..502a2c4 100644 --- a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/PlayerParser.kt +++ b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/PlayerParser.kt @@ -6,11 +6,13 @@ import org.bukkit.entity.Player class PlayerParser( context: CommandContext, - stringArg: String + stringArg: String? ) : BaseParser(context, stringArg) { init { val players = Bukkit.getOnlinePlayers() context.tabCompletions.addAll(players.map { it.name }) - returnValue = Bukkit.getPlayer(stringArg) + if (stringArg != null) { + returnValue = Bukkit.getPlayer(stringArg) + } } } diff --git a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/StringParser.kt b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/StringParser.kt index 293ee18..89c8142 100644 --- a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/StringParser.kt +++ b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/StringParser.kt @@ -2,7 +2,7 @@ package `in`.kyle.mcspring.commands.dsl.parsers import `in`.kyle.mcspring.commands.dsl.CommandContext -class StringParser(context: CommandContext, stringArg: String) : BaseParser(context, stringArg) { +class StringParser(context: CommandContext, stringArg: String?) : BaseParser(context, stringArg) { init { returnValue = stringArg } diff --git a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/DoubleParser.kt b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/DoubleParser.kt index 100d5cb..2006ad1 100644 --- a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/DoubleParser.kt +++ b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/DoubleParser.kt @@ -2,7 +2,7 @@ package `in`.kyle.mcspring.commands.dsl.parsers.numbers import `in`.kyle.mcspring.commands.dsl.CommandContext -class DoubleParser(context: CommandContext, stringArg: String) : NumberParser(context, stringArg) { +class DoubleParser(context: CommandContext, stringArg: String?) : NumberParser(context, stringArg) { override fun zero() = 0.0 override fun toNumber(s: String): Double? = s.toDoubleOrNull() override fun parse(s: String) = s.toDoubleOrNull() diff --git a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/IntParser.kt b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/IntParser.kt index 323065f..9c743cb 100644 --- a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/IntParser.kt +++ b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/IntParser.kt @@ -3,7 +3,7 @@ package `in`.kyle.mcspring.commands.dsl.parsers.numbers import `in`.kyle.mcspring.commands.dsl.CommandContext import `in`.kyle.mcspring.commands.dsl.parsers.ConditionFail -class IntParser(context: CommandContext, stringArg: String) : NumberParser(context, stringArg) { +class IntParser(context: CommandContext, stringArg: String?) : NumberParser(context, stringArg) { override fun zero() = 0 override fun toNumber(s: String): Int? = s.toIntOrNull() fun even(conditionFail: ConditionFail) = require(predicate = { it % 2 == 0 && it != 0 }, conditionFail = conditionFail) diff --git a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/NumberParser.kt b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/NumberParser.kt index f600210..8e1477d 100644 --- a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/NumberParser.kt +++ b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/NumberParser.kt @@ -4,12 +4,12 @@ import `in`.kyle.mcspring.commands.dsl.parsers.BaseParser import `in`.kyle.mcspring.commands.dsl.CommandContext import `in`.kyle.mcspring.commands.dsl.parsers.ConditionFail -abstract class NumberParser(context: CommandContext, stringArg: String) : BaseParser(context, stringArg) +abstract class NumberParser(context: CommandContext, stringArg: String?) : BaseParser(context, stringArg) where T : Number, T : Comparable { init { - returnValue = parse(stringArg) + returnValue = if (stringArg != null) this.parse(stringArg) else null } internal abstract fun toNumber(s: String): T? diff --git a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/util/CommandMapWrapper.kt b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/util/CommandMapWrapper.kt index fd5cc7a..6853273 100644 --- a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/util/CommandMapWrapper.kt +++ b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/util/CommandMapWrapper.kt @@ -2,10 +2,17 @@ package `in`.kyle.mcspring.commands.dsl.util import org.bukkit.Bukkit import org.bukkit.command.Command +import org.bukkit.command.CommandExecutor import org.bukkit.command.CommandMap +import org.bukkit.command.TabCompleter import org.bukkit.plugin.Plugin +import org.bukkit.plugin.java.JavaPlugin +import org.slf4j.LoggerFactory object CommandMapWrapper { + + val logger by lazy { LoggerFactory.getLogger(CommandMapWrapper::class.java) } + val commandMap by lazy { val bukkitCommandMap = Bukkit.getServer().javaClass.getDeclaredField("commandMap") bukkitCommandMap.isAccessible = true @@ -13,6 +20,28 @@ object CommandMapWrapper { } fun registerCommand(plugin: Plugin, command: Command) { - commandMap.register(command.label, plugin.name, command) + require(plugin is JavaPlugin) { "Plugin must be an instance of JavaPlugin to register commands" } + val pluginCommand = plugin.getCommand(command.name) + if (pluginCommand != null) { + pluginCommand.apply { + val (executor, tabCompleter) = delegateTabsAndCommands(command) + setExecutor(executor) + setTabCompleter(tabCompleter) + } + } else { + val registered = commandMap.register(command.label, plugin.name, command) + if (!registered) { + logger.warn("""Could not register ${command.label} for ${plugin.name}. + There is a command label conflict. + Using aliases instead.""".trimIndent()) + } + } + } + + private fun delegateTabsAndCommands(command: Command): Pair { + return Pair( + CommandExecutor { sender, _, label, args -> command.execute(sender, label, args) }, + TabCompleter { sender, _, alias, args -> command.tabComplete(sender, alias, args) } + ) } } diff --git a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/util/CommandUtils.kt b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/util/CommandUtils.kt index 931dc97..6b612b1 100644 --- a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/util/CommandUtils.kt +++ b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/util/CommandUtils.kt @@ -2,33 +2,86 @@ package `in`.kyle.mcspring.commands.dsl.util import `in`.kyle.mcspring.commands.dsl.CommandContext import `in`.kyle.mcspring.commands.dsl.CommandMeta +import `in`.kyle.mcspring.commands.dsl.ContextReciever +import `in`.kyle.mcspring.commands.dsl.ContextReciever.* +import `in`.kyle.mcspring.commands.dsl.ContextReciever.BreakParseException.* +import `in`.kyle.mcspring.commands.dsl.ParsedCommand import org.bukkit.command.Command +import org.bukkit.command.CommandException import org.bukkit.command.CommandSender import org.bukkit.plugin.Plugin +class NoValidExecutorException(string: String) : CommandException(string) + object CommandUtils { + private val noValidExecutorException = NoValidExecutorException(""" + It appears that this command executed without hitting a terminal block. + A terminal block is an endpoint for a command. Such would be `missing`, `invalid`, + or `then`. Make sure your command executes one of these blocks. + """.trimIndent()) + fun register(plugin: Plugin, meta: CommandMeta) { - meta.preRegister() + meta.validate() CommandMapWrapper.registerCommand(plugin, makeBukkitCommand(meta).apply { aliases = meta.aliases description = meta.description usage = meta.usageMessage - permission = meta.usageMessage.takeIf { it.isNotBlank() } - permissionMessage = meta.permissionMessage.takeIf { it.isNotBlank() } + meta.permission.takeIf { it.isNotBlank() } + ?.run { permission = meta.permission } + meta.permissionMessage.takeIf { it.isNotBlank() } + ?.run { permissionMessage = meta.permissionMessage } }) } + fun runCommand(context: CommandContext, provider: (CommandContext) -> ParsedCommand) { + try { + provider(context) + throw noValidExecutorException + } catch (_: BreakParseException) { + } + } + + fun getCompletions(context: CommandContext, provider: (CommandContext) -> ParsedCommand): List { + val completions = try { + provider(context) + throw noValidExecutorException + } catch (e: BreakParseException) { + when (e) { + is ParseCompletedException -> listOf() + else -> context.tabCompletions + } + } + + // make sure this is the last arg + return if (context.argIndex+1 == context.args.size || context.args.isEmpty()) { + val arg = context.nextArg ?: "" + completions.filter { it.toLowerCase().startsWith(arg.toLowerCase()) } + } else { + emptyList() + } + } + private fun makeBukkitCommand(meta: CommandMeta) = object : Command(meta.name) { override fun execute( sender: CommandSender, commandLabel: String, - args: Array + args: Array ): Boolean { - val provider = meta.executor.provider val context = CommandContext(sender, label, args.toList()) - provider(context) + val provider = meta.executor.provider + runCommand(context, provider) return true } + + override fun tabComplete( + sender: CommandSender, + alias: String, + args: Array + ): List { + val provider = meta.executor.provider + val context = CommandContext(sender, label, args.toList(), runExecutors = false) + return getCompletions(context, provider) + } } } diff --git a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/CommandTestSupport.kt b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/CommandTestSupport.kt index 2445d07..12357f6 100644 --- a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/CommandTestSupport.kt +++ b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/CommandTestSupport.kt @@ -1,18 +1,28 @@ package `in`.kyle.mcspring.commands.dsl +import `in`.kyle.mcspring.commands.dsl.util.CommandUtils import org.bukkit.entity.Player import org.mockito.ArgumentMatchers import org.mockito.Mockito.doAnswer import org.mockito.Mockito.mock +class TestException : RuntimeException() + object CommandTestSupport { fun runCommand(command: String, lambda: CommandBuilder.() -> Unit): String { val (context, messages) = makeContext(command) - commandExecutor(lambda).provider(context) + val provider = command(lambda).provider + CommandUtils.runCommand(context, provider) return messages.joinToString("\n") } + fun getCompletions(command: String, lambda: CommandBuilder.() -> Unit): List { + val (context, _) = makeContext(command) + val provider = command(lambda).provider + return CommandUtils.getCompletions(context, provider) + } + fun makeContext( command: String, runExecutors: Boolean = true diff --git a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestArgBuilder.kt b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestArgBuilder.kt index efa134b..4802426 100644 --- a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestArgBuilder.kt +++ b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestArgBuilder.kt @@ -18,7 +18,6 @@ class TestArgBuilder : FreeSpec({ val test = Test(makeContext("test").first) test.returnValue = null - class TestException : RuntimeException() shouldThrow { test.invalid { throw TestException() @@ -27,8 +26,8 @@ class TestArgBuilder : FreeSpec({ } "invalid block does not run when valid" - { - val test = Test(makeContext("test", runExecutors = false).first) - test.returnValue = null + val test = Test(makeContext("test").first) + test.returnValue = "" test.invalid { fail("should not run") } diff --git a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestCommandBuilder.kt b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestCommandBuilder.kt index 9d86089..1a7b1a1 100644 --- a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestCommandBuilder.kt +++ b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestCommandBuilder.kt @@ -6,7 +6,6 @@ import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.FreeSpec import io.kotest.matchers.shouldBe import io.mockk.mockk -import java.lang.RuntimeException class TestCommandBuilder : FreeSpec({ @@ -14,6 +13,7 @@ class TestCommandBuilder : FreeSpec({ "require(true) should not fail" - { runCommand("test") { require({ true }) { fail("should not run") } + commandComplete() } } @@ -24,18 +24,31 @@ class TestCommandBuilder : FreeSpec({ } } + "missing arg should work" - { + shouldThrow { + runCommand("") { + stringArg { + missing { + throw TestException() + } + } + } + } + } + "then should only run while executors are enabled" - { "test disabled" - { - CommandBuilder(CommandContext(mockk(), "test", listOf(), mutableListOf(), runExecutors = false)).apply { - then { - fail("should not run") + shouldThrow { + CommandBuilder(CommandContext(mockk(), "test", listOf(), mutableListOf(), runExecutors = false)).apply { + then { + fail("should not run") + } } } } "test enabled" - { - runCommand("test") { - class TestException : RuntimeException() - shouldThrow { + shouldThrow { + runCommand("test") { then { throw TestException() } diff --git a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestSubcommandBuilder.kt b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestSubcommandBuilder.kt index 378ad05..2879cb2 100644 --- a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestSubcommandBuilder.kt +++ b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestSubcommandBuilder.kt @@ -11,9 +11,12 @@ class TestSubcommandBuilder : FreeSpec({ val context = makeContext("").first val builder = SubcommandBuilder(context) - builder.apply { - shouldThrow { - missing {} + shouldThrow { + builder.apply { + on("test") {} + missing { + throw TestException() + } } } } @@ -22,9 +25,11 @@ class TestSubcommandBuilder : FreeSpec({ val context = makeContext("arg1").first val builder = SubcommandBuilder(context) - builder.apply { - shouldThrow { - invalid {} + shouldThrow { + builder.apply { + invalid { + throw TestException() + } } } } @@ -33,10 +38,11 @@ class TestSubcommandBuilder : FreeSpec({ val context = makeContext("sub1").first val builder = SubcommandBuilder(context) - builder.apply { - shouldThrow { + shouldThrow { + builder.apply { on("sub1") { context.argIndex shouldBe 1 + throw TestException() } } } diff --git a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestTabCompletions.kt b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestTabCompletions.kt new file mode 100644 index 0000000..b8c33f5 --- /dev/null +++ b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestTabCompletions.kt @@ -0,0 +1,66 @@ +package `in`.kyle.mcspring.commands.dsl + +import `in`.kyle.mcspring.commands.dsl.CommandTestSupport.getCompletions +import io.kotest.core.spec.style.FreeSpec +import io.kotest.matchers.shouldBe + +class TestTabCompletions : FreeSpec({ + "should return completions for command" - { + getCompletions("") { + tabCompletions("one", "two", "three") + commandMissing() + } shouldBe listOf("one", "two", "three") + } + + "should return partial completions" - { + getCompletions("on") { + tabCompletions("one", "two", "three") + commandMissing() + } shouldBe listOf("one") + } + + "completed commands return no completions" - { + getCompletions("") { + tabCompletions("one", "two", "three") + commandComplete() + } shouldBe emptyList() + } + + "multi-arg commands should reset completions" - { + getCompletions("one") { + mapArg { + parser { + map("one" to 1) + } + } + mapArg { + parser { + map("two" to 1) + } + missing {} + } + + commandComplete() + } shouldBe listOf("two") + } + + "invalid arg values should stop parsing" - { + getCompletions("on tw") { + mapArg { + parser { + map("one" to 1) + } + invalid {} + } + + mapArg { + parser { + map("two" to 1) + } + missing {} + } + + commandComplete() + } shouldBe emptyList() + } +}) diff --git a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestValueBuilder.kt b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestValueBuilder.kt index a124863..718e56b 100644 --- a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestValueBuilder.kt +++ b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestValueBuilder.kt @@ -2,9 +2,7 @@ package `in`.kyle.mcspring.commands.dsl import `in`.kyle.mcspring.commands.dsl.CommandTestSupport.makeContext import `in`.kyle.mcspring.commands.dsl.parsers.numbers.IntParser -import io.kotest.assertions.failure import io.kotest.assertions.throwables.shouldThrow -import io.kotest.assertions.throwables.shouldThrowMessage import io.kotest.core.spec.style.FreeSpec import io.kotest.matchers.shouldBe @@ -22,7 +20,7 @@ class TestValueBuilder : FreeSpec({ builder.returnValue shouldBe 1 } - "default should not overwrite" - { + "default should not overwrite existing" - { val context = makeContext("one").first val builder = ValueBuilder(context) { IntParser(context, it) } builder.returnValue = 99 @@ -39,11 +37,10 @@ class TestValueBuilder : FreeSpec({ val context = makeContext("").first val builder = ValueBuilder(context) { IntParser(context, it) } - builder.apply { - class Test : RuntimeException() - shouldThrow { + shouldThrow { + builder.apply { missing { - throw Test() + throw TestException() } } } diff --git a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/TestMapParser.kt b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/TestMapParser.kt index 9cb5f13..08c21ca 100644 --- a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/TestMapParser.kt +++ b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/TestMapParser.kt @@ -1,8 +1,12 @@ package `in`.kyle.mcspring.commands.dsl.parsers +import `in`.kyle.mcspring.commands.dsl.CommandTestSupport.getCompletions import `in`.kyle.mcspring.commands.dsl.CommandTestSupport.makeBuilder +import `in`.kyle.mcspring.commands.dsl.TestException +import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.FreeSpec import io.kotest.matchers.shouldBe +import java.lang.RuntimeException class TestMapParser : FreeSpec({ @@ -21,4 +25,33 @@ class TestMapParser : FreeSpec({ } } shouldBe 1 } + + "missing map arg should not error" - { + shouldThrow { + makeBuilder("").mapArg { + parser { + map("one" to 1) + map("two" to 2) + map("three" to 3) + } + missing { + throw TestException() + } + } + } + } + + "test tab completions" - { + getCompletions(" ") { + mapArg { + parser { + map("one" to 1) + map("two" to 2) + map("three" to 3) + } + invalid { } + missing { } + } + } shouldBe listOf("one", "two", "three") + } }) diff --git a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/TestStringParser.kt b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/TestStringParser.kt index 9fea91c..56c9d37 100644 --- a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/TestStringParser.kt +++ b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/TestStringParser.kt @@ -1,6 +1,8 @@ package `in`.kyle.mcspring.commands.dsl.parsers import `in`.kyle.mcspring.commands.dsl.CommandTestSupport.runCommand +import `in`.kyle.mcspring.commands.dsl.TestException +import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.FreeSpec import io.kotest.matchers.shouldBe import io.kotest.property.Arb @@ -14,17 +16,20 @@ class TestStringParser : FreeSpec({ runCommand("test") { val arg0 = stringArg { } arg0 shouldBe "test" + commandComplete() } } "missing called on empty string" - { - runCommand("") { - val arg0 = stringArg { - missing { message("missing arg") } - invalid { fail("should not run") } + shouldThrow { + runCommand("") { + stringArg { + invalid { fail("should not run") } + missing { throw TestException() } + } + fail("should not run") } - fail("should not run") - } shouldBe "missing arg" + } } "multiple strings are parses correctly" - { @@ -42,6 +47,8 @@ class TestStringParser : FreeSpec({ invalid { fail("should not run") } } arg1 shouldBe "arg2" + + commandComplete() } } @@ -53,6 +60,7 @@ class TestStringParser : FreeSpec({ invalid { fail("should not run") } } arg1 shouldBe it + commandComplete() } } } diff --git a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/TestIntParser.kt b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/TestIntParser.kt index de35318..9f5326f 100644 --- a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/TestIntParser.kt +++ b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/numbers/TestIntParser.kt @@ -16,6 +16,7 @@ class TestIntParser : FreeSpec({ missing { fail("should not run") } } arg0 shouldBe 123 + commandComplete() } } @@ -27,6 +28,7 @@ class TestIntParser : FreeSpec({ missing { fail("should not run") } } testArg shouldBe it + commandComplete() } } } diff --git a/mcspring-build/mcspring-gradle-plugin/build.gradle.kts b/mcspring-build/mcspring-gradle-plugin/build.gradle.kts index cac2ab6..cee74ca 100644 --- a/mcspring-build/mcspring-gradle-plugin/build.gradle.kts +++ b/mcspring-build/mcspring-gradle-plugin/build.gradle.kts @@ -5,7 +5,7 @@ plugins { } group = "in.kyle.mcspring" -version = "0.0.1" +version = "0.0.2" repositories { jcenter() diff --git a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/BuildPlugin.kt b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/BuildPlugin.kt index 29bbfe3..2b0aeb4 100644 --- a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/BuildPlugin.kt +++ b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/BuildPlugin.kt @@ -41,7 +41,6 @@ class BuildPlugin : Plugin { doFirst { pluginsDir.mkdirs() } - mustRunAfter("buildServer") into(pluginsDir) } diff --git a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/McSpringExtension.kt b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/McSpringExtension.kt index 68a82a5..6ff5139 100644 --- a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/McSpringExtension.kt +++ b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/McSpringExtension.kt @@ -8,7 +8,7 @@ open class McSpringExtension(project: Project) { var spigotDownloadUrl: String = "https://papermc.io/api/v1/paper/$spigotVersion/latest/download" var spigotDirectory: String = project.projectDir.resolve("spigot").absolutePath - var pluginMainPackage: String = "${project.group}.${project.name}" + var pluginMainPackage: String? = null var pluginName: String? = null var pluginVersion: String? = null var pluginDescription: String? = project.description @@ -20,9 +20,6 @@ open class McSpringExtension(project: Project) { var pluginPrefix: String? = null var pluginLoadBefore: List? = null - val pluginMainClassName: String - get() = "$pluginMainPackage.SpringPlugin" - enum class Load { STARTUP, POSTWORLD } diff --git a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt index 656d41f..f1f4947 100644 --- a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt +++ b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt @@ -1,17 +1,20 @@ package `in`.kyle.mcspring.tasks +import `in`.kyle.mcspring.McSpringExtension import `in`.kyle.mcspring.annotation.PluginDepend import `in`.kyle.mcspring.annotation.SoftPluginDepend import `in`.kyle.mcspring.commands.dsl.mcspring.Command import `in`.kyle.mcspring.div import `in`.kyle.mcspring.getMainSourceSet import `in`.kyle.mcspring.mcspring +import io.github.classgraph.AnnotationParameterValueList import io.github.classgraph.ClassGraph import org.apache.log4j.Logger import org.gradle.api.DefaultTask +import org.gradle.api.GradleException import org.gradle.api.tasks.TaskAction +import org.springframework.boot.autoconfigure.SpringBootApplication import org.yaml.snakeyaml.Yaml -import java.io.File import java.net.URLClassLoader import java.time.LocalDateTime import java.time.format.DateTimeFormatter @@ -25,11 +28,10 @@ open class BuildPluginYml : DefaultTask() { @TaskAction fun buildYml() { val props = project.extensions.mcspring + logInfo(props) props.apply { - attributes["name"] = pluginName ?: project.name - attributes["version"] = pluginVersion ?: project.version - attributes["main"] = "$pluginMainPackage.SpringJavaPlugin" + writeMainAttributes() fun writeNonNull(key: String, value: Any?) = value?.apply { attributes[key] = this } @@ -43,14 +45,56 @@ open class BuildPluginYml : DefaultTask() { writeNonNull("loadbefore", pluginLoadBefore) } - project.getMainSourceSet().runtimeClasspath.files.apply { - addDependencies(this) - addCommands(this) - } + project.getMainSourceSet().runtimeClasspath.files + .apply { + val classLoader = URLClassLoader(this.map { it.toURI().toURL() }.toTypedArray()) + addDependencies(classLoader) + addCommands(classLoader) + addSpringBootMain(classLoader) + classLoader.close() + } writeYmlFile() } + private fun McSpringExtension.writeMainAttributes() { + val name = pluginName ?: project.name + val version = pluginVersion ?: project.version + if (pluginMainPackage == null) { + pluginMainPackage = "${project.group}.${project.name}" + logger.info("No main package specified, using $pluginMainPackage") + } + val main = "$pluginMainPackage.SpringJavaPlugin" + + if (!name.matches("[a-zA-Z0-9_-]+".toRegex())) { + throw GradleException(""" + Invalid plugin name: $name + The plugin name must consist of all alphanumeric characters and underscores (a-z,A-Z,0-9,_) + + Update the project name + OR + Update the generated name by setting the `pluginName` in the mcspring extension block. + """.trimIndent()) + } + + val pattern = "(\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*\\.)*\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*" + if (!main.matches(pattern.toRegex())) { + throw GradleException(""" + Invalid main class location: $main + Refer to the Java specification for valid package/class names. + The main class location is set using: {project group}.{project location}.SpringJavaPlugin + + Update the project group and project name + OR + Update the generated main package by setting the `pluginMainPackage` in the mcspring extension block. + """.trimIndent()) + } + + attributes["name"] = name + attributes["version"] = version + attributes["main"] = main + } + private fun writeYmlFile() { val outputFile = project.buildDir / "resources" / "main" outputFile.mkdirs() @@ -59,6 +103,8 @@ open class BuildPluginYml : DefaultTask() { val formatter = DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm:SS") val nowFormatted = LocalDateTime.now().format(formatter) + if (pluginYml.exists()) pluginYml.delete() + pluginYml.createNewFile() pluginYml.appendText(""" # File auto generated by mcspring on $nowFormatted # https://github.com/kylepls/mcspring @@ -67,8 +113,7 @@ open class BuildPluginYml : DefaultTask() { pluginYml.appendText(Yaml().dumpAsMap(attributes)) } - private fun addDependencies(files: Set) { - val classLoader = URLClassLoader(files.map { it.toURI().toURL() }.toTypedArray()) + private fun addDependencies(classLoader: ClassLoader) { val scanResult = ClassGraph() .overrideClassLoaders(classLoader) .enableAnnotationInfo() @@ -89,8 +134,7 @@ open class BuildPluginYml : DefaultTask() { } } - private fun addCommands(files: Set) { - val classLoader = URLClassLoader(files.map { it.toURI().toURL() }.toTypedArray()) + private fun addCommands(classLoader: ClassLoader) { val scanResult = ClassGraph() .overrideClassLoaders(classLoader) .enableAnnotationInfo() @@ -99,11 +143,14 @@ open class BuildPluginYml : DefaultTask() { .scan() val annotations = scanResult.use { - fun getAnnotations(annotation: KClass<*>) = - scanResult.getClassesWithMethodAnnotation(annotation.qualifiedName!!) - .flatMap { it.methodInfo.filter { it.hasAnnotation(annotation.qualifiedName!!) } } - .map { it.getAnnotationInfo(annotation.qualifiedName!!) } - .map { it.parameterValues } + fun getAnnotations(annotation: KClass<*>): List { + val methods = scanResult.getClassesWithMethodAnnotation(annotation.qualifiedName!!) + .flatMap { it.methodInfo.filter { it.hasAnnotation(annotation.qualifiedName!!) } } + val annotations = + methods.map { it.getAnnotationInfo(annotation.qualifiedName!!) } + .map { it.parameterValues } + return annotations + } getAnnotations(Command::class) } @@ -132,4 +179,47 @@ open class BuildPluginYml : DefaultTask() { attributes["commands"] = commands } } + + private fun addSpringBootMain(classLoader: ClassLoader) { + val scanResult = ClassGraph() + .overrideClassLoaders(classLoader) + .enableAnnotationInfo() + .scan(4) + val mains = scanResult.use { + scanResult + .allStandardClasses + .filter { it.hasAnnotation(SpringBootApplication::class.qualifiedName) } + .map { it.name } + } + require(mains.size == 1) { + """ + There should be 1 main class on the classpath: $mains + Make sure to annotate a class with @SpringPlugin + This serves as an entry point for mcspring. + """.trimIndent() + } + attributes["spring-boot-main"] = mains.first() + } + + private fun logInfo(extension: McSpringExtension) { + extension.apply { + logger.info("Spigot Version: $spigotVersion") + logger.info("Spigot Download URL: $spigotDownloadUrl") + logger.info("Spigot Directory: $spigotDirectory") + logger.info("Plugin Main Package: $pluginMainPackage") + logger.info("Project Name: ${project.name}") + logger.info("Project Version: ${project.version}") + logger.info("Project Group: ${project.group}") + logger.info("Plugin Name: $pluginName") + logger.info("Plugin Version: $pluginVersion") + logger.info("Plugin Description: $pluginDescription") + logger.info("Plugin Load: $pluginLoad") + logger.info("Plugin Author: $pluginAuthor") + logger.info("Plugin Authors: $pluginAuthors") + logger.info("Plugin Website: $pluginWebsite") + logger.info("Plugin Database: $pluginDatabase") + logger.info("Plugin Prefix: $pluginPrefix") + logger.info("Plugin Load Before: $pluginLoadBefore") + } + } } diff --git a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/GradleContext.kt b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/GradleContext.kt new file mode 100644 index 0000000..0a3b474 --- /dev/null +++ b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/GradleContext.kt @@ -0,0 +1,76 @@ +package `in`.kyle.mcspring + +import org.yaml.snakeyaml.Yaml +import java.io.File + +class GradleContext private constructor() { + + val folder = createTempDir() + val buildFile by lazy { folder / "build.gradle" } + val kotlinSourceFolder by lazy { folder / "src" / "main" / "kotlin" } + val pluginYml by lazy { folder / "build" / "resources" / "main" / "plugin.yml" } + val libsFolder by lazy { folder / "build" / "libs" } + val spigotFolder by lazy { folder / "spigot" } + + val pluginYmlContents: Map + get() = Yaml().load(pluginYml.inputStream()) + + fun runTask(goal: String) = runGradle(folder, goal).task(":$goal")!! + + private fun writeMainClass() { + val srcFile = kotlinSourceFolder / "ExampleMain.kt" + srcFile += """ + |import org.springframework.boot.autoconfigure.SpringBootApplication + |@SpringBootApplication + |open class ExampleMain + """.trimMargin() + } + + companion object { + fun setup(): GradleContext { + val context = GradleContext() + context.writeMainClass() + writeBaseGradleConfig(context.buildFile) + return context + } + } +} + +private fun writeBaseGradleConfig(file: File) { + file += """ + |plugins { + | id("org.jetbrains.kotlin.jvm") version "1.3.72" + | id("in.kyle.mcspring") + |} + | + |repositories { + | mavenLocal() + | jcenter() + |} + |dependencies { + | implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") + | implementation("in.kyle.mcspring:mcspring-base:+") + | implementation("in.kyle.mcspring:mcspring-commands-dsl:+") + |} + |mcspring { + | pluginName = "test" + | pluginMainPackage = "test.plugin" + |} + """.trimMargin() + val settings = file.parentFile / "settings.gradle.kts" + settings += """ + |plugins { + | id("com.gradle.enterprise").version("3.3.4") + |} + |gradleEnterprise { + | buildScan { + | termsOfServiceUrl = "https://gradle.com/terms-of-service" + | termsOfServiceAgree = "yes" + | } + |} + """.trimMargin() + val gradleProperties = file.parentFile / "gradle.properties" + gradleProperties += """ + org.gradle.jvmargs=-Xmx1024m + """.trimIndent() +} diff --git a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/PluginTestSupport.kt b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/PluginTestSupport.kt index 5ba5d43..481eb74 100644 --- a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/PluginTestSupport.kt +++ b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/PluginTestSupport.kt @@ -4,35 +4,6 @@ import org.gradle.testkit.runner.BuildResult import org.gradle.testkit.runner.GradleRunner import java.io.File -fun writeBaseGradleConfig(file: File) { - file += """ - |plugins { - | id("org.jetbrains.kotlin.jvm") version "1.3.72" - | id("in.kyle.mcspring") - |} - | - |repositories { - | jcenter() - |} - """.trimMargin() - val settings = file.parentFile / "settings.gradle.kts" - settings += """ - |plugins { - | id("com.gradle.enterprise").version("3.3.4") - |} - |gradleEnterprise { - | buildScan { - | termsOfServiceUrl = "https://gradle.com/terms-of-service" - | termsOfServiceAgree = "yes" - | } - |} - """.trimMargin() - val gradleProperties = file.parentFile / "gradle.properties" - gradleProperties += """ - org.gradle.jvmargs=-Xmx1024m - """.trimIndent() -} - fun runGradle(folder: File, vararg args: String): BuildResult { return GradleRunner.create() .withProjectDir(folder) diff --git a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestBuildPluginJar.kt b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestBuildPluginJar.kt index 7797000..c740f36 100644 --- a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestBuildPluginJar.kt +++ b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestBuildPluginJar.kt @@ -1,9 +1,6 @@ package `in`.kyle.mcspring.tasks -import `in`.kyle.mcspring.div -import `in`.kyle.mcspring.plusAssign -import `in`.kyle.mcspring.runGradle -import `in`.kyle.mcspring.writeBaseGradleConfig +import `in`.kyle.mcspring.* import io.github.classgraph.ClassGraph import io.kotest.assertions.assertSoftly import io.kotest.core.spec.style.FreeSpec @@ -16,31 +13,14 @@ import java.net.URLClassLoader class TestBuildPluginJar : FreeSpec({ "should create proper plugin jar" - { - val folder = createTempDir() - val buildFile = folder / "build.gradle" - writeBaseGradleConfig(buildFile) - buildFile += """ - |repositories { - | mavenCentral() - | mavenLocal() - |} - |dependencies { - | implementation "org.jetbrains.kotlin:kotlin-stdlib" - | implementation "in.kyle.mcspring:mcspring-base:+" - |} - |mcspring { - | pluginMainPackage = "main" - |} - """.trimMargin() + val gradle = GradleContext.setup() - val srcFile = folder / "src" / "main" / "kotlin" / "Base.kt" - srcFile += "fun test() { }" + (gradle.kotlinSourceFolder / "Base.kt") += "fun test() { }" - val result = runGradle(folder, "buildPluginJar") - val task = result.task(":buildPluginJar")!! + val task = gradle.runTask("buildPluginJar") task.outcome shouldBe TaskOutcome.SUCCESS - val pluginJar = (folder / "build" / "libs").listFiles()?.firstOrNull() + val pluginJar = gradle.libsFolder.listFiles()?.firstOrNull() ?: error("Plugin jar not generated") val classLoader = URLClassLoader(arrayOf(pluginJar.toURI().toURL())) @@ -61,8 +41,8 @@ class TestBuildPluginJar : FreeSpec({ "plugin main should be relocated" - { assertSoftly { - scan.allClasses.filter { it.packageName == "main" } - .map { it.name } shouldBe listOf("main.SpringJavaPlugin") + scan.allClasses.filter { it.packageName == "test.plugin" } + .map { it.name } shouldBe listOf("test.plugin.SpringJavaPlugin") } } diff --git a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestBuildPluginYml.kt b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestBuildPluginYml.kt index 5eb3f9f..1d31f0b 100644 --- a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestBuildPluginYml.kt +++ b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestBuildPluginYml.kt @@ -1,47 +1,35 @@ package `in`.kyle.mcspring.tasks +import `in`.kyle.mcspring.GradleContext import `in`.kyle.mcspring.div import `in`.kyle.mcspring.plusAssign -import `in`.kyle.mcspring.runGradle -import `in`.kyle.mcspring.writeBaseGradleConfig import io.kotest.assertions.assertSoftly import io.kotest.core.spec.style.FreeSpec import io.kotest.matchers.shouldBe import io.kotest.matchers.string.shouldEndWith import org.gradle.testkit.runner.TaskOutcome -import org.yaml.snakeyaml.Yaml class TestBuildPluginYml : FreeSpec({ "should write minimum required info" - { - val folder = createTempDir() - val buildFile = folder / "build.gradle.kts" - writeBaseGradleConfig(buildFile) - buildFile += """ - version = "29" - """.trimIndent() - - val result = runGradle(folder, "buildPluginYml") - val task = result.task(":buildPluginYml")!! - task.outcome shouldBe TaskOutcome.SUCCESS + val gradle = GradleContext.setup() + gradle.buildFile += """version = "29" """ - val pluginYml = folder / "build" / "resources" / "main" / "plugin.yml" - val yml = Yaml().load>(pluginYml.inputStream()) + val task = gradle.runTask("buildPluginYml") + task.outcome shouldBe TaskOutcome.SUCCESS - assertSoftly { - yml.size shouldBe 3 - yml["name"].toString() shouldEndWith ".tmp" - yml["version"] shouldBe "29" - yml["main"].toString() shouldEndWith ".SpringJavaPlugin" + gradle.pluginYmlContents.assertSoftly { + it.size shouldBe 4 + it["name"] shouldBe "test" + it["version"] shouldBe "29" + it["main"].toString() shouldEndWith ".SpringJavaPlugin" + it["spring-boot-main"] shouldBe "ExampleMain" } } "should write all user-specified info properly" - { - val folder = createTempDir() - val buildFile = folder / "build.gradle" - writeBaseGradleConfig(buildFile) - // For some reason this doesn't work with the Kotlin dsl??? - buildFile += """ + val gradle = GradleContext.setup() + gradle.buildFile += """ |mcspring { | pluginDescription = "test description" | pluginLoad = "startup" @@ -54,78 +42,44 @@ class TestBuildPluginYml : FreeSpec({ |} """.trimMargin() - val result = runGradle(folder, "buildPluginYml") - val task = result.task(":buildPluginYml")!! + val task = gradle.runTask("buildPluginYml") task.outcome shouldBe TaskOutcome.SUCCESS - val pluginYml = folder / "build" / "resources" / "main" / "plugin.yml" - val yml = Yaml().load>(pluginYml.inputStream()) - - assertSoftly { - yml["description"] shouldBe "test description" - yml["load"] shouldBe "startup" - yml["author"] shouldBe "kyle" - yml["authors"] shouldBe listOf("kyle1", "kyle2") - yml["website"] shouldBe "github" - yml["database"] shouldBe true - yml["prefix"] shouldBe "prefix" - yml["loadbefore"] shouldBe listOf("other") + gradle.pluginYmlContents.assertSoftly { + it["description"] shouldBe "test description" + it["load"] shouldBe "startup" + it["author"] shouldBe "kyle" + it["authors"] shouldBe listOf("kyle1", "kyle2") + it["website"] shouldBe "github" + it["database"] shouldBe true + it["prefix"] shouldBe "prefix" + it["loadbefore"] shouldBe listOf("other") } } "should write dependency tags" - { - val folder = createTempDir() - val buildFile = folder / "build.gradle.kts" - writeBaseGradleConfig(buildFile) - buildFile += """ - |repositories { - | mavenCentral() - | mavenLocal() - |} - |dependencies { - | implementation("in.kyle.mcspring:mcspring-base:+") - |} - """.trimMargin() + val gradle = GradleContext.setup() - val srcFile = folder / "src" / "main" / "kotlin" / "Example.kt" - srcFile += """ + (gradle.kotlinSourceFolder / "Example.kt") += """ |import `in`.kyle.mcspring.annotation.* |@PluginDepend("plugin") |@SoftPluginDepend("plugin2") |class Example """.trimMargin() - val result = runGradle(folder, "buildPluginYml") - val task = result.task(":buildPluginYml")!! + val task = gradle.runTask("buildPluginYml") task.outcome shouldBe TaskOutcome.SUCCESS - val pluginYml = folder / "build" / "resources" / "main" / "plugin.yml" - val yml = Yaml().load>(pluginYml.inputStream()) - - assertSoftly { - yml["depend"] shouldBe listOf("plugin") - yml["softdepend"] shouldBe listOf("plugin2") + gradle.pluginYmlContents.assertSoftly { + it["depend"] shouldBe listOf("plugin") + it["softdepend"] shouldBe listOf("plugin2") } } "should write commands" - { - val folder = createTempDir() - val buildFile = folder / "build.gradle.kts" - writeBaseGradleConfig(buildFile) - buildFile += """ - |repositories { - | mavenCentral() - | mavenLocal() - |} - |dependencies { - | implementation(kotlin("stdlib")) - | implementation("in.kyle.mcspring:mcspring-base:+") - | implementation("in.kyle.mcspring:mcspring-commands-dsl:+") - |} - """.trimMargin() + val gradle = GradleContext.setup() - val srcFile = folder / "src" / "main" / "kotlin" / "Command.kt" - srcFile += """ + (gradle.kotlinSourceFolder / "Command.kt") += """ |import `in`.kyle.mcspring.commands.dsl.mcspring.Command |@Command( | value = "test", @@ -138,14 +92,10 @@ class TestBuildPluginYml : FreeSpec({ |fun test() { } """.trimMargin() - val result = runGradle(folder, "buildPluginYml") - val task = result.task(":buildPluginYml")!! + val task = gradle.runTask("buildPluginYml") task.outcome shouldBe TaskOutcome.SUCCESS - val pluginYml = folder / "build" / "resources" / "main" / "plugin.yml" - val yml = Yaml().load>(pluginYml.inputStream()) - - yml["commands"] shouldBe mapOf( + gradle.pluginYmlContents["commands"] shouldBe mapOf( "test" to mapOf( "description" to "a test command", "aliases" to listOf("t"), @@ -157,35 +107,17 @@ class TestBuildPluginYml : FreeSpec({ } "should add minimal info to commands" - { - val folder = createTempDir() - val buildFile = folder / "build.gradle.kts" - writeBaseGradleConfig(buildFile) - buildFile += """ - |repositories { - | mavenCentral() - | mavenLocal() - |} - |dependencies { - | implementation(kotlin("stdlib")) - | implementation("in.kyle.mcspring:mcspring-base:+") - | implementation("in.kyle.mcspring:mcspring-commands-dsl:+") - |} - """.trimMargin() + val gradle = GradleContext.setup() - val srcFile = folder / "src" / "main" / "kotlin" / "Command.kt" - srcFile += """ + (gradle.kotlinSourceFolder / "Command.kt") += """ |import `in`.kyle.mcspring.commands.dsl.mcspring.Command |@Command("test") |fun test() { } """.trimMargin() - val result = runGradle(folder, "buildPluginYml") - val task = result.task(":buildPluginYml")!! + val task = gradle.runTask("buildPluginYml") task.outcome shouldBe TaskOutcome.SUCCESS - val pluginYml = folder / "build" / "resources" / "main" / "plugin.yml" - val yml = Yaml().load>(pluginYml.inputStream()) - - yml["commands"] shouldBe mapOf("test" to emptyMap()) + gradle.pluginYmlContents["commands"] shouldBe mapOf("test" to emptyMap()) } }) diff --git a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestDownloadJar.kt b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestDownloadJar.kt index f49d1d2..526962d 100644 --- a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestDownloadJar.kt +++ b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestDownloadJar.kt @@ -1,8 +1,8 @@ package `in`.kyle.mcspring.tasks +import `in`.kyle.mcspring.GradleContext import `in`.kyle.mcspring.div import `in`.kyle.mcspring.runGradle -import `in`.kyle.mcspring.writeBaseGradleConfig import io.kotest.core.spec.style.FreeSpec import io.kotest.matchers.file.shouldNotBeEmpty import io.kotest.matchers.shouldBe @@ -11,15 +11,12 @@ import org.gradle.testkit.runner.TaskOutcome class TestDownloadJar : FreeSpec({ "should download the Spigot jar" - { - val folder = createTempDir() - val buildFile = folder / "build.gradle.kts" - writeBaseGradleConfig(buildFile) + val gradle = GradleContext.setup() - val result = runGradle(folder, "downloadJar") - val task = result.task(":downloadJar")!! + val task = gradle.runTask("downloadJar") task.outcome shouldBe TaskOutcome.SUCCESS - val spigot = folder / "spigot" / "spigot.jar" + val spigot = gradle.spigotFolder / "spigot.jar" spigot.shouldNotBeEmpty() } }) diff --git a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestSetupSpigot.kt b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestSetupSpigot.kt index cb034c4..deaefab 100644 --- a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestSetupSpigot.kt +++ b/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestSetupSpigot.kt @@ -1,9 +1,7 @@ package `in`.kyle.mcspring.tasks +import `in`.kyle.mcspring.GradleContext import `in`.kyle.mcspring.div -import `in`.kyle.mcspring.plusAssign -import `in`.kyle.mcspring.runGradle -import `in`.kyle.mcspring.writeBaseGradleConfig import io.kotest.assertions.assertSoftly import io.kotest.core.spec.style.FreeSpec import io.kotest.matchers.file.shouldNotBeEmpty @@ -13,18 +11,15 @@ import org.gradle.testkit.runner.TaskOutcome class TestSetupSpigot : FreeSpec({ "setup spigot should make required files" - { - val folder = createTempDir() - val buildFile = folder / "build.gradle.kts" - writeBaseGradleConfig(buildFile) - val spigotJar = folder / "spigot" / "spigot.jar" + val gradle = GradleContext.setup() + val spigotJar = gradle.spigotFolder / "spigot.jar" spigotJar.parentFile.mkdirs() spigotJar.createNewFile() - val result = runGradle(folder, "setupSpigot") - val task = result.task(":setupSpigot")!! + val task = gradle.runTask("setupSpigot") task.outcome shouldBe TaskOutcome.SUCCESS - val spigot = folder/"spigot" + val spigot = gradle.spigotFolder assertSoftly { (spigot / "bukkit.yml").shouldNotBeEmpty() (spigot / "eula.txt").shouldNotBeEmpty() diff --git a/mcspring-build/mcspring-plugin-manager/build.gradle.kts b/mcspring-build/mcspring-plugin-manager/build.gradle.kts deleted file mode 100644 index d5203f1..0000000 --- a/mcspring-build/mcspring-plugin-manager/build.gradle.kts +++ /dev/null @@ -1,17 +0,0 @@ -plugins { - id("in.kyle.mcspring") -} - -dependencies { - implementation(project(":mcspring-api:mcspring-base")) - implementation(project(":mcspring-api:mcspring-commands-dsl")) -} - -mcspring { - pluginAuthor = "kylepls" -} - -tasks.build { - dependsOn(tasks.named("buildServer")) -} - diff --git a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/McSpringPluginManager.kt b/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/McSpringPluginManager.kt deleted file mode 100644 index eabc82a..0000000 --- a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/McSpringPluginManager.kt +++ /dev/null @@ -1,8 +0,0 @@ -package `in`.kyle.mcspring.manager - -import `in`.kyle.mcspring.annotation.SpringPlugin - -@SpringPlugin -open class McSpringPluginManager { - -} diff --git a/mcspring-examples/build.gradle.kts b/mcspring-examples/build.gradle.kts new file mode 100644 index 0000000..b9ec3a7 --- /dev/null +++ b/mcspring-examples/build.gradle.kts @@ -0,0 +1,22 @@ +plugins { + kotlin("jvm") version "1.3.72" + id("in.kyle.mcspring") version "0.0.2" apply false +} + +subprojects { + apply(plugin = "org.jetbrains.kotlin.jvm") + apply(plugin = "in.kyle.mcspring") + + repositories { + mavenLocal() + mavenCentral() + maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") + maven("https://oss.sonatype.org/content/repositories/snapshots") + } + + dependencies { + val spigotVersion = "1.15.2-R0.1-SNAPSHOT" + compileOnly("org.spigotmc:spigot-api:$spigotVersion") + implementation(kotlin("stdlib")) + } +} diff --git a/mcspring-examples/gradle/wrapper/gradle-wrapper.jar b/mcspring-examples/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..f3d88b1c2faf2fc91d853cd5d4242b5547257070 GIT binary patch literal 58695 zcma&OV~}Oh(k5J8>Mq;vvTfV8ZQE5{wr$(iDciPf+tV}m-if*I+;_h3N1nY;M6TF7 zBc7A_WUgl&IY|&uNFbnJzkq;%`2QLZ5b*!{1OkHidzBVe;-?mu5upVElKVGD>pC88 zzP}E3wRHBgaO?2nzdZ5pL;m-xf&RU>buj(E-s=DK zf%>P9se`_emGS@673tqyT^;o8?2H}$uO&&u^TlmHfPgSSfPiTK^AZ7DTPH`Szw4#- z&21E&^c|dx9f;^@46XDX9itS+ZRYuqx#wG*>5Bs&gxwSQbj8grds#xkl;ikls1%(2 zR-`Tn(#9}E_aQ!zu~_iyc0gXp2I`O?erY?=JK{M`Ew(*RP3vy^0=b2E0^PSZgm(P6 z+U<&w#)I=>0z=IC4 zh4Q;eq94OGttUh7AGWu7m){;^Qk*5F6eTn+Ky$x>9Ntl~n0KDzFmB0lBI6?o!({iX zQt=|-9TPjAmCP!eA{r|^71cIvI(1#UCSzPw(L2>8OG0O_RQeJ{{MG)tLQ*aSX{AMS zP-;|nj+9{J&c9UV5Ww|#OE*Ah6?9WaR?B04N|#`m0G-IqwdN~Z{8)!$@UsK>l9H81 z?z`Z@`dWZEvuABvItgYLk-FA(u-$4mfW@2(Eh(9fe`5?WUda#wQa54 z3dXE&-*@lsrR~U#4NqkGM7Yu4#pfGqAmxmGr&Ep?&MwQ9?Z*twtODbi;vK|nQ~d_N z;T5Gtj_HZKu&oTfqQ~i`K!L||U1U=EfW@FzKSx!_`brOs#}9d(!Cu>cN51(FstP_2dJh>IHldL~vIwjZChS-*KcKk5Gz zyoiecAu;ImgF&DPrY6!68)9CM-S8*T5$damK&KdK4S6yg#i9%YBH>Yuw0f280eAv3 za@9e0+I>F}6&QZE5*T8$5__$L>39+GL+Q(}j71dS!_w%B5BdDS56%xX1~(pKYRjT; zbVy6V@Go&vbd_OzK^&!o{)$xIfnHbMJZMOo``vQfBpg7dzc^+&gfh7_=oxk5n(SO3 zr$pV6O0%ZXyK~yn++5#x`M^HzFb3N>Vb-4J%(TAy#3qjo2RzzD*|8Y} z7fEdoY5x9b3idE~-!45v?HQ$IQWc(c>@OZ>p*o&Om#YU904cMNGuEfV=7=&sEBWEO z0*!=GVSv0>d^i9z7Sg{z#So+GM2TEu7$KXJ6>)Bor8P5J(xrxgx+fTLn1?Jlotz*U z(ekS*a2*ml5ft&R;h3Gc2ndTElB!bdMa>UptgIl{pA+&b+z_Y&aS7SWUlwJf-+PRv z$#v|!SP92+41^ppe}~aariwztUtwKA8BBLa5=?j3@~qHfjxkvID8CD`t5*+4s|u4T zLJ9iEfhO4YuAl$)?VsWcln|?(P=CA|!u}ab3c3fL8ej9fW;K|@3-c@y4I;^8?K!i0 zS(5Cm#i85BGZov}qp+<-5!Fh+KZev3(sA2D_4Z~ZLmB5B$_Yw2aY{kA$zuzggbD{T zE>#yd3ilpjM4F^dmfW#p#*;@RgBg{!_3b6cW?^iYcP!mjj!}pkNi{2da-ZCD2TKKz zH^x^+YgBb=dtg@_(Cy33D|#IZ&8t?w8$E8P0fmX#GIzq~w51uYmFs{aY76e0_~z2M z(o%PNTIipeOIq(H5O>OJ*v8KZE>U@kw5(LkumNrY>Rv7BlW7{_R9v@N63rK)*tu|S zKzq|aNs@81YUVZ5vm>+pc42CDPwQa>oxrsXkRdowWP!w?=M(fn3y6frEV*;WwfUV$s31D!S_;_~E@MEZ>|~wmIr05#z2J+& zBme6rnxfCp&kP@sP)NwG>!#WqzG>KN7VC~Gdg493So%%-P%Rk!<|~-U|L3VASMj9K zk(Pfm1oj~>$A>MFFdAC8M&X0i9-cV7Q($(R5C&nR5RH$T&7M=pCDl`MpAHPOha!4r zQnYz$7B1iLK$>_Ai%kZQaj-9)nH$)tESWUSDGs2|7plF4cq1Oj-U|+l4Ga}>k!efC z*ecEudbliG+%wI8J#qI!s@t%0y9R$MBUFB)4d47VmI`FjtzNd_xit&l1T@drx z&4>Aj<2{1gUW8&EihwT1mZeliwrCN{R|4@w4@@Btov?x5ZVzrs&gF0n4jGSE33ddUnBg_nO4Zw)yB$J-{@a8 z);m%fvX2fvXxogriNb}}A8HxA)1P-oK+Da4C3pofK3>U_6%DsXFpPX}3F8O`uIpLn zdKjq(QxJTJ4xh->(=lxWO#^XAa~<7UxQl8~8=izS!TcPmAiBP5Et7y?qEbFd9Q=%IJ;%Kn$lto-~3`}&`x=AVS+Uo7N*hbUxhqVH_w^sn!74z{Ka#*U6s z=8jIrHpUMBC@@9Jn~GS<$lse*EKuX%3Swl5&3~GiK_$vn8Vjqe{mjhBlH}m4I8qK+ ztU50COh7)d-gXpq-|}T;biGa^e=VjxjjFuoGIA8`2jJ}wNBRcsx24?7lJ7W4ksNPv zA7|gcXT@~7KTID#0|EX#OAXvgaBJ8Jg!7X#kc1^Tvl;I(=~(jtn-(5bhB=~J^w5bw z8^Hifeupm;nwsSDkT{?x?E(DgLC~Nh8HKQGv`~2jMYrz9PwS^8qs3@nz4ZBCP5}%i z=w}jr2*$X-f(zDhu%D8(hWCpix>TQpi{e`-{p^y?x4?9%)^wWc?L}UMcfp~lL|;g) zmtkcXGi9#?cFOQQi_!Z8b;4R%4y{$SN~fkFedDJ&3eBfHg|DRSx09!tjoDHgD510Z z_aJLHdS&7;Dl;X|WBVyl_+d+2_MK07^X1JEi_)v$Z*ny-()VrD6VWx|Un{)gO0*FQ zX{8Ss3JMrV15zXyfCTsVO@hs49m&mN(QMdL3&x@uQqOyh2gnGJYocz0G=?BX7qxA{ zXe0bn4ij^;wfZfnRlIYkWS^usYI@goI9PccI>}Ih*B!%zv6P$DoXsS%?G)|HHevkG z>`b#vtP=Lx$Ee(t??%_+jh(nuc0Q&mCU{E3U z1NqNK!XOE#H2Pybjg0_tYz^bzX`^RR{F2ML^+<8Q{a;t(#&af8@c6K2y2m zP|parK=qf`I`#YxwL=NTP>tMiLR(d|<#gEu=L-c!r&(+CpSMB5ChYW1pUmTVdCWw|!Ao?j&-*~50S`=) z9#Knf7GPA19g%Y7wip@`nj$aJcV|SakXZ*Q2k$_SZlNMx!eY8exF;navr&R)?NO9k z#V&~KLZ0c9m|Mf4Gic}+<=w9YPlY@|Pw*z?70dwOtb<9-(0GOg>{sZaMkZc9DVk0r zKt%g5B1-8xj$Z)>tWK-Gl4{%XF55_Ra3}pSY<@Y&9mw`1jW8|&Zm{BmHt^g=FlE{` z9Lu7fI2v3_0u~apyA;wa|S4NaaG>eHEw&3lNFVd_R9E=Y? zgpVQxc9{drFt2pP#ZiN~(PL%9daP4pWd*5ABZYK{a@e&Vb`TYiLt$1S>KceK36Ehz z;;MI%V;I`#VoSVAgK3I%-c>ViA>nt=5EZ zjr$Jv~$_vg<$q<@CpZ1gdqP_3v^)uaqZ`?RS_>f(pWx3(H;gWpjR?W8L++YPW;)Vw3)~tozdySrB3A2;O<%1F8?Il4G|rO0mEZYHDz!?ke!$^bEiWRC1B%j~ws0+hHS;B8l5Wh)e+Ms7f4M4CbL%Q_*i~cP}5-B(UkE&f7*pW6OtYk5okQCEoN4v|7;(+~~nyViqo5 z(bMGQi$)KN6EmfVHv4pf2zZMJbcAKyYy>jY@>LB5eId|2Vsp{>NMlsee-tmh({;@b z@g;wiv8@a1qrDf-@7$(MR^M^*dKYBewhIDFX%;*8s zR#u?E;DJO;VnTY6IfbO=dQ61V0DisUAs4~t|9`9ZE(jG}ax#-xikDhsO_4^RaK ziZ?9AJQP_{9WuzVk^s_U+3V8gOvVl5(#1>}a|RL>};+uJB%nQM-J>M4~yK)cioytFXtnmOaJZSiE+3g}C`Im~6H z*+-vjI>ng5w>>Y!L(+DwX2gs0!&-BFEaDie4i5ln*NGP$te7$F9iUlJl4`XpkAsPm z0l?GQ17uN^=g~u1*$)S`30xL%!`LW*flwT*#svAtY(kHXFfvA`dj*pDfr0pBZ`!La zWmX$Z@qyv|{nNsRS|+CzN-Pvb>47HEDeUGFhpp5C_NL0Vp~{Wc{bsm_5J!#tuqW@? z)Be zb&Gj&(l*bHQDq7w-b`F9MHEH*{Dh~0`Gn8t`pz}!R+q~4u$T@cVaUu`E^%0f-q*hM z1To6V31UGJN7a-QW5;nhk#C26vmHyjTVZkdV zqYMI9jQY)3oZt=V0L7JZQ=^c2k){Y_lHp&V_LIi*iX^Ih3vZ_K<@Di(hY<&g^f?c$wwF-wX1VLj>ZC4{0#e`XhbL_$a9uXS zKph*4LupSV2TQBCJ4AfOXD8fs2;bAGz-qU4=Qj$^1ZJX z2TtaVdq>OjaWGvv9)agwV)QW9eTZ-xv`us2!yXSARnD5DwX_Vg*@g4w!-zT|5<}-7 zsnllGRQz>k!LwdU`|i&!Bw^W7CTUU3x`Zg8>XgHj=bo!cd<#pI8*pa*1N`gg~I0ace!wzZoJ)oGScm~D_Sc;#wFed zUo;-*0LaWVCC2yqr6IbeW3`hvXyMfAH94qP2|cN``Z%dSuz8HcQ!WT0k38!X34<6l zHtMV%4fH5<6z-lYcK;CTvzzT6-^xSP>~a*8LfbByHyp$|X*#I6HCAi){gCu1nvN%& zvlSbNFJRCc&8>f`$2Qa`fb@w!C11v1KCn)P9<}ei0}g*cl~9A9h=7(}FO!=cVllq3 z7nD)E%gt;&AYdo{Ljb2~Fm5jy{I><%i*GUlU8crR4k(zwQf#nima@xb%O71M#t-4< z(yjX(m^mp_Y;5()naqt2-VibylPS)Oof9uBp$3Gj`>7@gjKwnwRCc>rx%$esn);gI z5B9;~uz57n7Rpm8K^o=_sFPyU?>liHM&8&#O%f)}C5F7gvj#n#TLp@!M~Q?iW~lS}(gy%d&G3p?iBP z(PZQUv07@7!o3~1_l|m5m;Xr)^QK_JaVAY3v1UREC*6>v;AT$BO`nA~KZa1x3kV2F z%iwG7SaaAcT8kalCa^Hg&|eINWmBQA_d8$}B+-Q_@6j_{>a- zwT3CMWG!A}Ef$EvQsjK>o)lJ;q!~#F%wo`k-_mT=+yo%6+`iGe9(XeUl;*-4(`G;M zc@+ep^Xv&<3e7l4wt48iwaLIC1RhSsYrf6>7zXfVD zNNJ1#zM;CjKgfqCabzacX7#oEN{koCnq1-stV+-CMQ=ZX7Fpd*n9`+AEg9=p&q7mTAKXvcbo?$AVvOOp{F>#a;S?joYZl_f}BECS%u&0x!95DR;|QkR9i}`FEAsPb=)I z8nb=4iwjiLRgAF}8WTwAb^eA>QjL4Srqb#n zTwx^-*Z38Uzh@bX$_1tq>m{o8PBX*t3Lqaf$EBqiOU*2NFp{LJX#3}p9{|v{^Hg4f zlhllKI>F+>*%mu6i9V7TT*Wx-zdK z(p8faUOwGOm5mBC%UGA1jO0@IKkG;i&+6Ur8XR2ZuRb$*a}R^-H6eKxcYodlXsF`& z{NkO+;_Yh-Ni@vV9iyzM43Yibn;oC7hPAzC24zs&+RYdY&r`3&&fg2hs62ysV^G`N zHMfBEFo8E3S$0C_m({bL8QCe$B@M{n1dLsaJYIU;(!n*V?0I1OvBB=iYh&`?u8 z&~n-$nbVIhO3mMhCQRlq%XRr1;Hvl=9E_F0sc9!VLnM>@mY~=Cx3K5}wxHKEZF9pC zIdyu1qucM!gEiomw7bW0-RwbX7?o=FE#K0l4`U2KhC8*kMWaEWJyVNZVu_tY2e&4F zb54Lh=Oz>(3?V$!ArXFXh8Cb3i;%KQGCrW$W#;kvx$YA2gofNeu?@nt>Yq8?2uJQp zUTo14hS%&dHF3Uhm~Z1>W)yb%&HoM!3z?%a%dmKT#>}}kKy2B=V3{Nu=bae%V%wU$ zb4%^m?&qn==QeHo`nAs3H}wtiK~!!&i|iBLfazh6!y9F)ToKNyE0B385!zq{p)5vB zvu`R#ULIS|2{3w52c*c$4}Pe>9Fw&U^>Bb_LUWn!xPx3X-uQsv(b1XFvFzn#voq0* z5~o`V_G805QXdgAOwOjoqmZ?uzwBVYSNP0Ie8FL`P0VK1J4CzV@t&%0duHB{;yIL$FZ9 zz#s#%ZG6ya&AwE;0_~^$1K

Hnj76Oym1QVh(3qRgs)GmgnEt-KxP|nCFY3uezZn zmtR0CZ$Z_-+f07?lu_tr~IC{&U6+QOth>ZgYk4V2FI$B2V3`M`Jk zsr>>lupymPeK129PfpDt9?GA2;I>03Ktz8NxwvTroqu8oaRB&bXT}G=^2UyOW}(4H z;9sG^YwV8K7pC&&viM^X_pfeFoN!cIhrE>OPQ5E<4KKDyPhRV^BGb_^Y6GO6#w}c= zu`0fC-@F4qXQtnB^nPmfI7Uw0bLhY^09TCO+H2(nvg8jdPjMAi4oSX%GP3oeo0`ks z%DoV|waU-Q7_libJCwnnOL9~LoapKqFPpZx?5FygX zsA~*ZR7X=@i{smf?fgxbcY6Y`JvD50P=R;Xv^sANPRp-Hc8n~Wb*gLIaoZJ2Q^CFe z_=G}y&{_NXT|Ob??}$cF7)$oPQMaeN_va1f%>C>V2E01uDU=h~<_fQKjtnl_aho2i zmI|R9jrNdhtl+q*X@}>l08Izz&UJygYkbsqu?4OOclV{GI5h98vfszu2QPiF?{Tvh19u_-C^+NjdAq!tq&Rd`ejXw#` z@U15c$Nmylco)Yj4kctX{L+lz$&CqTT5~}Q>0r-Xe!m5+?du6R&XY|YD5r5C-k*`s zOq-NOg%}RJr5ZWV4)?EO%XzZg&e8qVFQ?40r=8BI-~L%9T7@_{1X@<7RjboXqMzsV z8FiSINMjV*vC^FCv_;`jdJ-{U1<_xjZg4g?ek z4FtsapW_vFGqiGcGHP%?8US~Dfqi8^ZqtHx!}0%dqZFg%nQB)8`mE$~;1)Fb76nFk z@rK#&>2@@)4vO&gb{9&~R8-_{8qz6Rmw`4zeckD(L9xq}{r(fUO0Zh-R(d#x{<0j| z?6xZ2sp3mWnC}40B~g2QinHs1CZqZH&`+x2yBLT8hF7oWNIs_#YK2cyHO6AoGRG|RM>Hyn(ddpXFPAOGh~^0zcat`%&WoEQf9)!@l*3Tt@m>Lb z6$+$c!zsy_=%L9!_;jfd`?VXDd*^Vn%G>n~V9Vr6+_D@#E+dWB#&zAE+6xJeDMr1j zV+Tp~ht!M%^6f?)LBf8U1O4G#CutR07SB>8C&_&;g3TdIR#~e~qRtwd>&)|-ztJJ#4y0|UMjhJZlS8gA zAA260zUh+!$+xMfWKs|Lr23bcy#)JNnY|?WOka&wTS7_u%*N7PrMl1Lp9gxJY%CF? zz4IA@VVxX{knZPlNF+$9)>YIj#+(|$aflt=Wnforgn6`^3T+vaMmbshBjDi&tR(a7 zky~xCa77poRXPPam)@_UCwPdha^X~Aum=c0I@yTyD&Z!3pkA7LKr%Y6g%;~0<`{2& zS7W$AY$Kd}3Tg9CJgx=_gKR59zTMROsos?PU6&ocyCwCs8Qx1R%2#!&5c%~B+APu( z<1EXfahbm{XtOBK%@2a3&!cJ6R^g|2iLIN1)C2|l=;uj%tgSHoq2ojec6_4@6b<8BYG1h-Pm_V6dkRB!{T?jwVIIj&;~b7#%5Ew=0Fx zc(p7D1TT&e=hVt4spli}{J6tJ^}WL>sb`k}&gz+6It`Yz6dZdI53%$TR6!kSK2CfT*Q$`P30 z;$+G$D*C$U(^kkeY!OWn$j@IUu0_a{bZQ=TCbHD1EtmZ0-IBR<_3=tT%cz$>EE!V}pvfn7EMWs^971+XK}~kxSc_ATJJD$?)1Gz^Jq!>Hz#KkdCJ~jb-Y*Xv01_}}=T_V-A1<3O!V9Ezf z%Lnjihb3>=ZV}jSeqNu5AAdVbe|`;|p<%W#-<$s1oDYrB;C({psqV>ENkhadsC{cfEx=teVSB`?FOs+}d#pssxP z(ihudAVu3%%!*vOIWY11fn1M0&W|(|<2lEShz|#%W|wV2qM%#+P9NOy1x8jytHpfU zh;_L^uiL<<$L@~NpRXSrkJgdC>9R=>FmVu3^#C?3H>P{ue=mcv7lBmnfA?mB|L)EF zHv%Nl|D}0Tb~JVnv$ZysvbD8zw)>|5NpW3foe!QHipV9>Zy`|<5?O+rsBr*nZ4OE} zUytv%Rw7>^moSMsSU?@&a9+OdVgzWZnD>QXcUd{dd7vad+=0Hy)4|0A`}rpCx6cu!Ee5AM=iJ?|6=pG^>q(ExotyZP3(2PGhgg6-FkkQHS?nHX(yU0NG;4foCV|&)7 z1YK!bnv%#5n<25|CZ>4r1nK=D39qMzLAja*^#CN(aBbMx${?Iur3t=g2EMK|KwOF?I@W~0y`al&TGqJ zwf#~(?!>@#|JbDjQV9ct%+51l%q|lcY&f{FV&ACRVW*%VY6G5DzTpC!e%=T30mvav zRk$JOTntNoxRv>PDlJG1X=uep&???K00ep|l_#7=YZPuRHYoM46Z$O=ZZuGy_njgC z>P@gd+zKH5SjpWQ!h_r*!ol1s{9DS@sD4}xgFxaw>|av!xrKzg?rGnhZ#uZeU~iod z3-i*Hl@7cge0);y{DCVU(Ni1zg{yE&CxYT7)@zJ%ZZABj-Fh}0au^)*aw`vpmym;( z5|JZ!EACYenKNXH%=Md{my$sI3!8^FgtqkMcUR%w_)EBdP5DZ64aCIR%K99tId6SU ziT8Ef)K%7{XuIpPi}N+&FCm$elE>oKY;3c$x+*mXy?~wt6~?ss$HGqCm=YL2xzVTQ zr>*2_F;7j{5}NUPQ(aY0+h~rOKN|IA28L7^4XjX!L0C^vFB+3R5*1+s@k7;4d#U=5 zXTy8JN^_BCx1a4O3HMa9rf@?Fz>>dq}uvkY7!c?oksgs~xrpCo1{}^PD?w}Ug z3MbfBtRi z$ze~eRSLW^6bDJJeAt^5El{T*i1*v9wX{T7`a2wAVA z%j>3m*g^lc*~GOHFNy?h7>f7mPU*)3J>yPosaGkok}2#?wX5d$9moM~{NTzLznVhX zKa}bFQt#De`atoWzj4Lb@ZCud_T9rA@6VcmvW(+X?oIaH-FDbEg#0Slwf|7f!zUO( z7EUzpBOODL&w~(tNt0z|<9}Filev&4y;SQPp+?kIvJgnpc!^eYmsWz1)^n`LmP&Ui z-Oi1J2&O|$I<^V@g2Z91l3OArSbCkYAD0Tuw-O(INJJ>t%`DfIj}6%zmO+=-L{b!P zLRKvZHBT=^`60YuZon~D$;8UDlb-5l8J=1erf$H(r~ryWFN)+yY@a;=CjeUGNmexR zN)@)xaHmyp$SJcl>9)buKst5_+XomJu34&QMyS zQR(N@C$@%EmfWB8dFN(@Z%xmRma@>QU}!{3=E`wrRCQ~W=Dwb}*CW8KxAJ;v@TAs3 zW}Pq5JPc)(C8Rths1LR}Bgcf6dPOX<#X08^QHkznM-S>6YF(siF;pf~!@)O{KR4q1_c`T9gxSEf`_;a-=bg6=8W zQ&t`BK^gsK-E0Jp{^gW&8F9k?L4<#}Y0icYT2r+Dvg!bnY;lNNCj_3=N=yd9cM9kY zLFg|R0X;NRMY%zD*DbAmFV`(V@IANtz4^_32CH*)XCc$A>P-v49$k@!o$8%Ug>3-- z$#Fpo9J>eUMKg>Cn+T0H!n0Hf#avZX4pp54cv}YcutP+CmKC~a745-zhZp`KNms;J zS3S49WEyS8gCRAY|B~6yDh*cehY52jOSA#MZmk2dzu`_XpBXx9jDf!H3~!`n zaGe=)1VkfIz?*$T3t>-Pwhrw447idZxrsi;ks;(NF>uVl12}zI(N~2Gxi)8yDv-TLgbZ;L&{ax&TBv;m@z6RcbakF^el{!&)<___n#_|XR%jedxzfXG!a2Eyi)4g zYAWkYK{bQzhm|=>4+*SLTG2<#7g-{oB48b05=?PeW;Jo3ebWlo5y5|cl?p8)~PVZqiT^A~w-V*st8kV%%Et1(}x(mE0br-#hyPspVehofF`{gjFXla1lrqXJqQKE9M)8Xe0ZO&s$}Q zBTPjH>N!UU%bRFqaX(O9KMoG$Zy|xt-kCDjz(E*VDaI={%q? zURR{qi>G^wNteX|?&ZfhK-93KZlPXmGMsPd1o?*f_ej~TkoQ#no}~&#{O=>RadgtR zvig@~IZMsm3)vOr`>TGKD&fbRoB*0xhK7|R?Jh-NzkmR}H6lJiAZTIM1#AXE1LOGx zm7j;4b(Lu6d6GwtnsCvImB8%KJD+8z?W{_bDEB$ulcKP*v;c z*Ymsd)aP+t$dAfC-XnbwDx3HXKrB{91~O}OBx)fsb{s-qXkY<@QK7p-q-aaX&F?GS z2};`CqoNJ$<0DuM2!NCbtIpJ9*1a8?PH#bnF#xf~AYOIc4dx1Bw@K=)9bRX;ehYs; z$_=Ro(1!iIM=kZDlHFB>Ef46#rUwLM%)(#oAG(gYp>0tc##V{#aBl!q``!iIe1GBn z+6^G^5)(nr z8h#bm1ZzI450T?!EL)>RWX8VwT1X`2f;dW!{b~S>#$Pa~D6#Hp!;85XzluH%v5325 z730-aW?rY1!EAt;j7d23qfbMEyRZqxP};uID8xmG@mGw~3#2T^B~~14K5?&dP&H@r zL|aXJsEcAAXEXfu2d-!otZTV=if~^EQD*!NkUFQaheV&b-?-zH6JfjKO)aYN=Do*5 zYZ-@m#)5U0c&sUqu_%-Editr5#%Ne&bs)DxOj2_}`f;I_ReEY9U&Cf3rb>A3LK(ZD zid0_-3RfsS*t&g!zw}C_9u(_ze-vc1L59CdBl(IS^yrvsksfvjXfm>(lcol%L3))Q z@ZT;aumO3Q#8R!-)U697NBM@11jQ>lWBPs#?M4_(w=V_73rsiZh8awEm>q1phn1Ks ze@D|zskeome3uilE8-dgG(EojlI(@Yhfm}Xh_AgueHV`SL##I@?VR+bEHH=sh21A_ zhs&pIN7YTLcmJiyf4lZ;`?pN0`8@QbzDpmT`$m0CTrTMiCq%dE&Cd_{-h`I~f8Kps zAuZt4z)}@T>w$9V@iLi=mh({yiCl}}d>JN)z;*G<6&mgl(CYhJHCAPl=PYK2D>*F zy;YK=xS@1JW7i=C)T04(2P#|fowalY=`Y`G8?eRMAKt|ddG9UF^0M5 zW=ZGZ5qb-z@}iS`4RKXvuPIfzUHT)rv<8a|b?bgB3n=ziCiX4m2~CdVBKHWxw2+Hz zLvqoAij9(0moKoo2$`dqS0?5-(?^RXfcsQB6hU2SAgq8wyeasuyFGcK+@An?8ZzVw zW8wwbZB@i=<<4fA7JKPkki6y>>qO3_bW>-uQ*>9g+g7M0U^`RV)YTrGu2Q=2K>fiI zY0dFs>+}xuOZE^efLK2K6&X@>+y10Oqejnnq^NjfXt9JpK4K_E=cl29 z(t2P;kl4AK_Jg9v{1(z)ESpyo_(Z`74D&J1A#J?l5&J^Ad1sm5;Po@s9v7wOs(=_T zkutjt`BaxT09G{-r>yzyKLlM(k`GZl5m+Tgvq=IN|VjtJ*Zu66@#Rw;qdfZqi15A@fr^vz?071F5!T`s>Lx5!TszI%UK|7dDU;rUCwrRcLh!TZZ9$UMfo z@Qzjw>tKS3&-pyWS^p4mMtx`AvwxVc?g?#8aj@jQ#YKDG0aCx{pU+36?ctAiz=f$k z05S(b&VPQgA(Sm`oP&M^eiHvBe&PcTb+j$!!Yx(j3iI5zcQLOn(QqfX5OElbSsQBUw7);5C92onieJyx`p{V!iwXk)+1v zA6vStRZo0hc>m5yz-pkby#9`iG5+qJ{x>6I@qeAK zSBFylj8{FU*0YbFd2FZ6zdt^2p?V;3F~kap`UQgf@}c33+6xP)hK)fmDo@mm=`47* z9S6rnwCSL&aqgZs959!lhEZZp`*>V8ifNmL;cqajMuaJ~t`;jLPB?X~Ylk_Z#Q;%} zV+sAJ=4505-DdnIR=@D_a`Gy#RxtSX+i-zInO@LVDOd*p>M-|X(qRrZ3S(>(=Oj>} z89d75&n?m^j>;SOXM=)vNoum|3YmzxjYx%^AU*V|5v@SjBYtESp^yz?eQ#>5pnCj} zJ_WCw23wGd2AA-iBve8Hq8`%B3K4@9q@a}sf$49IA^IPsX@QK)36mrzqOv?R_n9K@ zw3=^_m#j{gNR0;&+F~wlS(i8IQN8mIvIO)mkx|e)u*y+xDie}%mkZ*m)BQM^$R@-g z1FrP0{8A?EcxtxxxX&J;393ljwwG?2A2?y-1M0-tw$?5ssoEsbPi?sd2!s~TrwPLF zYo-5XYV7AU-c|Vb-v;>pVi^CwX(Rpt<9{Ic?@<9SrNu>F(gwij%?dC9^!Xo90o1-| z&_aPKo%+xyw64e&v<}F^-7sO0Cz-VOF@7**i@v&(Oy4Q8PbV+4&rKwmYyokM z48OZ|^%*mC_Q)RJ31D#b4o4Jzr{~BX4D#swW<31;qCil2qlim;e=9ymJAEXfv-|h3 z)>uqQ5~S+8IgiWW28Fqbq+@ukCLy+k7eGa1i5#G_tAUquw$FjFvQt6~kWa69KXvAj z-knF`5yWMEJvCbTX!K{L)VeNF?(+s?eNjtE5ivg^-#937-l()2nKr#cHShB&Pl^l8 zVYws26D^7nXPlm<_DYU{iDS>6Bq0@QsN%6n>XHVvP<^rDWscC!c+LFrK#)T@$%_0{ zob%f&oaq>1_Z8Ata@Y2K6n?GYg|l8SgUr(}hi4D!@KL~hjRv<}ZZ`tCD^ev=H&^0pP%6q2e+t=Ua`ag8xqWvNnIvCU|6ZA^L5v{DD)!mcQ@n6{=; z#Z)PrAz>*+h-|IV!&J*f@{xb!L7h3{?FEs*ifw5z2U9$&OkYseI68yb=V4xv*VK3- zVxGhtmedujX32y-kC{5ej-Wy#JvB~4oxTb{|1H825_B(A0#?CjUTc=PrGh6jAgK9h zoLAe`+NBdStZE@Y8UH^Rd*|R-|7Ke}wr$(CZQHhO+upHlCp)%n+fH_}S8%^%xqhu%20_1p=x#Dl9ia`c3iM+9Vh5?gyY8M9c$tJ5>}V_sidHN zoMl%rSgSK!7+Y8tQkYq|;Vh`4by2uMsUfnxkk2{S@a>V#d}fv}Yud*>paVi_~T zU!GoYwWbnG%92!Cte(zhZX-i9#KJ;b{$(aZs|{MerP#6||UUx$=y)4XOb zihyKn`_QhJ#~@_peJ*8yD4>I7wQyKkZG%#FTKZfb(@G+9x7-3@hG}+ZC&$7DwbaB$ zC)jLj7yituY&WpOWlG7Z4Tuxzdwo6k!3lgwhh7BYMyB? zO9Q5nvn77~g~c623b`Pe5efNzYD#2Sfmg>aMB5s?4NC|-0pIXy%%`J;+E{(irb!Szc8M8A@!}0zqJLoG4SJ5$~1*yRo0^Z`uObA+= zV?1sYNvzvWbP%AsMzoIo3Cwx~y%i8rHF(BgLS>tH5Ab|1wp$X_3o2_VB(pFxgQ5QQ zk@)Vy95$b%HVf4@ppX(wrv^Jwfrsu+9N_OUm}nD7Ch_7STj66EYsZR#`9k|Tf^@p& ziHwnO$p{TB#R(Q{Os>Un~0!r$JO zLZ&F%SP|%$TuG)mFeOhKr1?S!aa0jTV$2XIeZb_fgO&n{8HTe9s`L&(tKoy?OaS^$ zLHNrgYgq920EI~M>LyU7gK70$7*`nFKD^d>MoEAhsBU0%@*RW@%T(J z?+wVbz=mcN%4#7qlCpl_^Ay7VB%?+uW1WSNnQOj^tALyqTpV zkEN2C;qO_W)MYl^Ow5I;t3;z#iG82F(qe}#QeE;AjA=wM==dB(Gu+ez*5|RVxO4}l zt`o?*B;);-0`vR(#+Q^L4WH_9wklh-S-L-_zd%Q0LZ%|H5=>Z)-x#Z+m%p&6$2ScV zEBneIGo)r0oT)xjze*Q~AIqhB%lOM5Id}^eKwS!?b_;B&TouZsemyL&y`)#FX}ZKp zp)ZnB*^)1P@2bCoe+Z|#KhTBNrT)UN@WIuudw})fwHl)re1|b~E1F=xpH?7L77p>5 zei$aD@KO0<+zo1<&7OuZatNsPq24Whu%0jD_ z$ZZy6MzayYgTJulNEy8D$F%JDYgx|d6{6kpDg#s170<15bM#4tzvrDU$6bvu-hH@6 zgcjq&3aR3k(23$FaUA|iuoy*bO{2F6W0<+ZdsYvXjc?d@ZT8kM!GD}r@qr;TF@0Hb z2Dz-A!HZ$-qJ?F%w6_`t`8xk$f$MNBfjqwvJiVdD+pf7NVFGh?O=qp2vh%UcYvc{rFldib~rkIlo`seU%pO_6hmBWGMcUhsBSWiQYYPMX<-Cjp49@7U==iS57bG zw3T9Nbm`)m9<<4e$U74`t~zRo0JSfi}=GdQXGLLPyW zlT^I}y=t$j{Vx!wN^z8X4l0|@RNrC#)G>bK)7IT7Qop>YdS^NnI3gfP>vtp)pXkr2WSVcAAv8uN>@ z`6)kICvNYU$DA8pnkl4sQopDC6<_M8zGJ^@ANXJL(yd#n1XFj9pH;rld*gwY8om_I zdB55w@FUQ_2k}d%HtQsmUx_7Mzftky&o2X2yDQrgGcehmrDDDtUJj5``AX$gzEbMc zUj2Qzp)Lo>y-O*@HJ|g9$GR2-jgjKfB68J6OlIg;4F2@2?FlW zqj|lO7A2Ts-Kd!SO|r9XLbPt_B~pBpF40xcr0h=a&$bg(cwjp>v%d~Uk-7GUWom?1 z92p+C0~)Og*-N~daT#gQdG{&dPRZso(#{jGeDb1G`N)^nFSB`{2-UQ&!fkPyK`m03 z_Di94`{-(%3nE4}7;4MZ)Pmawf#{}lyTSs5f(r;r1Dp4<;27K=F}Oga^VsUs3*NIn zOsYstpqpRF&rq^9>m50LRORj>=;{CV2&#C$-{M5{oY9biBSoQyXvugVcwyT-19S;pf!`GSNqb4**TI%Y z*zyV)XN3Fdp3RNNr9FU+cV*tt?4L8>D@kJp^rkf_rJ~DPYL}oJngd1^l!4ITQN`0RTT^iq4xMg|S6;d}lznE$Ip^8pW-CHu zP*^!U>Lcd3*shqa)pswq;y<|ISM1g1RG#`|MSPNAsw*XH1IAD(e(Kgqp6aDHgv>fI z!P67$z{#()Pdo3;4dUoy*Xor(O?+YTRPe=g*FfRj*9q9!8p%1l>g3e^rQ_nm{(@4t z?^nMDC2J8@my5q0QyCljCSp_@)No+6bZ*y)lSdrkLFcR6YOHu*vZ-q(C);5$MmM_z z1WT>Gc8g%`Rt~6*!}JhWi0=Rc_z5c8GR9YXW+cdoK~Ea(@wyXf|89HagNuFAO-V7k zUb|9zaCCWH3^Fz(m7$8K$|0ZOP!SNpgP!ql<)!z8w$Z$?9gq2f<~koe3|zD=imLfD z>IV5?SkRZ;7JlOG%z%Tlze$GXr0A}ResyF63ZGZVDLv2k4HWtoqoCaq+Z&GaVKuLA z>@zhNjYYc=sexH?;DTe4&2vnQE}C@UFo&|qcLddvH0FwswdRUc(p*X&IT^Zu>xLpG zn(@C%3ig(l2ZPm#Fc){+0b+%O7nt4zbOt+3@GQVm|1t70=-U(>yo3VY2`FnXFHUyi zwiqf(akt0kEE5_Pa-a*VCS}Pi6?`~P%bvX6UT~r-tUAY%I4XF3^nC+tf3alyL{M`w zv?aVQ#usdwpZmkrfv19O39}tQPQM+oY**a{X?@3Qe>r$+G!>r#?Id&U&m^HU(f= zjVpSi9M||1FyNQA&PO`*94&(qTTMQv3-z`bpCXs-3bX}#Ovqec<>omYhB*VrwxqjY zF3#OXFsj`h#G?F}UAilxTQ|78-edHc-Uc-LHaH*Y(K%R#dVw>_gz}kRD4s#+U&Pq= zps)kMf_t9`GHR7CO4zI8WVj0%qiSqy50N{e_5o#GrvNhMpJf5_sCPrEa%a@ltFnss ziaWh26vEW4fQp}qa4oP(l4xIMpA)~VHD9!lP%;Tm`(HD$jYMM-5Ag>S(gC35J35$%?^gk(r|`4Ewi-W z;f&;B*fO=kC@N=r<-#nGW|yXE;`zb0Y3TJOAkw1a$SQgoTawHZTck+V%T=spmP`^BHihc(jc+S1ObX%6AYQ6LVVc+BfM*P{2s0T2z zVIs*5{ql%#CKAzv0?@S+%||z;`dpfj0Y(VtA51n$j%sG5I%A|h98VU}PkVZFrk1*G zaw75v3(N50lanvr&ND4=7Db;HS4fpi)2vTME7aD2-8N5+kcOXmYCrLE?*5&dWhvB` zbD5)ADuIwwpS*Ms;1qyns(8&tZ*)0*&_lNa`_(phwqkL}h#WdX_ zyKg%+7vP>*&Fus9E4SqIN*Ms`QLB(YOnJ|md%U|X`r#tVN$#q6nEH1|blQ?9e(3|3 z`i#;GUl~v?I6&I6%YvkvmR?*l%&z)Pv8irzVQsWrZSr%aoYuPJa#EjK|4NmiuswK= zlKP2v&;yXv3>LQ$P){aYWrb)5GICwbj;ygw>*amKP;Z{xb^cF}O@IeQ^hB-OjEK{l z>#PNyLuVkeDroL9SK2*ChHmJJSkv@YRn7)E49fy!3tqhq`HtHs_(DK|2Lyv(%9L&f zSy+H}Uk{nE2^5h7zN7;{tP3)$1GK9Xcv^L48Sodg0}ZST@}x607yJo2O*XCfs7*wT@d?G^Q6QQRb!kVn?}iZLUVoyh8M4A^ElaHD*Nn2= zkfCS=(Bg9-Mck6K{ z%ZM59Rs4(j1tSG1B#wS=$kQfXSvw6V>A(IC@>F;5RrCos`N{>Oyg|o*qR2EJ>5Gpe ze~a4CB{mmDXC7C>uS@VL&t%X#&4k<`nDx;Zjmo%?A4fV3KOhBr;VuO!cvM8s2;pG5 zcAs!j?nshFQhNA`G3HMS z?8bfRyy1LwSYktu+I7Hurb-AIU9r|rl5nMd!S&!()6xYNJ1EqJd9BkjgDH@F*! zzjtj4ezywvlkV7X@dG^oOB}T76eK=y!YZB#53LhYsZuP&HdmVL>6kH8&xwa zxv8;t-AE>D5K<{`-({E0O4%fGiLVI8#GfZ0aXR6SfYiPUJKnujMoTI5El<1ZO9w|u zS3lJFx<7XUoUD(@)$pDcs3taMb*(v2yj#G)=Mz-1M1q@Tf4o{s9}Uj9Yo?8refJwV zJ;b+7kf0M}fluzHHHS!Ph8MGJxJNks7C$58^EmlaJcp`5nx+O7?J)4}1!Y>-GHf9o zk}oTyPa>+YC$)(Qm8|MhEWbj?XEq}R=0NFH@F3ymW>&KS!e&k5*05>V@O*~my_Th; zlP05~S5@q+XG>0EuSH!~gZe_@5Dbj}oNIiPJpEOip+3l!gyze@%qOkmjmx=?FWJLF zj?b}f8Vet*yYd16KmM43rVfZo?rz3u|L6Foi*GQe4+{REUv9*}d?%a{%=8|i;I!aT z7Wxm}QJC`?cEt9+$@kSkB!@`TKZz1|yrA1^*7geq zD5Kx-zf|pvWA+8s$egLrb=kY385v2WCGL{y4I15NCz5NMnyXP_^@rsP#LN$%`2+AL zJaUyV<5;B^7f+pLzTN50Z~6KC0WI<|#bMfv+JiP3RTN^2!a7*oi+@v3w*sm5#|7zz zosF*{&;fHBXn2@uguQ1IDsh(oJzH#i4%pk;Qh^T zfQLyOW;E*NqU!Fki*f-T4j(?C$lY2CT{e!uW}8E(evb3!S%>v^NtNy@BTYAD;DkVo zn9ehVGaO7s?PQBP{p%b#orGi6Y&~<;D%XLWdUi}`Nu-(U$wBBTt*|N4##sm2JSuWc)TRoYg57cM*VDGj~ka<=&JF zo8=4>Z8F`wA?AUHtoi$_hHoK!3v?l*P0$g^yipOWlcex4?N2?Ewb1U=lu}0`QICA4 zef61j-^1p}hkA*0_(esa!p%dX6%-1e-eMfQsIp6wRgtE=6=hDe`&jel{y=6x5;78s z?5^{J|t!#x1aS8<3C`v%E%u{*wZwSXr$0Owl5_ zmXh>D>C_SjOCL^CyGZpBpM5`eymt{*rf~9`%F&&o7*S!H%3X)7~QFgn^J>6 zD+yV}u{HN-x9*_$R;a+k?4k*1f)rE~K|QvcC3dlr>!nftB?gE-cfcPMj&9mRl>|Lg zQyCe|&SuZopU0>IfRmcV3^_mhueN5oQ=J+H4%UsSIum4r4!`^DJqZr?1j3BU)Ttzg z6LwM)W&UEMIe*H2T6|{rQ;x9qGbp7ca#-!Egm4|ECNTMN);`>2Q&%|BpOdIJ4l|fp zk!qEhl;n(Y7~R1YNt7FnY10bQZXRna2X`E_D1f*}v1bW^lJorDD0_p2Rkr32n}hY! zCDB(t$)4YOd)97R60gfg3|wrlsVs#4=poh4JS7Ykg$H)vE#B|YFrxU-$Ae^~62e;! zK9mwxK?dV4(|0_sv(zY&mzkf{x@!T8@}Z6Bf)#sfGy#XyRS1{$Bl(6&+db=>uy-@y z$Eq~9fYX$06>PSKAs#|7RqJ3GFb;@(^e`jpo-14%^{|%}&|6h{CD(w@8(bu-m=dVl zoWmYtxTjwKlI!^nwJ}^+ql`&fE#pcj*3I|_Z>#y##e@AvnlSN4po#4N#}WT)V5oNP zkG+h_Yb=fB$)i`e2Fd28kS$;$*_sI;o0Xoj#uVAtsB6CjX&|;Bk}HzQ*hJ!HDQ&qZ z^qf{}c`l^h5sg-i(pEg#_9aW(yTi?#WH=48?2Hfl_X+(SfW)_c48bG5Bf+MDNp>Y#Mpil%{IzCXD&azAq4&1U10=$#ETJzev$)C*S;Pr9papU3OabRQk_toRZ!Ge(4-=Ki8Db?eSBq~ZT#ufL6SKaXZ+9rA~ zQwyTQTI7*NXOhn?^$QOU>Y6PyCFP|pg;wi8VZ5Z$)7+(I_9cy--(;T#c9SO;Hk~|_ z0tEQ)?geu8C(E$>e1wy%f@o;Ar2e#3HZP$I#+9ar9bDa(RUOA+y!oB;NEBQ`VMb@_ zLFj{syU4mN%9GF;zCwNbx@^)jkv$|vFtbtbi7_odG)9s=q(-PtOnIVcwy(FxnEZm&O^y`vwRfhB z7Urcums9SQS6(swAgl?S|WDGUTFQu51yG$8069U zviuZ=@J&7tQ8DZG<(a->RzV+sUrmH$WG+QvZmUJhT*IoR3#3{ugW%XG0s?_ycS6V6 zS)019<_Rl@DN~8K4#w3g_lvRm4mK3&jmI$mwROr0>D`mX+228Dw4r;mvx7df zy~$zP8NjVX?xkGFaV>|BLuXMQ+BN+MMrIB4S6X)p&5l$;6=S8oI9qi&1iQbs?TroDMfCmIeJ}pbVVtVqHhS(zutEy6#UjTk29-+3@W0`KfehW`@np zhhu#)O&g%r)hTj4b$CY41NYp_)7!bYyG;v(rts z^}YDJt2W88H^H;e$LSm3dh=~yi@)mzJtEfW8=4avbeOE&;Oc>-6OHO+MW`XBZ4rO6 zS;nAi**w3Yso4&Ty+8f$uvT?Z)eaLe$KW1I~9YM2zeTIT}C%_G6FPH-s5Wi3r`=I&juGTfl zZ;4qFZV|6V0c&>t!Y>mvGx#1WWL0N5evV=u28K9**dv`}U3tJ$W?>3InXiwyc)SA% zcnH}(zb0@&wmE>J07n#DOs7~lw>5qUY0(JDQszC~KAAM}Bmd-2tGIzUpO@|yGBrJyXGJk3d+7 zJBN0$?Se(rEb0-z2m%CBd;~_4aH04%9UnSc4KP!FDAM5F_EFujJZ!KDR-fn181GX` z8A?8BUYV}D9bCE0eV~M>9SPag%iVCLWOYQJDzC4~B~Ct0{H7x|kOmVcTQ;esvyHJC zi$H0R73Z8+Z!9^3|2tNut#&MVKbm`8?65s)UM8rg6uE(|e^DYqvoc15-f;u8c=>3;Viz*T# zN%!T+Hex0>>_gUKs%+lgY9jo6CnxL6qnQ>C*RseLWRpipqI;AQE7;LUwL`zM%b`Vu z%Sa-+?a#+=)HaD|k2%_(b;pHRF96(c;QyPl6XHL8IqGQKC$M8R=US-c8;hUe?LKo&l!{V)8d&55sUXEu z5uITcO~`ipddh+Nr{7ibp^Wd{bU)^3##<5`lkuqfckxEU*9{pgNpTB2=ku1c-|3dK z|LIQF=ld@I7swq^4|G1VA}BK85&>2p#*P95W`I1FF(8G9vfNJ6MoN$+C^M89u!X=< zJSS%l?Qj>$J%9?0#0&S6#*h*(-9Z$}q*G#hP?cX7cAvM0eiVFhJJ~$`iZM!N5NhDb zi<1u_m#?jzpIaOe7h|Kiap#mHA`L|)ATnPJ7du{^ybuNx@1jA+V1l8ux#{LJ#teM(6=%gZcMq24J$2p z`wcC!qRssmwUv4H6Psw{(YdDNOv$!sq&O1SvIS}fCKZa+`T=Ayt@uZjQqEC{@Uj+| z!;i3W+p~=@fqEEhW@gT^JtCR<`m`i|Htg<TSJ&v`p;55ed zt@a|)70mq;#RP@=%76*iz>fAr7FKd|X8*@?9sWOFf$gbH$XFG zcUNu#=_+ovUd>FW*twO`+NSo*bcea=nbQ_gu^C7iR*dZtYbMkXL5mB@4a3@0wnwH! z(fZKLy+yfQRd%}-!aPC z4GB%OvPHXl(^H(BwVr6u6s=I;`SHQ1um7GPCdP-BjO%OQUH!_UKbEGvHCY}{OL`8FU$GZ;Y$SlS$-0VjK%lCP?U0shcadt4x7lN4%V}wBrLEbiEcK-OHl+pcBNSqN#mftpRj2A4Q z+av@-<#t_Dj_FN^O2~wq(ij1O*+=RVl+6gNV^~CI1UED- zn^zN@UOq8?q58b^4RA>lV}x;jA2OE=SqMYV9P#RsUlI+pp!y*jpwHgp-w3i$V)%?L z>irn1pnRc|P@r|Z0pCeMZ*k$}$`1GVGCT&QtJ`V%Mq!TXoge?8Fjn$bz}NqDn*2ZQ z$p3@F_^(}IVS76>OLNzs`O5!pF=LZ$<&gyuM$HQzHx8ww^FVxnP%Yv2i=m*1ASF~~ zP=!H}b`xl`k0pL5byku2QOS~!_1po!6vQyQL#LQ#rIRr?G5^W?yuNvw-PP{}%m35i$i+I?DJ%RGRcqekT#X~CxOjkV1UQrd&m_bbJ+gsSGbPwKS{F& zU-`QNw!*yq#Co#{)2JvP-6>lY$J$2u+e=r0&kEc#j#jh@4Tp;l*s<28wU%r= zezVPG^r*a?&Fn_(M|A7^xTPD998E-)-A4agNwT?=>FbrHz8w~w?hWBeHVYM()|buJ zvGv4j<%!U_Rh^ZKi~2(h1vk-?o9;`*Zc}m5#o@a1ncp)}rO2SDD9y!nT$_Eb%h`>% zDmssJ8Dl=gDn<-7Ug$~nTaRzd?CJh;?}nCco$7Pz<#J8;YL40#VFbAG|4nA$co;l^byBOT2Ki@gAO!{xU7-TY|rujdYTaWV(Rr{Jwu?(_TA zDR1|~ExJBfJ?MAReMF47u!oEw>JHVREmROknZUs2>yaboEyVs$Pg1f6vs06gCQp$b z?##4PWI#BxjCAVl>46V_dm4?uw=Y@h#}ER4|ACU{lddiweg`vq>gmB25`XuhNai1- zjt{?&%;TRFE+2Y_Gn;p^&&|bU44M=`9!Mc%NbHv|2E4!2+dUL z>6be$Kh|Duz}+)(R7WXsh!m`+#t^Its($x`pqDaN-^E z?*a=0Ck^rZBLQV~jY-SBliN&7%-y3s@FB;X)z(t&D=~@U0vT%xfcu`Lix=W#WVE{{ z2=C~L$>`~@JCIg8RAyk= zYG`(@w4H95n0@Fqv16~nlDU!+QZw&#w@K)hv!V>zA!ZOL$1Iykd&Su3rEln@(gxO| zxWc++T-rQEIL+j7i`TeatMfp4z7Ir31(TE4+_Ds@M|-+cwQg(z>s=S}gsSz{X*Wm+ ziKJWgOd`5^o|5a#i%?Gvw~8e?Rpi7C>nQ5dvPHVTO$PI^mnJ*7?gd3RD{|c_a>WrXT#Es3d}(k z$wpmA#$Q^zFclx{-GUL_M$i0&mRQMd4J#xq-5es)yD{kYCP1s!An(~K5JDRkv6DUSKgo^s@lVM5|V4mWjNZp zsuw^##l%rbRDKglQyj?YT!nk$lNUzh%kH705HWhiMuv(5a<~yoRDM&oCqm+1#S~|8 zA$g2Xr=}p_FX%Eaq{tUO9i*Q1i!>$+1JYZCL}flWRvF0y1=#D#y-JQTwx6uP-(bC} z_uP7)c;Xd`C6k#JVW?#Id7-|`uW+hN0>OM=C2Ta^4?G zr;EvxJ{%l|8D-heRYRM%f*LBC)krHZJ@%&CL0)FADWh14&7KV<9km6gE=o9(7keg~^rIQtthK^_8%Jk&aZLY_bc6SbY>IcwDK9{sV*t1GfKwf8aCo8t za)yALEi^-WXb!k6n>W-62Z^n8hO|eRYr&uZiW5d_URi??nl*aGu?ioQ+9RF9u8kwD z6UZ6HVd(G%l9>y7E)uyn?gAJMKeki0@tG*jdcE-}K?8(D-&n=Ld1i=A1AI<1z>u5p=B z<1}|q3@2jNxW-}Q4z~s|j&^Qc;nXIdS3K8caP_07#ig} z#KAD&ue2jXc&K#Q`Hy#x+LeT4HHUCzi1e?*3w{tK+5Tij(#2l2%p#YGI-b~{5{aS8 z!jABC*n6y~W|h;P!kn(a4$Ri2G118!?0WHDNn((QDJP^I{{wPf<^efQWW?zS>VS?X zfIUgCS{7oV$|7z2hJBt+pp1CPx4L{B_yC3oWdE)d)20WG6m5qknl}8@;kjPJE@!xP zV(Nkv^-Vz>DuwBXmKT(z>57*D<$u=Blt)IS-RK0j89omD{5Ya*ULWkoO)qeM_*)jF zIn87l{kXPp=}4ufM1h7t(lAL?-kEq>_DE-in8-!@+>E1+gCV9Fq)5V3SY?**;AKq0 zIpQ(1u*3MVh#tHRu5E5=B{W-QOI34plm`#uH(mk*;9&Re%?|v-=fvb;?qvVL@gc|l z8^L?2_0ZrVFS-stRY(E>UiQeG_sMrw5UiO znGFLOP-GO{JtBM@!)Q37k3G_p&JhdwPwtJS6@R4_($Ut^b!8HP{52-tkue8MG=Zwr z7u6WaFranJq4oNadY)>_6d~?pKVxg$2Uz`zZPnZVHOh-;M|H7qbV0OF8}z;ZPoI+| z(`e}bn6u*kJpRLC>OZ}gX#eHCMEk#d8y$XzSU;QZ|An$pQ%uZC$=Ki!h@&m8$5(xCtGaY3X1FsU?l5w^Fr{Q-?+EbUBxx+b?D z80o*@qg0juG;aZhj=tO=YHjfo=1+-NqLME~Kw7Y1A*?}M7#cOyT(vd$1tVPKKd@U! z&oV!RzZcK6gPWj`*8FIAy2I&x``h_sXPe*O{|ih(Y+V3|o68MWq~2Iy^iQ8RqK76f zC$1+hXqd^jsz`U{+EFo^VQNrLZt#R`qE*>2-Ip&(@6FmtAngx@+YnG}b5B9Y)^wg#oc z24KlT2s!H_4ZR^1_nDX#UH4(UTgl603&Q3g{G4!?6Sl9Om=Sy|8CjWO>d@e9?Q%s- z-OS3*W_H7*LW|Ne{b+^#LqQ}UKDmiZDma@no2!ydO^jcm>+z379K%=Ifs{20mT|xh zP$e7P=?N(tW4PMHJOQ`a8?n}>^&@<`1Rgo`aRevPp^1n7ibeS6sc8^GPe>c&{Kc+R z^2_F~K=HVI45Pf|<3)^;I{?H}vU7-QK3L1nHpcn3!1_)<$V;e0d_b8^d1T==rVpky zZTn~UvKrjdr11k}UO@o>aR2wn{jX5`KQQM1J1A?^wAFvi&A#NA#`_qKksu`sQ0tdM ziif17TO<{wDq_Q;OM}+1xMji^5X=syK=$QdZnS#dwe$;JYC7JozV8KpwfV}?As|^! zFlln0UitprIpuzLd$`<{_XoUV>rrHgc{cUQH-Px#(_Ul%=#ENrfJe@MRP_$E@FLMa zI`(J)Imw$o427@Oc^3(U&vz}<3Lfmy7diVpJJJ@gA>e;q-&gj zcGcBC_luF%_;**EB?o--G?AkaruJ%-b*8aX$4E+-?V@RWMnjHJ;hx27Vd7l0nUUY( z6OQb&8g8cvN3LZ%^xvIav*X|Epqm@yrTZk9U{GSZXAUJt8Lh(%7?Eaf&AzmXOVvU| zmz<@l1oMe#^POR38KT6q3@c`{%eYNu4ccurv`q?b5DzLxENjSfYOJHAI$MbSNgB*D zJsP>i*BgrFlIn?x&DH9x~UbPBtMFj{_vJ#CaAF>1$oE&k`EF&L@HCa@mN>Q7~!RU>7 zW%fv84aCKSgBacmuvg}r@)YKqO$U{D5|!`vG-Gp%An}raz2gESWm0Exhux4C)zE}} z_@kn z3t}bvm?L+@@az@<*jG>(Xopq&c*;^mttlJ!mv;5k6o%Ac<_`o`4G3qzzo(GO{!&F8 zW+~bF?S;7gO1dQ@>gwZ?iIHjE#^@;Ix!Z`R6{RYLlGB&v4A)ha(2hc`RGV-8`LcvSf+Y@lhT%(Z7$tWEF;cZs2{B|9k#&C}sPyr; zd-g~${TqY7E$9X+h4_(yMxQ%q;tm(h(lKzK)2FQ%k#b2}aMy+a=LHYgk?1|1VQ=&e z9)olOA5H}UD{%nu+!3^HsrBoX^D9Iy0pw!xNGXB6bPSpKDAaun{!fT~Z~`xp&Ii~k zdac?&*lkM+k_&+4oc6=KJ6RwIkB|st@DiQ!4`sI;@40>%zAG^!oG2@ z@eBM$2PJ@F&_3_}oc8A*7mp-0bWng^he9UYX#Ph*JL+<>y+moP^xvQF!MD_)h@b}c2GVX8Ez`x!kjAIV>y9h;2EgwMhDc~tn<2~`lf9j8-Q~yL zM=!Ahm|3JL3?@Tt(OuDDfljlbbN@nIgn#k+7VC+Ko;@iKi>~ovA)(M6rz5KP(yiH| z#iwJqOB7VmFZ#6qI~93C`&qTxT(*Q@om-Xb%ntm_?E;|58Ipd1F!r>^vEjy}*M^E(WslbfLE z<+71#sY~m$gZvoRX@=^FY}X?5qoU|Vg8(o`Om5RM6I(baU^6HmB<+n9rBl@N$CmP41^s?s1ey}wu3r3 z4~1dkyi%kA#*pLQy0phlXa-u(oK2Dwzhuex$YZv=*t*Tg5=n~H=}fJA!p2L78y3D2 zimkqC1gTU(0q||k9QM#><$b-Ilw#Ut2>JF=T^qN34^qcBEd={! zB)rxUbM2IwvMo?S;Id^aglw}-t9et}@TP;!QlFoqqcs(-HfNt9VqGFJ4*Ko*Kk#*B zGpJ>tA9(=t|4#M!kBaf%{$Kfj3-uf|ZFgiU`Bo>%k_OuAp~vnE^_Tg8*% z*?)4JdzyMTzvNDy{r$c``zBw=Vr)6c4}CBIv#mw()3h7`?V-;LF?J&N5a>kjpy;9n zQyXvuu`n?+W84QV=(i`JEJY=}Ak+u4>!Lyt2P!$nBl}T=^|pG*z@)_l!)OKB{tIV&&E@hj=OIhSBHgPV~X=R3NrTMh?VzDm?1yW^IJ&zzAn2{8rE~MRX5EE)a(-T&oE)1J4pGXBYi+nexX-?5! z{EZ4Ju=Y8MQ87=uNc2t^7@X)?85KeSoc`?BmCD;Uv_cwQaLyc}vvnJKHV zuK)H_d)xhGKB!_pRXv{$XgfZ_(8G%N3o$ZI#_ zixQj~so0*m^iuA!bT>&8R@>b%#B~zbIlwt4Ba0v&>B(`*Z;~?6!>-aQ zal+Qt4^dCcjZZMd4b4Khg~(GP#8$3BeB8j!-6l?*##)H?J$PeUy)cA_I26#0aggao zaM5PweS_Sb@{OZ@Uw*(!DNV)KTQU+BTRi?AUAv0Vowth`7mr9)ZVC+TI?@; zWGL&zydnsuE3+D7#U~P%PrxpD3nTc9#mm621iX*?ZMS_Q#n9SzOJ~Hg@`rX{d?qJ; zt}`76!H)MX#=VKifJZP$3<8@}0-llthFpq3FV;(UP$-k63MkHHq~J&}d?C<+c~*Zk z<#G&>AD7EoiAVO38TO2TOBKN>6N|JS*{+`}V-)T0j(bAzGlEUWEvWLrMOIItYexh) z?he>SJk*#bywgDF6+*&%>n%0`-3tOY72+n&Q1NJ`A-bX*2tJV(@;%b6&RxMcUd7+# z@UzOmc9DolSHc-D$5(GouinaE%&uOVMyD&CTdKaEB{Qap4_wU7_=23CULKQ;jmZuV;+Y$(`#Gh0@}s7-!qk-^&#IG>7B{yft?UoA)H5 z|B0u3Tu0TF{AB0jpT|E&RsYB$3WiQU^5p*|f)^Si_#^j+Ao^|5(gNjn+!0|NtXDt* z5fwxpajl@e0FrdEuj2s#Pg>gUvJdko9RBwEe_4@?aEM?SiA2nvm^tsLML{-AvBWM7 z_bm7%tu*MaJkUWd#?GWVrqaQ0>B%Azkxj+Yidvc$XdG1{@$U~uF|1oovneldx`h;9 zB1>H;;n1_5(h`2ECl?bu-sSY@d!QTa`3DrNj_F@vUIdW5{R7$|K{fN11_l7={h7@D z4}I;wCCq>QR6(;JbVbb4$=OBO)#zVu|0iK~SnW~{SrOq&j*_>YRzU&bHUhPPwiy($ zK0qin8U;#F@@}_P_flw`bW_v^G;ct?Pb65%=%egDBgS#YF3?E36$9xzdvYqjAZoK#hcjctJu~MF^S*$q3`o2;!L|jPnM1x*Q~qF%BH(5UDFYglsJwO zEdEuB7NihnTXK6$)F~``nmSQNFP7x7hE{WuOjTAhEjGw#XxvL@S;aZYuyu9)!yZ~X zo35D6Cwb8`shRXCCR;xlR`n`cs4aie!SSM`0)x3ykwM*k zK~w^4x2u#=jEEi`3Q9AU!wE)Zpn#)0!*~)(T^SEjIJveav(d1$RaSMC0|}<)?}nSG zRC2xEBN_YAsuKyl_3yDt%W^F`J-TyeGrcfboC_0Ta=KcW_?~RLb>xbqIVI6`%iWz; zM8Kq9QzwO8w!TntqcB;gNuV$gd+N|(4?6A9GEzYs z5f4(*N5}&ObeYA~I28r;?pKUj4N6}iloE=ok%1|X()Ahdwir?xf6QJfY7owe>pPj)Me*}c^%W-pP6`dnX1&6 z`b#*_P0PeM+1FR)t)Rnr22f!@UFBW!TxgjV)u0%_C~gIbb_D3aPhZ~Wmex0)Lj`VoZKjoW)dUoKY6*| z0|V)|XyjiKgZ}s5(SN?te*muif87vD_(wYOiOjOKNI4L*aK||2$~;s25HS#iY6r=)WW8a^dkd0Y|pPc1-9jmy&wqoCbL84`C94At6$lm_o!8m*did^?o$m?ozIp{RmZ*M%YMX_i$KYkz_Q)QK?Fdm)REqf*f=@>C-SnW{Lb;yYfk&2nAC~b}&B@@^fY7g;n(FVh_hy zW}ifIO9T7nSBHBQP5%-&GF8@A-!%wJAjDn{gAg=lV6IJv!|-QEXT+O>3yoZNCSD3V zG$B?5Xl20xQT?c%cCh?mParFHBsMGB=_5hl#!$W@JHM-vKkiwYqr8kZJ06n%w|-bS zE?p&12hR2B+YB$0GQd;40fJd6#37-qd1}xc1mNCeC%PDxb zlK=X|WE*qn2fROb4{oXtJZSyjOFleI3i8RBZ?2u?EEL1W-~L%7<`H6Vp0;cz5vv`7jlTXf-7XGwp}3|Xl6tNaII3GC z9y1w*@jFLl2iFA!<5AQ~e@S|uK4WL9<$R^??V^aM?Bgy=#|wl$D2P$o;06>{f)P+X z91};NrzVV+)b}k2#rYLF0X0-A+eRul=opDju)g0+vd79B%i!Y}*&a^L$_|C&jQN^j z9q#4<(4)3qNst^+ZYpyVF2hP;DN|OMxM9w(+)%kFQRcYVI zO-frej9x6a%-D%Xuwedcw9#3VSVkOjNF!BYRoY1KD3wFJ%?ML*3QwcarMK)@v`o%s z$w=NLrO>og`nRJpZZ(%~*hNJU#Y~k;_Ci3~gc=4UQO!Ydje^?=W^DgCKyO;Zz4LgQ zKtm($MdY;UZ((U_g5*pMY+dYGyyT1ERkaj`U#S-2yyJ47wMonCpV+2rI8zPNHDfo& zc59dFz*2#^A-R?P6Np}jhDLi4&vP%$NW#8J>=CLj1mlf$XzmQezH*F1jNOiPgXl2j zzD07AKLT*h$CA*OsOba2etPLU%|p?=XhplXo?vOu@q0{QBo++)@6U?YKv_)GFK(^Y zm&uFBbrQyzJm;c49O00PIt;|{&ei%VSS%Y3m3#~L#(3%Gso^a4#9AaB$w@vnAvdr6 z%!2#)YS0HFt%o)q6~BelT;?%oUjX%9qQCn#-~+TM(a^s%Y>&aBkL(UY{+?a9@&Q+a;t%c_6u^6_r@>MEAN9ir5q=Yo|R8z4lKYd1sv^LyTozFn$KqaJ>? zoH&+`AX>E03Gv=71+NZK2>!-NasKeCfMp;@5rZ z*m<}q2!$AgKUwWRXTVHs!E>`FcMT|fzJo30W551|6RoE#Q0WPD$fdA>IRD-C=ae&$=Fuzc6q1CNF>b3z_c<9!;))OViz@ zP58XOt`WOQS)r@tD0IiEIo4Umc(5f%J1p{y4F(1&3AzeAP%V)e#}>2%8W9~x^l}S4 zUOc9^;@m{eUDGL={35TN0+kQbN$X~)P>~L?3FD>s;=PIq9f{Xsl)b7D@8JW{!WVi=s?aqGVKrSJB zO-V&R>_|3@u=MEV1AF%!V*;mZS=ZK9u5OVbETOE$9JhOs!YRxgwRS9XMQ0TArkAi< zu1EC{6!O{djvwxWk_cF`2JgB zE{oo?Cyjy5@Et}<6+>vsYWY3T7S-EcO?8lrm&3!318GR}f~VZMy+(GQ#X9yLEXnnX z7)UaEJSIHQtj5?O(ZJQ{0W{^JrD=EqH_h`gxh^HS!~)?S)s<7ox3eeb7lS!XiKNiWDj5!S1ZVr8m*Vm(LX=PFO>N%y7l+73j-eS1>v0g}5&G zp?qu*PR0C>)@9!mP#acrxNj`*gh}21yrvqyhpQQK)U6|hk1wt3`@h^0-$GQCE z^f#SJiU zb@27$QZ^SVuNSI7qoRcwiH6H(ax|Xx!@g__4i%NN5wu0;mM`CSTZjJw96htSu%C7? z#pPQ9o4xEOJ#DT#KRu9mzu!GH0jb{vhP$nkD}v`n1`tnnNls#^_AN-c~PD;MVeGMBhLT0Ce2O2nwYOlg39xtI24v>pzQ zanl2Vr$77%weA<>>iVZQ&*K9_hfmv=tXiu#PVzNA;M@2}l&vaQsh84GX_+hrIfZC= z0Se*ilv-%zoXRHyvAQW9nOI2C$%DlFH1%zP-4r8bEfHjB3;8{WH`gOYt zg+fX)HIleuMKewYtjg+cSVRUIxAD9xCn+MT zs`DA7)Wx;B`ycL8Q&dR8+8mfhK;a^Rw9 zh9tC~qa>%5T{^8THrj^VEl5Do4j4h@nkrBG6+k8CDD~KB=57m@BL-)vXGkKIuVO9v z7t_L5rpY^0y=uu5iNw0v&Ca-zWk>v;fLJ=+SaV&V#C-o^}8 zp&Xp$v?~ccnfR=&5Df)32^d6QJLg*iuF#s|0M4zJF@Hza1p`q|f}~K)q;HC*I1_9t zQ&1jr9-kdUi8)DGxiwdqU|rPxYWDQPWY&SI&Rxkhxobp~C=Y*`d?HD4JW?WjU7dBPeuIE`ABLq95b#lfKS52IB^6KoHmm60$R}TESplQt59#mboJj+Na!P)V{ic@$yQ-&Z za^JU0T+n0Lf2VdusoNr0?g~1DMsY)zdY-63yH!Ii#aWe|;0TO>L7#YlaDrH}xvYXn zh-NYa>O>f_NTTBG=|k0qWH+X?d5@+INsQ}WcI_3z1Z4-%Gj#_{P$0A~cAye`?j0cW z8)hd(V}7rattLUSMvgZ4g96P7n` z^{55A&&29;-P992{yhkGWa3v_Z6iB4a&~NmL)IpC&dsSwe$9jS(4RVJGt=Y!b-O~1 zSCl@wlaba_cA*yt(QvulMcLUuK z>(ys_!{vqKy{%%~d#4ibQ5$yKn6|4Ky0_ngH>x-}h3pHzRt;iqs}KzajS!i!Pqs8c zCP%xI*d=F=6za_0g`{ZO^mAwRk0iwkzKB7D)SaLR0h|ovGF2w9C9g8;f#EtDN*vBP9yl;n=;B2a7#E8(%Bw()z(M$_pu zQ+9uFnlJ!5&$kk^S_+kJ>r9y8MFPpSf9;o8v;ZxsMA!p>eaAIwt5xNiQ|2_ydGkbi zkggG;Xp&I7C8R{>ten^j@MsN#V5JPs1Ezc!74->Nh0a}U){OK@j=OIoY}C7IYYd8-V9 zQ6s?v=Y7(?Y$7=P#Wwub-*0DLqli?I%kT-D^jqK?c2~HEx<2(poRWAUoC}!~6$1=I z*M(IfPmdID8i+5l@=1(+`?i`G_ew=1Y!gF?tFbdgtW2etKLOFoNozkH(i!Qa7(h^| zF`9!VeqQQwM+yO6J`;oWUWq@9l6hP~FiG8-{Pj*T`XI3~s@FfjW2Tl(llpa901$&y`F}K1uZuHEo;=mr+_8d(o z2Be#yWHEN@euC$=VUSB+3A}khJdF$)0r#<5(f3n`kx>ZT8ifaKyX*OhffeHH1?6OM z*-19$j5tMNYQoB)>cGpz@11>J%q4KW`GLNj?uB>LcNg$0G@}XN#Tqf2F5@jv<`|~p zqB^l!%v!g{R_+0GX5z0>3Q~O``%T$NFc==dsPsTj-;{b$XUS0TGoJs2BUA*H;4S?w z|Nigt|F@9hf7QLSo}JPEK#CPgYgTjrdCSChx0yJeRdbXipF(OwV)ZvghYba)5NZxS zm=L8k_7Lb?f8`=vpv(@m%gzsCs9^E$D5Jn+sf}1lep*zz&5V?~qi_@B?-$Vd1ti(rCi*I0}c}slKv@H_+g?#yarVzpYZN zIk21Bz9Z#WOF`JG&TC&C%a*3*`)GJx9I!U8+!#J4}@5rm8*jK%Xg2VLjP-a;H zFydWO;nxOZ&|{yOW;ta$ZU^6*4vFP)idD6M*M0+9buB#hK4z%YTGBdSva?Pvxim2` zF-?QVGuRQ2-1eYzd1Y%}w^`t1S7|{{8=Es#ApC0<;pc$|NJ)IU%WVK+4gnTWA7-t1 z0K{DCESXb}!y_tzrycr^%%|G4T4)`$BC8+qm|n1lS?CO=`V`1T#ykY#5g5$dc$lGt zqGHyw-*Av%C;33nEiU(rU?w^3F46!dEz#cHd3IF<(XCq)>JG?Bi)4v26MQr1A-g5RqhFoPy%^TD3sa|D^9aS>>_2-X2i#? ztVp@ZkyMB;Uo#9s!R!@G#CCaFVaxx*8YYu$kGFk4g3|9t!1nKqOaDBAe;w!(6#w)0 z?{&F2BgctT1=Z;TvjOGL_!}Vlt=kaLA7#W`mv1h%hUg983!wA*K@_r6_cd6o z6LHiCE6qwlt2H&|Ica~%b9C?Z@$dreBNR_!NKcfL)%8kGr7!IVq|^&6PKYK%EhcKu z6+uR*%EOw=rF6Q42Mx|a> z$2XrM*NV2x9ci6|X^eh1UAbJ9Ky!#*Q5w7)#o#%}d!#-^k8To=n8{UU*LmFsS-wRj zi6-p76V6g?If3S&Bj~GW&QI_WtyPY0@u3hjKtqf9`8S!wn{@P&Tc8uu8cf)YmrX7+ zrC+O3V{9}JG6ihA&^2Q7@)Kq)j(Y_oTzsoBUYQDG!}`Ame`bbcr>J-6E%gaBPEDCU zflX#1-)Ih^HJV*lew*N_SdG-4!b2}G8%U&9_V0~Qt?ZS z@H3L&5ybV8X}A@KQADl93H`}0qkNm!jGHkCJUM%r8`mP1nV?Oo%^l;yDnU6IJtbuY z`X2Sf8|r00mB_f)Q0;S{FqS1Yq?otd-BVbw`#@SDd5}n5X4lqdDi1*vtVv8-Zi10q zexCj0eyngrp`UxjEOrdzUt`?%jRlj7zSU-V-%R?y+_w7P7f1ge%t1ozmN+&)%3xQW zT3u@)))(_a<6`lTJd`DIYw>(pkb=PMKvCNEG~zza+LVNqkY^}QoGMVdS0K;gS*A3f z;6Ua!^sSV-try(M^pB6D9dsX}c>$Da#NHucp9vr(fg4pbBR*uPhYq+N>q1X4RSOCl znIQj4=A+y+8{?LQ$3L@(!Yy~~Cu4Sx72*%@dW>eP%Br7=uaynV6Mqa-49A9) z|L&5r=4K5SClwc`!2J|>(#n$4y1>lmR~2Om8q6HkcpK>d(Fk!T^NO?hM4Fc+(5J{` z&K|vrBz;;zWlNO%=a~JkMxMiZa%wYz#G901lw#+2SUaMMHrebb&|1L8tKoGJK*QhJ zU9|WkDy^-4F6U&VYSc3ScHDk@kV^0801#I|-pSK%az5=DwI}gMm)@s2O+-ESTk?QY z;y9gyucaXO(Cc+cd{B>2)euMHFT71$a6DssWU>>oLw4E-7>FC-YgZH1QAbRwmdahD zO4KAeuA^0q&yWS|zLTx%(P4VOqZv-^BO`0OFAXdBNt9>LAXmPALi3b|gt{b?e-$z0 z4n7H$eg6y_zs(c>*4FT!kN*$H`43~1p!g;IZ8-mYbUPTejaLW#BZnAPFES?ApM{TQ zE*TC%O8)apqcX|PrNjIZE-z{q`I(LwIE0kf=PLjExEX>)oIu><<@lt>-Ng9i$Lrk( znGXl|i4dP;Mt^-IbEp7K0e#*c7By@gCo@VQIW$93ujLL`)lMbA9R?C_5u~7^KopaAMj#6&>n-SOWlup_@{4 zcJ?w_!9JKPM=&Bd#IQ37F*x39y!azm$;~IRlkm>bHdABcNwW-TdDKD$pkD{j6A8d* z{vP~|<}bj_Oz#83K$ieRtsA4a@4a5cRjJ}A01{PgxXn3;fx)5ElMEPwDX_mW9)9oB z*;scve~v#HHqUj3KdC$tdV3&0)Whkp-=hKKz{SzD7g0@N!wyv;ZAime7AjB7&)!)5 zp_iVblaf)%agwJqOG2e7WTCM1&khq`{b>fN4n8hOJbvO?Y;60>LIwagLXWC@@0RSR zo%lPo1cUU=g$ahJ8D=;`v~ORUSl(1-&a@yTAC5Y8E892@{P@MM=GXUGpBSXSbSs!N z;L~0D_s7{+^F6c!WW+^yz5~o7eWtsOE}8{hKaFlHgnyBeUJ8Zz2$k7Lrh?NuMU|No zVvsq@57)8zin;&ckR1;*Z%(xH2lBw z`x%N;|H1En8au588bPDxP^$kfpO!bIzz>K=5Jiq9Rg(NGde0g!rKagLa+&yC)jg7y zq}~2IH)N*FJC31qrIH-2;%3^F?=bDD^U2Y;%ftN(v71oY;od+vh!!2z^}GHR$43rg z0In@ki}TglIsMU^O1(SiLK#oiuyw zB>-@z?&uW`ILoPupw0_cs?C|2YoX&87~us+ny%eo{A!3M<-7O7mHUBCgA~{yR!Dc^ zb= z8}s4Ly!GdxEQj7HHr<}iu@%Lu+-bV>EZ6MnB~{v7U59;q<9$h}&0WT;SKRpf2IId ztAjig0@{@!ab z{yVt$e@uJ{3R~8*vfrL03KVF2pS5`oR75rm?1c`@a8e{G$zfx^mA*~d>1x`8#dRm) zFESmEnSSsupfB>h7MipTeE!t>BayDVjH~pu&(FI%bRUpZ*H615?2(_6vNmYwbc^KX4HqSi!&mY9$w zpf%C6vy@O30&3N5#0s_!jDk|6qjb-7wE3YT3DA7q3D`Q&Y*y>XbgE7=g#rPx1hnf8 zTWd{IC!Iysq*vZup5VGrO)UM<3)6raR`rOwk(!ikf3XPp!n|gz0hS*P=VDXAyMW(s zL??-`&IusEuOMrz>m(A1W5Q~>9xJwCExAcMkOBD` zD5BJSadd{0u}%z4r!9qA`FW4;Ka_Qk>FcHxiucGw4L9qhtoge|ag8jbr`7LHSbVQz z6|xUo*^LV1SLxS>?D`m=g{8IC&1YF$e}VRGD#ZOc_15QW%J@FbEj8tE-nGxo4?X02 z@|q#k*G4xMW>q84Xc09pRj@>Hz8t^fMm3n&G;Al6KU*;=W`7Q{$^|=bnZiJ7?(s)@ zB`vW>#zJ{}!8=*|?p(~fcXSanO^j8+q7V!q16*ic!HLRdz0TzNI6}m+=OKd2b8KX< zAcDTj*%~vQlcO+%@H01gjv-1zZaOXVoM*t-+KXTR#NoTf-#{dQAm?GqK6q8Ta zu3xW?t=NE$EfYa#=0HofLn5~c#m-U#Ct_r6~X-pg6k*F zYIP7De52BBwcAnK?O(j?YEs1;q60!-!hTuKzw3T;XcA_w5HvU;tO~}byLA^cggu8i z-IP@pxFjTy&ie28m}j66dm@g78xK7aG{QSR^bAcY+W*xWu;G~I08sf(GK4>K-cbfJ z-%v9DGR77He<291M~=fg>>9&NFQlboP)pC6fT;{>_!lM`A&&HWIMd)Y6e@IL;nvRdBE*Tn({&3{-XJ9helJa{G51Ck}-_Y=5C|fEo z)7fZlsHxN&SY&ZLTdYuBBZnwIh0#VTzmyK>U0|r&SXb&GP0m)1dGV8z(^x6s5yQ-z zEyniK${#U@Y7p@Yxx}E+jA?1@{=|e6UM;iyai=0=aItVvqieogZUq@sio2#9NLW~L z{w@^H!HEGU;>;T0lu{Ad20Hr6u;?-9YHKvkjEc)}wsb4Y-ArRK8`24uBT8N)8m%Ee zYJX21)|e{peL26}VUUKYQ3L@NSe8rEbN#AIo$tjJm-$B|IJU?mu(h$Sq`XNY0@NhY z0?WeMtPwP)sUdk}dWA4qBUV^x>P|is-kPgVe)*WV>dKDL>gOq1 zUYw(nU|N#dw>97A_(c3?VA_zDfF{^A1eE#8Bucd^ON(sv-{tc@&i)Y)3V~o7U~+AA zOwnXB5`WN^z$z<9^@(?LY%7?y5X_C(j1ip-Ug^f7Tt6suI3&a=&~#EJegG4r2^tKz zJoEXCVOc1QdOSNHp2d;t&smxL%CfK@mSl)Ky}`!6kCsi#7s5&G2Q!sM9S6o)&mdx% zz|2M~pav2;Th=DTN5yB@6HFAO!pl-y+tEJsh}(? z!tIyg01O*w@mWxsFhHMi7%Gqz!v(Osc5WxK+^1PGfsozw)FE}VIxk9GexmAohPNAF*SAjxG3Al#(xQoYXdI}TR zoCHAFS6+LDqsP8L1SZH{RxJjFK_=vy4nNH^?M!OsQWe^qC~$c1r&y`H9n5;D z2F$t-Htc%2@K(>opJHE{NytI2<_J<6Kz*p$wtKUTEH}zITx?H0L%!5%i@!rLphSBrkFs>jscP6?HVQovX8!~b~ZY|0h%&souT7e5nD@OxuSgC zVW*eo0B|1POwg7;6fJSUC`g+`1%XQvwpRc*&|AtV*h!#5nQM(@m!K)-Qop!Rt3F`a z9HUO zF3w{uI_==EpjFQWV4boF^A?wc@@@U+KrKPjn6sK{OLu-~1UloSqt-aHYo*^@kQy2+ zH(9*-mFz?YV4cL7EW)9hsdmG{5jaYXLvm*&3PZ4y?8z`$9z6`q9fgsJm@*W$-QSzu zut}57hroSbTd=&RJpuy#?K?A6!-;_MowpK8eb~5T-^eye%3O-T^ktSMbd%PT0j-B?#yAKr37u%gB z*2)WJMw6Y)6BvY$JjD`(06ci7u;u$hv}gN5oS&Q^*y$J6L)0#BD<>XL|;pZgtZaxp3~$0zxA(;6Qr_AP$?8l@S)C^Hoaz#rQFK^lA}3&)Gr}Fsca? zK>9BkVcl;c*E2P9UMppEIB&38dL9R?Xg9N{Nl~4*w!qsZJElz}Xc9gz#}cwnP4u{+ z6VNTEx*>u67?3bn{sWk*P`1_$YfsB+)Ax0+jt|)0p&VS?N0k8IAp2KH_#eY3I#{Hw zB$vObUDtXyZX)*wVh*@BefnUej#jv@%uiA=>ngX0kQXaz>8(WM)fX~v__@I}7|!Il z@J%r#I!JqqFwGd4JPhmDmL>1Bh}nn_BE;hgKUesNOf9zQhiuhn%4B}O8jnxEwJiQFDaiiuXw2sb?*8a}Lr;_#7+IPfIjhVDhazSpbQZECL+4)p8lO;)!y>Rt=0X*;O# zX{s(p-*d{#{Y3gVhL;A{4a(Z5sIfpk;WMCqdFA&Mb7mp;YMXhBF@p`}$ShAug+bo`;<9fm!~F z-;1yCj$GQ^mzucrfuatilXrYLr)`izjn_m(f~);txN?D7d?Kg4wDuPXilVyeVwjzf z=4Kewf=u}X_H*viVfPWZW?Sqa3G#h3|;b!Q7>BRc7-Wox0}&>}Lqo=0v;T_i~% zqB&h;14|~nK{W0N=$obGP@O%(c8SraYS^qiu%Q`B zBHdA!`Vk7#Bz*@_3eE#bizLzjBV;F0vfSA~+7@8+F{$7Y?fwI~Pp_X`2ORgqW6g@2 z{cQV!niSsMEVr1IaeRAj8~|*4yW~X5$6o`crw4uTHhgPs^qAk?9UPu;xy5wh2^jZ; z)@27Q=QKa?8w7_C0|u`@k=%b9Ce$D7x42CdLsckF2<$wLuV2kpik8PXex2^Co$n2o z)l#H*;#>?yrPw0x6LI@x(X$nezCBa0Obi%|I5ZV|4bJSPtNHjDkS|3S?fiv(i_(n* zFbve0g!B0!MMmakRsgg_if8nwImb=kk%|s+08xGQ)J?vpkdaya3UD|RJK+LQ72|g> zc4LnwInx!2pN-5Yvp7rvRF#B=(ZO8gyVB^0Dh#ZdHA2BjjppfV<=2Nm#w_t{%6O$W z`-?7N?LwL0DWgK0Y7L#ChSHfa{=DOpJpl8L@V70cd%ei)n%SQO;Z+Xw#li#%LUfbs z&hP%UzN(qM3cw#bWQS6_B@>1^ea-AqNA12xoiQeb_Zdtf>yHljqeIHqlyC^gzH)h1 zstXTFEb0r=l9;><<$a}YWlscH7VW_xeKVZ#*#v#HiuUOs7PPj8ml4#!BiGEK)kDpO zX=2mU0ZuIDDnhfV7v_Rs)0R#ff6I6_|MrzV(R$3Nt#S7D?GQy6?a^WRvA@r2~?7f~s99*9;fuqJ(843U`hRl2O|sk>J@WMsR2O zwyZt$@J)DnSUNkF@B3MPNz|<@`72{M*S5d<1Vkg+G=q~u{8OP84Yh6VCE5pNC*#m> z*jzHy5Tc82sBVw+6W7DoR5@LXZ|+>;)Q%czg%8pyMyeE2-)R^oHg~SrO~#I8MxNc> z6pWT&F&H1mX7#2@mBY>#rRoFKszT z(gvV#j3x|7sF|Dt0*CgsJTdH1R!>inYZWp*2RDbjjQCP98L_ds!$x&{t85NRYk4ii ztJ3HyC8h2A2&`kq^Cfci>N*r&btHg_|v6=s|v=(-MQ zK4kjqoI^~y`j9poC2r{Izdlehm8!AcMP^+SwDUce1Zon(%YvxK)x|rXsJRlO?-K91 zMsmHgI&PmqT_W}C0mdA_6L!EEjgJzidRvTN;vQRJ-uBl#{dEeN?24PRwx)7c5kF^ut=M0)e@zr?z_vpYf=%;;@UYF9>9-->Qf2FW*# z5*#VFB$$-k(zphh4sAElMiLbp`$+SKm*{l6qX;Q8GZ7b|J>OhC!yg$}8dt$dx3E8b z$FlaM*K@6mSsYCoe#*QjLEB3|_Vs4GbZI#!>Ya}dzh%uMn}sw0gFQQ{+V+e|_`q)M3nK27)nAqQ-viJoPHUKdr9HN`v0 z+tZo0ORLuv_d)x}gO|~s(H!12RM(aMfqLG>KSH#kGxC{sUUj>FUC(6;ds1cOjeDYu zOrd>q@bNFq5?0s&@5nbF3-rw{{V&YYf3o_9|K-X4k861UwZ&C2bH+A7^%7nizU>b? zC2@*VlrqprJiv$rx{+^+Op9i3RM;IHq@a;34=Gn%B+rXMZi=UsHC@TEFk4{*fs96p z)wNUY?AhVkdLGQmPESuh@-!iqSZrnxIT~Mon)J+i+B~9VdL8QE`^4=2@lNaKluUVx z_^i7~5E4dN4&gVMi%;7ast@WIY21Q`+^iTC*Gx@IMVYB`BLFHzPh{Fpc6LKZTk@>P zquo2E*Pgq(0MX>h>4)YaJYbIK&V?-W}JfL@&R0I2)TOA!Teg zNa4DBO&)`Nn0$Inb|d8ea|)qqOLYVbQIBRC4T4E<5#Nzc2 z57|Bq7mYsW8y?uLA$XMj%OeK+1|DAKcLYB98-vDP<3*+SKYcPcOkm&}H|!{9l*9%L zbiYJYJ^)Cql-&wPwABGD>Ai7SUXe15m zIr^wNEU$9)D6@atm z(w(1~GuLpHi?JGgIBj`Ovy;j4M`XjrCNs?JsGh1zKsZ{8 z@%G?i>LaU7#uSQLpypocm*onI)$8zFgVWc7_8PVuuw>u`j-<@R$Of}T`glJ!@v*N^ zc(T~+N+M!ZczPSXN&?Ww(<@B=+*jZ+KmcpB8* zDY_1bZ3fwTw|urH{LLWB;DCGzz$jD|VX#Af@HC%BktA8F7VJSy&!5iTt};#U^e0_q zh6j7KCTInKqriZ1`BiF3iq2LWk;gyt0ORIFc4Mi3Bx`7WEuFq{u^C49-SYVjnv!_40m1>7x*+<8~Xkq?056 z!RBfE@osP%SxzOw>cLAQ$bioAOC0V!OzIXIc};)8HjfPtc~8tnah$PtoAz`4k)7$FDUc2O@D)g_uAo&nXMymK$##V?gYUPt^l zj{6NFDL(l-Rh(xkAHP%bBa=($r%3Y~jB!eQ1Smuq2iuQ|>n%Y=p(26SE5gFu11*Q< zaPN5G^d;Iovf`VY&Gh58z~%JpGzaeUz6QoBL^J%+U4|30w7Q&g9i}}@l61eKEfCgo zST6qMxF_Eaj7;0OC)TSU{4_m}%FOa6B{AxS$QIcmmG~IVjjf;7Uk!HBtHfm{%LsLb zu8~5VQFyOZk&!VY(wxL__haJ;>Bj?g&n`+i&=X{unJmv&0whCitWfGlOr6+Tc-lMZ z(ZRXqC-=O+GAvTXKViA9vdwu{aifhk$tYh~-9BScg!Yr*M2zw&9`pHMxHGh`dUH-1;~^6lF@ep;X9PjQ!rqmXNWJ?#P-qb%*TB%xe&3 zX*5V>xuW7)$3!Yc$y>cwBqd8+p+u>WS7p7~O80ipG{(a*#=NJ`^Ld6k-`|;Y&htFy zIi2(Sm)4eD=o+CGo~M3%qF|O9P0+ahmc%EklI?NgX05W3+OdS`_Rd#wg-}hd1&txU5wXy zy`x)05?WVZvELw`XWetIAg6$|(^4ntaE;=f$Wcpwbxm7?bLDnPs-1!bRoMcy!EeOh zpIv8ewDzcIU}mv1NxV!&(Wf7~_kqGAk=2=j&O5FA)z2!APCcDQPnIaiqMkVT4fUyX z))R|WvOJyzcU6d=z0q8JDt42*`js4g+_t{YP7lVguX+vhEejJ3TAIo*Z6jizHm#S- zZT_}-STQAa-0Gn8+RmR7V}{Ns1@jJ{^Sb!9&RSXXP;^ep)r6;&PW++~XYXC9a=zSF z?sp(JQo&MROb~b1Y*Xw4!P)>PHT>Z<)*U=Ax_75^OUw97pNudbxS1XPtNrIg zQ5YB77E@i7$2Ia}(^JcCi@OX`9a|m}PY%-th2m~y+)eCl>fTVjCP^lDOBLyhg1DZ+ z)~G{&OkDc$!;t~`gq(wz@qW3lh9B^ic$>-h#nV!H8d#l+>C(M%g}u2g=I#&W|L!VD zqHYoQkBW;`r|fW02u{7X!X;}T7X4iAaWzkeOh}7&o!F1qt4#$1|BDF;(2VlgEqJ$F zy8Ba-y(%fs`MzpvyXlQLEhS^ed$7Va2hO%?$-D>^*f$b)2Hx;}Ao$UqFt7l26<7eP z!{!C7PVrq>=794Zqmc z%LKkzIBZq@%Ja8EkH}?>c5ILG(EAMS*JHu?#9_7TsELw)8LZzN>f2Y6YN{AJC?34> zh42sPa1%2JpCeS9&E1URm+Pb}B>A1M`R{+O+2~}c(@^1Rf&J9p(4QqHl;E^4w5;I5 zM{?(A^eg*6DY_kI*-9!?If^HaNBfuh*u==X1_a?8$EQ3z!&;v2iJ``O7mZh%G)(O8 ze<4wX?N94(Ozf9`j+=TZpCbH>KVjWyLUe*SCiYO=rFZ4}S~Tq|ln75Jz7$AcKl$=hub=-0RM1s(0WMmE`(OPtAj>7_2I5&76hu2KPIA0y;9{+8yKa;9-m??hIE5t`5DrZ8DzRsQ+{p1jk-VFL9U z2NK_oIeqvyze>1K%b|V?-t;Wv`nY~?-t;tMC4ozyk8CR(hoZTno3!*8ZTc15`?MFf zDI892&g&3lshOEv4E@w-*_%)8C_<&HhV`0D5lN$WT4Q^UWHNSAE+RZe(o z%bqR^hp1IsDr47e^AajFtlppT)2F6yPcrWO9{Kw{o=P6y^HOW$Wqd_)_fwzn`ikZl zOGVc0+S(*=xZ_KbL0Nr`Sx$$CWEbw$52udl1f=X6CZEcFMA*nl>`0gn4&tc5^`!!)tGw<}^Q>P7E}$ zialDUofH*XcB3r9@tA@lnS}dA(@nK_xuw0b;FPUnNGD0;MIySCw=cSzB#=3>F37V-nni3UNB)-;;Gkk;3l9fh6FIjSZU zk=Eo2a`6i7@i*4>ym5`R?i-uZFv6+iX*Gi^I}ZU1OrLAX8aGiT@`*YnjeF>}$U}ORP`+EY5`eqVC_&4yG z;Tp>+2QbZ?lt1GB+D}q14W3dWP8lWnN zf(nlT6+XW&(zme{FbyDpP^NakA<~TK=Y}H^eS%2rt0v8Lr)B}@B!cTvC=9FM;7q4@ zf*;vb4HG>RFpY5?vFCp27VEnVIGx~-na6biU4{+UoYe=}^R#_My6wT$5d&r*=kpAA zu;=-c0|~yqi(N8&*H;aNfhyey+HHQ7J_qae*_CgG2V8j=Tq936S0DC8r3BXBql3Gz z0pLo_`|4Q+oY3rPBNaLmL{QM};9dke>ujP^j@z-N;fNlKb|edn>)YaafDaJ>GWKP$ z5}l&#$QFhN!CMT;WH&z-5E)kvM|36lV!^#3z{@2FF>HsgUO4PMqO#U$X%+U>K!xJ@ zBFs|+woG_9HZQs_Tw*vnCPGhlXG@>y|6pJT$I67!aP&b0o$AF2JwFy9OoapQAk>k7 z**+$_5L;5fKof<;NBX%_;vP@eyD=Z0(QW)5AF7 zp|=tk3p?5)*e~Inuydz-U?%Kuj4%zToS5I|lolPT!B)ZuRVkVa>f*-2aPeV3R79xh zB)3A$>X~szg#}>uNkpLPG#3IKyeMHM*pUuV5=-Jji7S6PSQ9oCLo{oXxzOZfF$PP) zrYwlmSQ-~n94uO3CD{K0QTmj@g%Yzn7_xQ4fTduU0Yqvln`e_`CdXH5iQ5qRr1 zBC;}%YZ2!4I>*=sR)O~jBPx6sxmIEBnq)s-fHz_y0z8-gPl2Us4BiBXNR5CIF!YR@ zb9B305SilU*@4|+ x6JBtc8JSt5M0pkooaq!^FqtuD_KdXXTo>Mw54>`rP&>h&58!3a6l6r9{sG7g--!SK literal 0 HcmV?d00001 diff --git a/mcspring-examples/gradle/wrapper/gradle-wrapper.properties b/mcspring-examples/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..ba94df8 --- /dev/null +++ b/mcspring-examples/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/mcspring-examples/gradlew b/mcspring-examples/gradlew new file mode 100644 index 0000000..2fe81a7 --- /dev/null +++ b/mcspring-examples/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/mcspring-examples/gradlew.bat b/mcspring-examples/gradlew.bat new file mode 100644 index 0000000..9618d8d --- /dev/null +++ b/mcspring-examples/gradlew.bat @@ -0,0 +1,100 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/mcspring-examples/mcspring-plugin-manager/build.gradle.kts b/mcspring-examples/mcspring-plugin-manager/build.gradle.kts new file mode 100644 index 0000000..aadd880 --- /dev/null +++ b/mcspring-examples/mcspring-plugin-manager/build.gradle.kts @@ -0,0 +1,9 @@ +dependencies { + implementation("in.kyle.mcspring:mcspring-base:+") + implementation("in.kyle.mcspring:mcspring-commands-dsl:+") +} + +mcspring { + pluginAuthor = "kylepls" + pluginMainPackage = "test" +} diff --git a/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/McSpringPluginManager.kt b/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/McSpringPluginManager.kt new file mode 100644 index 0000000..c5e586e --- /dev/null +++ b/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/McSpringPluginManager.kt @@ -0,0 +1,6 @@ +package `in`.kyle.mcspring.manager + +import org.springframework.boot.autoconfigure.SpringBootApplication + +@SpringBootApplication +open class McSpringPluginManager diff --git a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandAbout.kt b/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandAbout.kt similarity index 92% rename from mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandAbout.kt rename to mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandAbout.kt index dc2e144..8652480 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandAbout.kt +++ b/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandAbout.kt @@ -1,5 +1,6 @@ package `in`.kyle.mcspring.manager.commands +import `in`.kyle.mcspring.commands.dsl.command import `in`.kyle.mcspring.commands.dsl.commandExecutor import `in`.kyle.mcspring.commands.dsl.mcspring.Command import org.bukkit.Server @@ -15,7 +16,7 @@ internal class CommandAbout( @Command(value = "about", description = "Provides information about current library versions in use") - fun about() = commandExecutor { + fun about() = command { then { val aboutString = """ Plugin Name: ${properties.name} diff --git a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandClassLoader.kt b/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandClassLoader.kt similarity index 92% rename from mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandClassLoader.kt rename to mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandClassLoader.kt index 48c015c..5ff986f 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandClassLoader.kt +++ b/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandClassLoader.kt @@ -1,5 +1,6 @@ package `in`.kyle.mcspring.manager.commands +import `in`.kyle.mcspring.commands.dsl.command import `in`.kyle.mcspring.commands.dsl.commandExecutor import `in`.kyle.mcspring.commands.dsl.mcspring.Command import org.springframework.stereotype.Component @@ -13,7 +14,7 @@ internal class CommandClassLoader { description = "Show ClassLoader information for a specific class", usage = "/classloader " ) - fun classLoader() = commandExecutor { + fun classLoader() = command { val className = stringArg { missing { message("Usage: /$label ") diff --git a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandGamemode.kt b/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandGamemode.kt similarity index 83% rename from mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandGamemode.kt rename to mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandGamemode.kt index 6320eaf..eb209dd 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandGamemode.kt +++ b/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandGamemode.kt @@ -1,5 +1,6 @@ package `in`.kyle.mcspring.manager.commands +import `in`.kyle.mcspring.commands.dsl.command import `in`.kyle.mcspring.commands.dsl.commandExecutor import `in`.kyle.mcspring.commands.dsl.mcspring.Command import org.bukkit.GameMode @@ -17,7 +18,7 @@ internal class CommandGamemode { description = "Set your game mode", usage = "/gamemode " ) - fun gamemode() = commandExecutor { + fun gamemode() = command { requirePlayer { message("Only players can run this command.") } val gameMode = mapArg { @@ -32,14 +33,14 @@ internal class CommandGamemode { then { message(gamemodeExecutor(sender as Player, gameMode)) } } + @Command(value = "gmc", description = "Set your game mode to creative") + fun gmc() = command { then { message(gamemodeExecutor(sender as Player, GameMode.CREATIVE)) } } + + @Command(value = "gms", description = "Set your game mode to survival") + fun gms() = command { then { message(gamemodeExecutor(sender as Player, GameMode.SURVIVAL)) } } + private fun gamemodeExecutor(target: Player, gameMode: GameMode): String { target.gameMode = gameMode return "Game mode set to ${gameMode.name.toLowerCase()}" } - - @Command(value = "gmc", description = "Set your game mode to creative") - fun gmc() = commandExecutor { then { message(gamemodeExecutor(sender as Player, GameMode.CREATIVE)) } } - - @Command(value = "gms", description = "Set your game mode to survival") - fun gms() = commandExecutor { then { message(gamemodeExecutor(sender as Player, GameMode.SURVIVAL)) } } } diff --git a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandHeal.kt b/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandHeal.kt similarity index 93% rename from mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandHeal.kt rename to mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandHeal.kt index c39a1b1..add6178 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandHeal.kt +++ b/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandHeal.kt @@ -1,5 +1,6 @@ package `in`.kyle.mcspring.manager.commands +import `in`.kyle.mcspring.commands.dsl.command import `in`.kyle.mcspring.commands.dsl.commandExecutor import `in`.kyle.mcspring.commands.dsl.mcspring.Command import org.bukkit.entity.Player @@ -13,7 +14,7 @@ internal class CommandHeal { description = "Heal yourself or another player", usage = "/heal ?" ) - fun heal() = commandExecutor { + fun heal() = command { requirePlayer { message("Sender must be a player") } val target = playerArg { diff --git a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandOp.kt b/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandOp.kt similarity index 92% rename from mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandOp.kt rename to mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandOp.kt index be293e0..897ad0c 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandOp.kt +++ b/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandOp.kt @@ -1,5 +1,6 @@ package `in`.kyle.mcspring.manager.commands +import `in`.kyle.mcspring.commands.dsl.command import `in`.kyle.mcspring.commands.dsl.commandExecutor import `in`.kyle.mcspring.commands.dsl.mcspring.Command import org.bukkit.command.CommandSender @@ -14,7 +15,7 @@ internal class CommandOp { description = "Toggle yourself or another players OP status", usage = "/op ?" ) - fun op() = commandExecutor { + fun op() = command { val target = playerArg { default { sender as? Player } invalid { message("Player $it not found") } diff --git a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandPlugin.kt b/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandPlugin.kt similarity index 94% rename from mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandPlugin.kt rename to mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandPlugin.kt index ff43da1..0f5596a 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandPlugin.kt +++ b/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandPlugin.kt @@ -1,5 +1,6 @@ package `in`.kyle.mcspring.manager.commands +import `in`.kyle.mcspring.commands.dsl.command import `in`.kyle.mcspring.manager.controller.PluginController import `in`.kyle.mcspring.commands.dsl.commandExecutor import `in`.kyle.mcspring.commands.dsl.mcspring.Command @@ -18,7 +19,7 @@ internal class CommandPlugin(private val pluginController: PluginController) { description = "Load/unload/reload a specific plugin", usage = "/plugin " ) - fun plugin() = commandExecutor { + fun plugin() = command { subcommand { on("load", commandExecutor = load()) on("unload", commandExecutor = unload()) @@ -31,7 +32,7 @@ internal class CommandPlugin(private val pluginController: PluginController) { } } - private fun load() = commandExecutor { + private fun load() = command { val path = mapArg { parser { map(pluginController.loadablePlugins) @@ -41,7 +42,7 @@ internal class CommandPlugin(private val pluginController: PluginController) { then { executeLoad(path) } } - private fun unload() = commandExecutor { + private fun unload() = command { val plugin = mapArg { parser { map(pluginController.plugins.associateBy({ it.name }, { it })) diff --git a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandReload.kt b/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandReload.kt similarity index 89% rename from mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandReload.kt rename to mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandReload.kt index 4fcf439..90842b1 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandReload.kt +++ b/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandReload.kt @@ -1,5 +1,6 @@ package `in`.kyle.mcspring.manager.commands +import `in`.kyle.mcspring.commands.dsl.command import `in`.kyle.mcspring.commands.dsl.commandExecutor import `in`.kyle.mcspring.commands.dsl.mcspring.Command import org.bukkit.Bukkit @@ -16,7 +17,7 @@ class CommandReload { aliases = ["rl"], description = "Reload the server" ) - fun reload() = commandExecutor { + fun reload() = command { then { sender.sendMessage("Reloading the server...") Bukkit.getServer().reload() diff --git a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandSpeed.kt b/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandSpeed.kt similarity index 94% rename from mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandSpeed.kt rename to mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandSpeed.kt index fb48f99..5c17e07 100644 --- a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandSpeed.kt +++ b/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandSpeed.kt @@ -1,5 +1,6 @@ package `in`.kyle.mcspring.manager.commands +import `in`.kyle.mcspring.commands.dsl.command import `in`.kyle.mcspring.commands.dsl.commandExecutor import `in`.kyle.mcspring.commands.dsl.mcspring.Command import org.bukkit.entity.Player @@ -13,7 +14,7 @@ internal class CommandSpeed { description = "Set your movement and fly speed", usage = "/speed " ) - fun speed() = commandExecutor { + fun speed() = command { requirePlayer { message("Sender must be a player") } val player = sender as Player val speed = doubleArg { diff --git a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/BukkitPluginUnloader.kt b/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/BukkitPluginUnloader.kt similarity index 100% rename from mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/BukkitPluginUnloader.kt rename to mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/BukkitPluginUnloader.kt diff --git a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/LogFileController.kt b/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/LogFileController.kt similarity index 100% rename from mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/LogFileController.kt rename to mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/LogFileController.kt diff --git a/mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/PluginController.kt b/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/PluginController.kt similarity index 100% rename from mcspring-build/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/PluginController.kt rename to mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/PluginController.kt diff --git a/mcspring-examples/mcspring-plugin-manager/src/test/kotlin_disabled/in/kyle/mcspring/manager/commands/TestCommandClassLoader.kt b/mcspring-examples/mcspring-plugin-manager/src/test/kotlin_disabled/in/kyle/mcspring/manager/commands/TestCommandClassLoader.kt new file mode 100644 index 0000000..dccd5a7 --- /dev/null +++ b/mcspring-examples/mcspring-plugin-manager/src/test/kotlin_disabled/in/kyle/mcspring/manager/commands/TestCommandClassLoader.kt @@ -0,0 +1,19 @@ +package `in`.kyle.mcspring.manager.commands + +import `in`.kyle.mcspring.test.MCSpringTest +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired + +// TODO +@MCSpringTest +internal class TestCommandClassLoader { +// +// @Test +// fun testClassLoader(@Autowired commandExecutor: TestCommandExecutor) { +// val output = commandExecutor.run("classloader " + javaClass.name) +// assertThat(output).hasSize(2) +// assertThat(output[0]).isEqualTo("ClassLoader: ${javaClass.classLoader}") +// assertThat(output[1]).isEqualTo("Domain: ${javaClass.protectionDomain.codeSource.location}") +// } +} diff --git a/mcspring-examples/mcspring-plugin-manager/src/test/kotlin_disabled/in/kyle/mcspring/manager/commands/TestCommandGamemode.kt b/mcspring-examples/mcspring-plugin-manager/src/test/kotlin_disabled/in/kyle/mcspring/manager/commands/TestCommandGamemode.kt new file mode 100644 index 0000000..fd401ea --- /dev/null +++ b/mcspring-examples/mcspring-plugin-manager/src/test/kotlin_disabled/in/kyle/mcspring/manager/commands/TestCommandGamemode.kt @@ -0,0 +1,28 @@ +package `in`.kyle.mcspring.manager.commands + +import `in`.kyle.mcspring.test.MCSpringTest + +// TODO +@MCSpringTest +class TestCommandGamemode { +// +// @Autowired +// lateinit var executor: TestCommandExecutor +// +// @ParameterizedTest +// @CsvSource( +// "gmc, CREATIVE", +// "gms, SURVIVAL", +// "gm creative, CREATIVE", +// "gm survival, SURVIVAL", +// "gm 1, CREATIVE", +// "gm 0, SURVIVAL" +// ) +// fun testGamemodes(command: String, targetGameMode: GameMode) { +// val (sender, messages) = executor.makeTestPlayer() +// executor.run(sender, command) +// assertThat(messages).first().asString() +// .isEqualTo("Game mode set to ${targetGameMode.name.toLowerCase()}") +// verify(sender, times(1)).gameMode = targetGameMode +// } +} diff --git a/mcspring-examples/mcspring-plugin-manager/src/test/kotlin_disabled/in/kyle/mcspring/manager/commands/TestCommandSpeed.kt b/mcspring-examples/mcspring-plugin-manager/src/test/kotlin_disabled/in/kyle/mcspring/manager/commands/TestCommandSpeed.kt new file mode 100644 index 0000000..1728a48 --- /dev/null +++ b/mcspring-examples/mcspring-plugin-manager/src/test/kotlin_disabled/in/kyle/mcspring/manager/commands/TestCommandSpeed.kt @@ -0,0 +1,34 @@ +package `in`.kyle.mcspring.manager.commands + +import `in`.kyle.mcspring.test.MCSpringTest +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.mockito.ArgumentMatchers.anyFloat +import org.mockito.Mockito.* +import org.springframework.beans.factory.annotation.Autowired + +// TODO +@MCSpringTest +internal class TestCommandSpeed() { +// +// @Autowired +// lateinit var executor: TestCommandExecutor +// +// @Test +// fun testSpeed() { +// val (sender, messages) = executor.makeTestPlayer() +// executor.run(sender, "speed 10") +// assertThat(messages).first().asString().matches("Speed set to [^ ]+") +// verify(sender, times(1)).walkSpeed = 10F +// verify(sender, times(1)).flySpeed = 10F +// } +// +// @Test +// fun testSpeedUsage() { +// val (sender, messages) = executor.makeTestPlayer() +// executor.run(sender, "speed") +// assertThat(messages).first().asString().matches("Usage: .+") +// verify(sender, never()).walkSpeed = anyFloat() +// verify(sender, never()).flySpeed = anyFloat() +// } +} diff --git a/mcspring-examples/settings.gradle.kts b/mcspring-examples/settings.gradle.kts new file mode 100644 index 0000000..1b932af --- /dev/null +++ b/mcspring-examples/settings.gradle.kts @@ -0,0 +1,12 @@ +pluginManagement { + repositories { + mavenLocal() + gradlePluginPortal() + mavenCentral() + jcenter() + } +} + +include(":mcspring-plugin-manager") +include(":simple-factions") +include(":simple-factions-addon") diff --git a/mcspring-examples/simple-factions-addon/build.gradle.kts b/mcspring-examples/simple-factions-addon/build.gradle.kts index 74b92aa..96aac72 100644 --- a/mcspring-examples/simple-factions-addon/build.gradle.kts +++ b/mcspring-examples/simple-factions-addon/build.gradle.kts @@ -1,4 +1,3 @@ dependencies { - compileOnly(project(":mcspring-build:mcspring-starter:mcspring-examples:simple-factions")) - compileOnly("org.projectlombok:lombok:1.18.12") + compileOnly(project(":simple-factions")) } diff --git a/mcspring-examples/simple-factions/build.gradle.kts b/mcspring-examples/simple-factions/build.gradle.kts index 80bc545..ab53060 100644 --- a/mcspring-examples/simple-factions/build.gradle.kts +++ b/mcspring-examples/simple-factions/build.gradle.kts @@ -1,4 +1,4 @@ dependencies { - implementation(project(":mcspring-api:mcspring-commands-dsl")) - compileOnly("org.projectlombok:lombok:1.18.12") + implementation("in.kyle.mcspring:mcspring-base:+") + implementation("in.kyle.mcspring:mcspring-commands-dsl:+") } diff --git a/settings.gradle.kts b/settings.gradle.kts index ce61f00..39f6ad6 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -14,8 +14,6 @@ include(":mcspring-api:mcspring-chat") include(":mcspring-build") include(":mcspring-build:mcspring-gradle-plugin") -//include(":mcspring-build:mcspring-plugin-manager") -//includeBuild("./mcspring-build/mcspring-gradle-plugin") gradleEnterprise { buildScan { From 957e43a572e2f3778a6eb19a1cd597b13bde29e7 Mon Sep 17 00:00:00 2001 From: kylepls Date: Sat, 27 Jun 2020 13:25:35 -0600 Subject: [PATCH 46/54] Fix failing test --- .../kotlin/in/kyle/mcspring/commands/dsl/TestTabCompletions.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestTabCompletions.kt b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestTabCompletions.kt index b8c33f5..b884d61 100644 --- a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestTabCompletions.kt +++ b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/TestTabCompletions.kt @@ -41,7 +41,7 @@ class TestTabCompletions : FreeSpec({ } commandComplete() - } shouldBe listOf("two") + } shouldBe emptyList() } "invalid arg values should stop parsing" - { From f1b329872fb1528ccf05c321cd8c81c92d75efe6 Mon Sep 17 00:00:00 2001 From: kylepls Date: Sat, 27 Jun 2020 14:52:11 -0600 Subject: [PATCH 47/54] remove extra deps --- mcspring-api/mcspring-chat/build.gradle.kts | 1 - mcspring-api/mcspring-rx/build.gradle.kts | 1 - .../src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/mcspring-api/mcspring-chat/build.gradle.kts b/mcspring-api/mcspring-chat/build.gradle.kts index 6de75a4..7d82dc7 100644 --- a/mcspring-api/mcspring-chat/build.gradle.kts +++ b/mcspring-api/mcspring-chat/build.gradle.kts @@ -1,3 +1,2 @@ dependencies { - api("org.spigotmc:spigot-api") } diff --git a/mcspring-api/mcspring-rx/build.gradle.kts b/mcspring-api/mcspring-rx/build.gradle.kts index 0ba349d..0ac580d 100644 --- a/mcspring-api/mcspring-rx/build.gradle.kts +++ b/mcspring-api/mcspring-rx/build.gradle.kts @@ -1,6 +1,5 @@ val rxJava = "3.0.4" dependencies { - api("org.spigotmc:spigot-api") api("io.reactivex.rxjava3:rxjava:$rxJava") implementation("io.reactivex.rxjava3:rxjava:$rxJava") } diff --git a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt index f1f4947..e42cb0a 100644 --- a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt +++ b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt @@ -194,7 +194,7 @@ open class BuildPluginYml : DefaultTask() { require(mains.size == 1) { """ There should be 1 main class on the classpath: $mains - Make sure to annotate a class with @SpringPlugin + Make sure to annotate a class with @SpringBootApplication This serves as an entry point for mcspring. """.trimIndent() } From e9ed019c61f72cc82133f834977b0182c1851ecd Mon Sep 17 00:00:00 2001 From: kylepls Date: Tue, 30 Jun 2020 12:43:23 -0600 Subject: [PATCH 48/54] Add api version support --- mcspring-api/mcspring-guis/build.gradle.kts | 1 - .../src/main/kotlin/in/kyle/mcspring/McSpringExtension.kt | 5 +++++ .../src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginJar.kt | 2 ++ .../src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt | 2 ++ 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/mcspring-api/mcspring-guis/build.gradle.kts b/mcspring-api/mcspring-guis/build.gradle.kts index 99b2209..a186c34 100644 --- a/mcspring-api/mcspring-guis/build.gradle.kts +++ b/mcspring-api/mcspring-guis/build.gradle.kts @@ -1,5 +1,4 @@ dependencies { - api("org.spigotmc:spigot-api") implementation(project(":mcspring-api:mcspring-chat")) implementation(project(":mcspring-api:mcspring-rx")) } diff --git a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/McSpringExtension.kt b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/McSpringExtension.kt index 6ff5139..cfbadcb 100644 --- a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/McSpringExtension.kt +++ b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/McSpringExtension.kt @@ -19,6 +19,11 @@ open class McSpringExtension(project: Project) { var pluginDatabase: Boolean? = null var pluginPrefix: String? = null var pluginLoadBefore: List? = null + var pluginApiVersion: String? = null + + fun latestApiVersion() { + pluginApiVersion = "1.13" + } enum class Load { STARTUP, POSTWORLD diff --git a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginJar.kt b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginJar.kt index 4b57b18..30df94e 100644 --- a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginJar.kt +++ b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginJar.kt @@ -49,6 +49,8 @@ open class BuildPluginJar : ShadowJar() { private fun setup() { entryCompression = ZipEntryCompression.STORED includeEmptyDirs = false + val name = project.extensions.mcspring.pluginName ?: project.name + archiveBaseName.set(name) archiveClassifier.set("") val springJar = project.buildDir / "libs" / "spring.jar" diff --git a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt index e42cb0a..6da907d 100644 --- a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt +++ b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt @@ -35,6 +35,7 @@ open class BuildPluginYml : DefaultTask() { fun writeNonNull(key: String, value: Any?) = value?.apply { attributes[key] = this } + writeNonNull("api-version", pluginApiVersion) writeNonNull("description", pluginDescription) writeNonNull("load", pluginLoad?.toString()?.toLowerCase()) writeNonNull("author", pluginAuthor) @@ -220,6 +221,7 @@ open class BuildPluginYml : DefaultTask() { logger.info("Plugin Database: $pluginDatabase") logger.info("Plugin Prefix: $pluginPrefix") logger.info("Plugin Load Before: $pluginLoadBefore") + logger.info("Plugin API Version: $pluginApiVersion") } } } From d1064e696e166a9ac65ac7165db00f79c769a4e3 Mon Sep 17 00:00:00 2001 From: kylepls Date: Sun, 12 Jul 2020 11:03:16 -0600 Subject: [PATCH 49/54] start e2e, misc fixes --- .../src/main/kotlin/mcspring-build.gradle.kts | 2 +- .../mcspring-chat-actions/build.gradle.kts | 4 + .../mcspring/chat/CommandActionListener.kt | 63 +++++++++ .../kotlin/in/kyle/mcspring/chat/Support.kt | 30 +++++ mcspring-api/mcspring-chat/build.gradle.kts | 2 - .../kotlin/in/kyle/mcspring/chat/Message.kt | 4 - mcspring-api/mcspring-e2e/build.gradle.kts | 27 ++++ .../kotlin/in/kyle/mcspring/e2e/FileDsl.kt | 29 +++++ .../in/kyle/mcspring/e2e/SpigotServerTest.kt | 120 ++++++++++++++++++ .../mcspring-gradle-plugin/build.gradle.kts | 14 +- .../kotlin/in/kyle/mcspring/BuildPlugin.kt | 28 ++++ .../kotlin/in/kyle/mcspring/Extensions.kt | 47 +++++++ .../in/kyle/mcspring/McSpringExtension.kt | 0 .../in/kyle/mcspring/tasks/BuildPluginJar.kt | 0 .../in/kyle/mcspring/tasks/BuildPluginYml.kt | 0 .../in/kyle/mcspring/tasks/DownloadJar.kt | 0 .../in/kyle/mcspring/tasks/SetupSpigot.kt | 0 .../src/main/resources/README.md | 0 .../src/main/resources/bukkit.yml | 0 .../src/main/resources/eula.txt | 0 .../src/main/resources/server.properties | 0 .../src/main/resources/spigot.yml | 0 .../kotlin/in/kyle/mcspring/GradleContext.kt | 0 .../in/kyle/mcspring/PluginTestSupport.kt | 0 .../in/kyle/mcspring/TestBuildPlugin.kt | 0 .../kyle/mcspring/tasks/TestBuildPluginJar.kt | 0 .../kyle/mcspring/tasks/TestBuildPluginYml.kt | 0 .../in/kyle/mcspring/tasks/TestDownloadJar.kt | 0 .../in/kyle/mcspring/tasks/TestSetupSpigot.kt | 0 .../main/kotlin/in/kyle/mcspring/guis/Gui.kt | 43 +++++-- .../in/kyle/mcspring/guis/chat/ChatGui.kt | 6 + .../mcspring/guis/invenotry/InventoryGui.kt | 24 +++- .../kotlin/in/kyle/mcspring/Extensions.kt | 23 ---- mcspring-examples/build.gradle.kts | 16 ++- .../mcspring-plugin-manager/build.gradle.kts | 9 +- .../controller/BukkitPluginUnloader.kt | 31 +++-- .../manager/controller/LogFileController.kt | 1 - .../manager/controller/PluginController.kt | 22 +++- .../manager/controller/PluginFileWatcher.kt | 47 +++++++ .../kotlin/in/kyle/mcspring/manager/Test.kt | 24 ++++ settings.gradle.kts | 6 +- 41 files changed, 554 insertions(+), 68 deletions(-) create mode 100644 mcspring-api/mcspring-chat-actions/build.gradle.kts create mode 100644 mcspring-api/mcspring-chat-actions/src/main/kotlin/in/kyle/mcspring/chat/CommandActionListener.kt create mode 100644 mcspring-api/mcspring-chat-actions/src/main/kotlin/in/kyle/mcspring/chat/Support.kt create mode 100644 mcspring-api/mcspring-e2e/build.gradle.kts create mode 100644 mcspring-api/mcspring-e2e/src/main/kotlin/in/kyle/mcspring/e2e/FileDsl.kt create mode 100644 mcspring-api/mcspring-e2e/src/main/kotlin/in/kyle/mcspring/e2e/SpigotServerTest.kt rename {mcspring-build => mcspring-api}/mcspring-gradle-plugin/build.gradle.kts (95%) rename {mcspring-build => mcspring-api}/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/BuildPlugin.kt (65%) create mode 100644 mcspring-api/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/Extensions.kt rename {mcspring-build => mcspring-api}/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/McSpringExtension.kt (100%) rename {mcspring-build => mcspring-api}/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginJar.kt (100%) rename {mcspring-build => mcspring-api}/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt (100%) rename {mcspring-build => mcspring-api}/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/DownloadJar.kt (100%) rename {mcspring-build => mcspring-api}/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/SetupSpigot.kt (100%) rename {mcspring-build => mcspring-api}/mcspring-gradle-plugin/src/main/resources/README.md (100%) rename {mcspring-build => mcspring-api}/mcspring-gradle-plugin/src/main/resources/bukkit.yml (100%) rename {mcspring-build => mcspring-api}/mcspring-gradle-plugin/src/main/resources/eula.txt (100%) rename {mcspring-build => mcspring-api}/mcspring-gradle-plugin/src/main/resources/server.properties (100%) rename {mcspring-build => mcspring-api}/mcspring-gradle-plugin/src/main/resources/spigot.yml (100%) rename {mcspring-build => mcspring-api}/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/GradleContext.kt (100%) rename {mcspring-build => mcspring-api}/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/PluginTestSupport.kt (100%) rename {mcspring-build => mcspring-api}/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/TestBuildPlugin.kt (100%) rename {mcspring-build => mcspring-api}/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestBuildPluginJar.kt (100%) rename {mcspring-build => mcspring-api}/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestBuildPluginYml.kt (100%) rename {mcspring-build => mcspring-api}/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestDownloadJar.kt (100%) rename {mcspring-build => mcspring-api}/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestSetupSpigot.kt (100%) delete mode 100644 mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/Extensions.kt create mode 100644 mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/PluginFileWatcher.kt create mode 100644 mcspring-examples/mcspring-plugin-manager/src/test/kotlin/in/kyle/mcspring/manager/Test.kt diff --git a/buildSrc/src/main/kotlin/mcspring-build.gradle.kts b/buildSrc/src/main/kotlin/mcspring-build.gradle.kts index 00d2fd7..94b4b29 100644 --- a/buildSrc/src/main/kotlin/mcspring-build.gradle.kts +++ b/buildSrc/src/main/kotlin/mcspring-build.gradle.kts @@ -15,13 +15,13 @@ repositories { dependencies { val spigotVersion = "1.15.2-R0.1-SNAPSHOT" compileOnly("org.spigotmc:spigot-api:$spigotVersion") - testImplementation("org.spigotmc:spigot-api:$spigotVersion") implementation(kotlin("stdlib")) testImplementation("org.mockito:mockito-core:2.+") testImplementation("io.mockk:mockk:1.10.0") testImplementation("org.junit.jupiter:junit-jupiter:5.6.2") + testImplementation("org.spigotmc:spigot-api:$spigotVersion") val kotestVersion = "4.1.0.RC2" testImplementation("io.kotest:kotest-runner-junit5-jvm:$kotestVersion") diff --git a/mcspring-api/mcspring-chat-actions/build.gradle.kts b/mcspring-api/mcspring-chat-actions/build.gradle.kts new file mode 100644 index 0000000..eee9b8b --- /dev/null +++ b/mcspring-api/mcspring-chat-actions/build.gradle.kts @@ -0,0 +1,4 @@ +dependencies { + compile(project(":mcspring-api:mcspring-chat")) + compile(project(":mcspring-api:mcspring-rx")) +} diff --git a/mcspring-api/mcspring-chat-actions/src/main/kotlin/in/kyle/mcspring/chat/CommandActionListener.kt b/mcspring-api/mcspring-chat-actions/src/main/kotlin/in/kyle/mcspring/chat/CommandActionListener.kt new file mode 100644 index 0000000..905f7ae --- /dev/null +++ b/mcspring-api/mcspring-chat-actions/src/main/kotlin/in/kyle/mcspring/chat/CommandActionListener.kt @@ -0,0 +1,63 @@ +package `in`.kyle.mcspring.chat + +import `in`.kyle.mcspring.rx.observeEvent +import `in`.kyle.mcspring.rx.syncScheduler +import io.reactivex.rxjava3.core.Observable +import io.reactivex.rxjava3.subjects.PublishSubject +import org.bukkit.entity.Player +import org.bukkit.event.player.AsyncPlayerChatEvent +import org.bukkit.event.player.PlayerCommandPreprocessEvent +import org.bukkit.plugin.java.JavaPlugin +import java.util.* +import java.util.concurrent.TimeUnit + +object CommandActionListener { + + private val actions = mutableMapOf>() + private val ttls = mutableMapOf() + private var registered = false + + fun createCommand(): Pair> { + if (!registered) { + registerListener() + registered = true + } + prune() + + val command = UUID.randomUUID().toString() + val subject = PublishSubject.create() + + actions[command] = subject + ttls[command] = System.currentTimeMillis() + + return Pair(command, subject.doOnDispose { unregisterCommand(command) }) + } + + fun unregisterCommand(string: String) { + actions.remove(string)?.onComplete() + ttls.remove(string) + } + + private fun prune() { + val toRemove = ttls.filterValues { (System.currentTimeMillis() - it) > TimeUnit.DAYS.toMillis(1) } + toRemove.keys.forEach { + unregisterCommand(it) + } + } + + private fun registerListener() { + val plugin = JavaPlugin.getProvidingPlugin(CommandActionListener::class.java) + plugin.observeEvent(PlayerCommandPreprocessEvent::class) + .filter { it.message.startsWith("/") } + .subscribe { + val command = it.message.substring(1) + val subject = actions[command] + if (subject != null) { + it.isCancelled = true + plugin.syncScheduler().scheduleDirect { + subject.onNext(it.player) + } + } + } + } +} diff --git a/mcspring-api/mcspring-chat-actions/src/main/kotlin/in/kyle/mcspring/chat/Support.kt b/mcspring-api/mcspring-chat-actions/src/main/kotlin/in/kyle/mcspring/chat/Support.kt new file mode 100644 index 0000000..903e1c4 --- /dev/null +++ b/mcspring-api/mcspring-chat-actions/src/main/kotlin/in/kyle/mcspring/chat/Support.kt @@ -0,0 +1,30 @@ +package `in`.kyle.mcspring.chat + +import `in`.kyle.mcspring.chat.StringSupport.toTextComponent +import `in`.kyle.mcspring.chat.TextComponentSupport.command +import net.md_5.bungee.api.chat.TextComponent +import org.bukkit.entity.Player + +infix fun TextComponent.onClick(lambda: (Player) -> Unit): TextComponent { + val (command, subject) = CommandActionListener.createCommand() + subject.subscribe { lambda(it) } + return this command "/$command" +} + +infix fun TextComponent.onClickOnce(lambda: (Player) -> Unit): TextComponent { + val (command, subject) = CommandActionListener.createCommand() + subject.subscribe { + lambda(it) + CommandActionListener.unregisterCommand(command) + } + return this command "/$command" +} + +infix fun String.onClick(lambda: (Player) -> Unit): TextComponent { + return toTextComponent() onClick lambda +} + +infix fun String.onClickOnce(lambda: (Player) -> Unit): TextComponent { + return toTextComponent() onClickOnce lambda +} + diff --git a/mcspring-api/mcspring-chat/build.gradle.kts b/mcspring-api/mcspring-chat/build.gradle.kts index 7d82dc7..e69de29 100644 --- a/mcspring-api/mcspring-chat/build.gradle.kts +++ b/mcspring-api/mcspring-chat/build.gradle.kts @@ -1,2 +0,0 @@ -dependencies { -} diff --git a/mcspring-api/mcspring-chat/src/main/kotlin/in/kyle/mcspring/chat/Message.kt b/mcspring-api/mcspring-chat/src/main/kotlin/in/kyle/mcspring/chat/Message.kt index 13a323b..f5961e9 100644 --- a/mcspring-api/mcspring-chat/src/main/kotlin/in/kyle/mcspring/chat/Message.kt +++ b/mcspring-api/mcspring-chat/src/main/kotlin/in/kyle/mcspring/chat/Message.kt @@ -42,10 +42,6 @@ object StringSupport { infix fun String.suggest(command: String): TextComponent { return toTextComponent() suggest command } - - operator fun String.plus(textComponent: TextComponent): TextComponent { - return TextComponent(toTextComponent(), textComponent) - } } object TextComponentSupport { diff --git a/mcspring-api/mcspring-e2e/build.gradle.kts b/mcspring-api/mcspring-e2e/build.gradle.kts new file mode 100644 index 0000000..e24116a --- /dev/null +++ b/mcspring-api/mcspring-e2e/build.gradle.kts @@ -0,0 +1,27 @@ +import java.net.URL + +val spigotVersion by extra { "1.16.1" } + +dependencies { + implementation("org.junit.jupiter:junit-jupiter:5.6.2") + implementation("io.mockk:mockk:1.10.0") + val paper = urlFile( + "https://papermc.io/api/v1/paper/$spigotVersion/latest/download", + "paper-$spigotVersion" + ) + compileOnly(paper) + runtimeOnly(paper) +} + +fun urlFile(url: String, name: String): ConfigurableFileCollection { + val file = File("$buildDir/downloads/${name}.jar") + file.parentFile.mkdirs() + if (!file.exists()) { + URL(url).openStream().use { downloadStream -> + file.outputStream().use { fileOut -> + downloadStream.copyTo(fileOut) + } + } + } + return files(file.absolutePath) +} diff --git a/mcspring-api/mcspring-e2e/src/main/kotlin/in/kyle/mcspring/e2e/FileDsl.kt b/mcspring-api/mcspring-e2e/src/main/kotlin/in/kyle/mcspring/e2e/FileDsl.kt new file mode 100644 index 0000000..c202dd3 --- /dev/null +++ b/mcspring-api/mcspring-e2e/src/main/kotlin/in/kyle/mcspring/e2e/FileDsl.kt @@ -0,0 +1,29 @@ +package `in`.kyle.mcspring.e2e + +import java.io.File + +class FileDsl(private val file: File) { + + fun delete(string: String) { + val toDelete = file.resolve(string) + if (toDelete.isFile) { + toDelete.delete() + } else { + toDelete.deleteRecursively() + } + } + + fun create(string: String) = file.apply { mkdirs() }.resolve(string).createNewFile() + + fun folder(string: String, lambda: FileDsl.() -> Unit) = FileDsl(file.resolve(string)).lambda() + + fun write(string: String) = file.writeText(string) + + operator fun String.unaryPlus() = create(this) + + operator fun String.unaryMinus() = delete(this) + + operator fun String.invoke(lambda: FileDsl.() -> Unit) = folder(this, lambda) +} + +operator fun File.invoke(lambda: FileDsl.() -> Unit) = FileDsl(this).lambda() diff --git a/mcspring-api/mcspring-e2e/src/main/kotlin/in/kyle/mcspring/e2e/SpigotServerTest.kt b/mcspring-api/mcspring-e2e/src/main/kotlin/in/kyle/mcspring/e2e/SpigotServerTest.kt new file mode 100644 index 0000000..fb80678 --- /dev/null +++ b/mcspring-api/mcspring-e2e/src/main/kotlin/in/kyle/mcspring/e2e/SpigotServerTest.kt @@ -0,0 +1,120 @@ +package `in`.kyle.mcspring.e2e + +import io.mockk.every +import io.mockk.mockk +import io.papermc.paperclip.Paperclip +import org.bukkit.Bukkit +import org.bukkit.plugin.Plugin +import org.bukkit.plugin.PluginDescriptionFile +import org.junit.jupiter.api.extension.* +import org.junit.platform.commons.JUnitException +import java.io.File +import java.lang.reflect.Method + +class SpigotServerTest : BeforeAllCallback, AfterAllCallback, InvocationInterceptor { + + override fun beforeAll(context: ExtensionContext) { + startSpigot() + } + + override fun afterAll(context: ExtensionContext) { + println("Shutting down") + Bukkit.getServer().shutdown() + cleanupFiles() + // let junit clean up the zombie threads + } + + override fun interceptTestMethod( + invocation: InvocationInterceptor.Invocation, + invocationContext: ReflectiveInvocationContext, + extensionContext: ExtensionContext? + ) { + val plugin = mockk() + every { plugin.isEnabled } returns true + every { plugin.name } returns "test-instance" + every { plugin.description } returns PluginDescriptionFile("test-instance", "0.0.1", "main-class") + every { plugin.logger } returns Bukkit.getLogger() + every { plugin.server } returns Bukkit.getServer() + + var hasRun = false + Bukkit.getServer().scheduler.runTask(plugin, Runnable { + invocation.proceed() + hasRun = true + }) + + while (!hasRun) { + Thread.sleep(1) + } + println("Run test") + } + + private fun startSpigot() { + try { + startSpigotServer() + waitForSpigotToStart() + } catch (e: Exception) { + throw JUnitException("Could not start Spigot server", e) + } + } + + private fun findSpigotMain(): Method { + return try { + Class.forName("org.bukkit.craftbukkit.Main") + .declaredMethods + .first { it.name == "main" } + } catch (e: ClassNotFoundException) { + error(""" + Could not find Spigot jar main class on the runtime classpath. + """.trimIndent()) + } + } + + private fun startSpigotServer() { + System.setProperty("IReallyKnowWhatIAmDoingISwear", "true") + System.setProperty("com.mojang.eula.agree", "true") + + val args = arrayOf("-nogui", "-o=false") + Thread(Runnable { + try { + Paperclip.main(args) + } catch (e: Exception) { + println(e) + } + }, "spigot-thread").start() + } + + private fun waitForSpigotToStart() { + @Suppress("UNNECESSARY_SAFE_CALL", "SENSELESS_COMPARISON") + while (Bukkit.getServer() == null || Bukkit.getWorlds().isEmpty()) { + Thread.sleep(1) + } + + println("Spigot Server ${Bukkit.getVersion()} ready") + } + + private fun cleanupFiles() { + File("")() { + -"logs" + -"plugins" + -"cache" + -"world" + -"banned-ips.json" + -"banned-players.json" + -"bukkit.yml" + -"commands.yml" + -"eula.txt" + -"help.yml" + -"ops.json" + -"paper.yml" + -"server.properties" + -"spigot.yml" + -"version_history.json" + -"whitelist.json" + -"permissions.yml" + -"usercache.json" + -"world_nether" + -"world_the_end" + -"crash-reports" + } + } +} diff --git a/mcspring-build/mcspring-gradle-plugin/build.gradle.kts b/mcspring-api/mcspring-gradle-plugin/build.gradle.kts similarity index 95% rename from mcspring-build/mcspring-gradle-plugin/build.gradle.kts rename to mcspring-api/mcspring-gradle-plugin/build.gradle.kts index cee74ca..5377c2e 100644 --- a/mcspring-build/mcspring-gradle-plugin/build.gradle.kts +++ b/mcspring-api/mcspring-gradle-plugin/build.gradle.kts @@ -4,14 +4,6 @@ plugins { id("java-gradle-plugin") } -group = "in.kyle.mcspring" -version = "0.0.2" - -repositories { - jcenter() - mavenCentral() -} - val createClasspathManifest = tasks.create("createClasspathManifest") { val outputDir = buildDir.resolve(name) @@ -66,3 +58,9 @@ dependencies { testRuntimeOnly(files(createClasspathManifest)) } + +tasks.withType() { + manifest { + attributes("Implementation-Version" to archiveVersion.get()) + } +} diff --git a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/BuildPlugin.kt b/mcspring-api/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/BuildPlugin.kt similarity index 65% rename from mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/BuildPlugin.kt rename to mcspring-api/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/BuildPlugin.kt index 2b0aeb4..dc29e97 100644 --- a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/BuildPlugin.kt +++ b/mcspring-api/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/BuildPlugin.kt @@ -4,6 +4,7 @@ import `in`.kyle.mcspring.tasks.BuildPluginJar import `in`.kyle.mcspring.tasks.BuildPluginYml import `in`.kyle.mcspring.tasks.DownloadJar import `in`.kyle.mcspring.tasks.SetupSpigot +import org.codehaus.groovy.ast.ClassHelper import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.tasks.Copy @@ -57,5 +58,32 @@ class BuildPlugin : Plugin { } project.tasks.named("jar") { onlyIf { false } } + + registerTestDependencies(project) + registerMcSpringDependencies(project) + } + + private fun registerTestDependencies(project: Project) { + project.afterEvaluate { + val extension = project.extensions.mcspring + project.dependencies.add( + "testImplementation", + project.paperRunner(extension.spigotVersion) + ) + } + } + + private fun registerMcSpringDependencies(project: Project) { + project.configurations.all { + resolutionStrategy.eachDependency { + if (requested.group == "in.kyle.mcspring" && requested.version?.isBlank() == true) { + val version = BuildPlugin::class.java.`package`.implementationVersion + useVersion(version) + because(""" + mcspring dependencies with missing versions are set to the build plugin's version. + """.trimIndent()) + } + } + } } } diff --git a/mcspring-api/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/Extensions.kt b/mcspring-api/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/Extensions.kt new file mode 100644 index 0000000..a4bd1f6 --- /dev/null +++ b/mcspring-api/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/Extensions.kt @@ -0,0 +1,47 @@ +package `in`.kyle.mcspring + +import org.gradle.api.Project +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.plugins.ExtensionContainer +import org.gradle.api.plugins.JavaPluginConvention +import org.gradle.api.tasks.SourceSet +import org.gradle.kotlin.dsl.DependencyHandlerScope +import org.gradle.kotlin.dsl.findByType +import java.io.File +import java.net.URL + +fun Project.getMainSourceSet(): SourceSet { + val convention = try { + project.convention.getPlugin(JavaPluginConvention::class.java) + } catch (e: IllegalStateException) { + error("Kotlin/Java/Other plugin not found. Make sure to add one of these.") + } + return convention.sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME) +} + +fun DependencyHandlerScope.mcspring(name: String) = "in.kyle.mcspring:mcspring-$name" + +fun Project.paperRunner(version: String): ConfigurableFileCollection { + return urlFile(this, + "https://papermc.io/api/v1/paper/$version/latest/download", + "paper-runner-$version") +} + +private fun urlFile(project: Project, url: String, name: String): ConfigurableFileCollection { + val file = File("${project.buildDir}/downloads/${name}.jar") + file.parentFile.mkdirs() + if (!file.exists()) { + URL(url).openStream().use { downloadStream -> + file.outputStream().use { fileOut -> + downloadStream.copyTo(fileOut) + } + } + } + return project.files(file.absolutePath) +} + +operator fun File.div(string: String) = resolve(string) + +val ExtensionContainer.mcspring: McSpringExtension + get() = findByType()!! + diff --git a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/McSpringExtension.kt b/mcspring-api/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/McSpringExtension.kt similarity index 100% rename from mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/McSpringExtension.kt rename to mcspring-api/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/McSpringExtension.kt diff --git a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginJar.kt b/mcspring-api/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginJar.kt similarity index 100% rename from mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginJar.kt rename to mcspring-api/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginJar.kt diff --git a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt b/mcspring-api/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt similarity index 100% rename from mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt rename to mcspring-api/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt diff --git a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/DownloadJar.kt b/mcspring-api/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/DownloadJar.kt similarity index 100% rename from mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/DownloadJar.kt rename to mcspring-api/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/DownloadJar.kt diff --git a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/SetupSpigot.kt b/mcspring-api/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/SetupSpigot.kt similarity index 100% rename from mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/SetupSpigot.kt rename to mcspring-api/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/SetupSpigot.kt diff --git a/mcspring-build/mcspring-gradle-plugin/src/main/resources/README.md b/mcspring-api/mcspring-gradle-plugin/src/main/resources/README.md similarity index 100% rename from mcspring-build/mcspring-gradle-plugin/src/main/resources/README.md rename to mcspring-api/mcspring-gradle-plugin/src/main/resources/README.md diff --git a/mcspring-build/mcspring-gradle-plugin/src/main/resources/bukkit.yml b/mcspring-api/mcspring-gradle-plugin/src/main/resources/bukkit.yml similarity index 100% rename from mcspring-build/mcspring-gradle-plugin/src/main/resources/bukkit.yml rename to mcspring-api/mcspring-gradle-plugin/src/main/resources/bukkit.yml diff --git a/mcspring-build/mcspring-gradle-plugin/src/main/resources/eula.txt b/mcspring-api/mcspring-gradle-plugin/src/main/resources/eula.txt similarity index 100% rename from mcspring-build/mcspring-gradle-plugin/src/main/resources/eula.txt rename to mcspring-api/mcspring-gradle-plugin/src/main/resources/eula.txt diff --git a/mcspring-build/mcspring-gradle-plugin/src/main/resources/server.properties b/mcspring-api/mcspring-gradle-plugin/src/main/resources/server.properties similarity index 100% rename from mcspring-build/mcspring-gradle-plugin/src/main/resources/server.properties rename to mcspring-api/mcspring-gradle-plugin/src/main/resources/server.properties diff --git a/mcspring-build/mcspring-gradle-plugin/src/main/resources/spigot.yml b/mcspring-api/mcspring-gradle-plugin/src/main/resources/spigot.yml similarity index 100% rename from mcspring-build/mcspring-gradle-plugin/src/main/resources/spigot.yml rename to mcspring-api/mcspring-gradle-plugin/src/main/resources/spigot.yml diff --git a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/GradleContext.kt b/mcspring-api/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/GradleContext.kt similarity index 100% rename from mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/GradleContext.kt rename to mcspring-api/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/GradleContext.kt diff --git a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/PluginTestSupport.kt b/mcspring-api/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/PluginTestSupport.kt similarity index 100% rename from mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/PluginTestSupport.kt rename to mcspring-api/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/PluginTestSupport.kt diff --git a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/TestBuildPlugin.kt b/mcspring-api/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/TestBuildPlugin.kt similarity index 100% rename from mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/TestBuildPlugin.kt rename to mcspring-api/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/TestBuildPlugin.kt diff --git a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestBuildPluginJar.kt b/mcspring-api/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestBuildPluginJar.kt similarity index 100% rename from mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestBuildPluginJar.kt rename to mcspring-api/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestBuildPluginJar.kt diff --git a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestBuildPluginYml.kt b/mcspring-api/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestBuildPluginYml.kt similarity index 100% rename from mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestBuildPluginYml.kt rename to mcspring-api/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestBuildPluginYml.kt diff --git a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestDownloadJar.kt b/mcspring-api/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestDownloadJar.kt similarity index 100% rename from mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestDownloadJar.kt rename to mcspring-api/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestDownloadJar.kt diff --git a/mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestSetupSpigot.kt b/mcspring-api/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestSetupSpigot.kt similarity index 100% rename from mcspring-build/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestSetupSpigot.kt rename to mcspring-api/mcspring-gradle-plugin/src/test/kotlin/in/kyle/mcspring/tasks/TestSetupSpigot.kt diff --git a/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/Gui.kt b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/Gui.kt index 1001b0f..85a4414 100644 --- a/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/Gui.kt +++ b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/Gui.kt @@ -28,32 +28,57 @@ object Gui { player: Player, title: String, size: InventorySize = InventorySize.ONE_LINE, + parent: ClickableGui? = null, lambda: GuiBuilder.() -> Unit ): Disposable { + require(parent?.isDisabled() ?: true) { "Cannot open GUIs over each other." } val gui = InventoryGui( size.slots, title, plugin, player, - currentGuis[player] + parent ) return setup(gui, lambda) } - fun hotbar(player: Player, lambda: GuiBuilder.() -> Unit): Disposable { - val gui = HotbarGui( - plugin, - player, - currentGuis[player] - ) + fun inventoryWithParent( + player: Player, + title: String, + size: InventorySize = InventorySize.ONE_LINE, + lambda: GuiBuilder.() -> Unit + ) = inventory(player, title, size, currentGuis[player], lambda) + + fun hotbar( + player: Player, + parent: ClickableGui? = null, + lambda: GuiBuilder.() -> Unit + ): Disposable { + require(parent?.isDisabled() ?: true) { "Cannot open GUIs over each other." } + val gui = HotbarGui(plugin, player, parent) return setup(gui, lambda) } - fun chat(player: Player, lambda: GuiBuilder.() -> Unit): Disposable { - val gui = ChatGui(plugin, player, currentGuis[player]) + fun hotbarWithParent( + player: Player, + lambda: GuiBuilder.() -> Unit + ) = hotbar(player, currentGuis[player], lambda) + + fun chat( + player: Player, + parent: ClickableGui? = null, + lambda: GuiBuilder.() -> Unit + ): Disposable { + require(parent?.isDisabled() ?: true) { "Cannot open GUIs over each other." } + val gui = ChatGui(plugin, player, parent) return setup(gui, lambda) } + fun chatWithParent( + player: Player, + lambda: GuiBuilder.() -> Unit + ) = chat(player, currentGuis[player], lambda) + @Suppress("UNCHECKED_CAST") private fun setup( gui: ClickableGui, diff --git a/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/chat/ChatGui.kt b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/chat/ChatGui.kt index ccf6a4a..9296881 100644 --- a/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/chat/ChatGui.kt +++ b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/chat/ChatGui.kt @@ -47,6 +47,12 @@ open class ChatGuiDrawer(val chat: ChatGui) : ClickContext(chat) { chat.actions.add(lambda) } + fun clear() { + repeat(101) { + chat.player.sendMessage(" ") + } + } + fun redraw() = chat.redraw() fun close() = chat.close() fun enable() = chat.enable() diff --git a/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/invenotry/InventoryGui.kt b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/invenotry/InventoryGui.kt index 8368d45..16c47dc 100644 --- a/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/invenotry/InventoryGui.kt +++ b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/invenotry/InventoryGui.kt @@ -5,17 +5,20 @@ import `in`.kyle.mcspring.guis.ClickContext import `in`.kyle.mcspring.guis.ClickableGui import `in`.kyle.mcspring.guis.item.ItemBuilder import `in`.kyle.mcspring.rx.observeEvent +import `in`.kyle.mcspring.rx.syncScheduler import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.Disposable import org.bukkit.Bukkit import org.bukkit.entity.Player import org.bukkit.event.inventory.ClickType.* import org.bukkit.event.inventory.InventoryClickEvent +import org.bukkit.event.inventory.InventoryCloseEvent import org.bukkit.event.inventory.InventoryDragEvent import org.bukkit.event.inventory.InventoryEvent import org.bukkit.inventory.Inventory import org.bukkit.inventory.ItemStack import org.bukkit.plugin.Plugin +import java.util.concurrent.TimeUnit class InventorySetup(gui: InventoryGui) : InventoryDrawer(gui) { val listenerSubscription: CompositeDisposable = gui.listenerSubscription @@ -108,6 +111,13 @@ class InventoryGui constructor( } } + override fun enable() { + if (player.openInventory.topInventory != bukkitInventory) { + player.openInventory(bukkitInventory) + } + super.enable() + } + override fun registerListeners(): CompositeDisposable { val listeners = CompositeDisposable() listeners.add(plugin.observeEvent(InventoryClickEvent::class) @@ -120,8 +130,16 @@ class InventoryGui constructor( }) listeners.add(plugin.observeEvent(InventoryDragEvent::class) - .filter { event -> event.inventory == bukkitInventory } + .filter { it.inventory == bukkitInventory } .subscribe { it.isCancelled = true }) + + listeners.add(plugin.observeEvent(InventoryCloseEvent::class) + .filter { it.player == player } + .subscribe { + listeners.add(plugin.syncScheduler().scheduleDirect({ + it.player.openInventory(bukkitInventory) + }, 100, TimeUnit.MILLISECONDS)) + }) return listeners } @@ -162,6 +180,10 @@ class InventoryItemBuilder { this.itemStack = ItemBuilder.create(lambda) } + fun itemStack(itemStack: ItemStack) { + this.itemStack = itemStack + } + fun onClick(lambda: ClickContext.() -> Unit) { click = lambda } diff --git a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/Extensions.kt b/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/Extensions.kt deleted file mode 100644 index cac93bd..0000000 --- a/mcspring-build/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/Extensions.kt +++ /dev/null @@ -1,23 +0,0 @@ -package `in`.kyle.mcspring - -import org.gradle.api.Project -import org.gradle.api.plugins.ExtensionContainer -import org.gradle.api.plugins.JavaPluginConvention -import org.gradle.api.tasks.SourceSet -import org.gradle.kotlin.dsl.findByType -import java.io.File - -fun Project.getMainSourceSet(): SourceSet { - val convention = try { - project.convention.getPlugin(JavaPluginConvention::class.java) - } catch (e: IllegalStateException) { - error("Kotlin/Java/Other plugin not found. Make sure to add one of these.") - } - return convention.sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME) -} - -operator fun File.div(string: String) = resolve(string) - -val ExtensionContainer.mcspring: McSpringExtension - get() = findByType()!! - diff --git a/mcspring-examples/build.gradle.kts b/mcspring-examples/build.gradle.kts index b9ec3a7..59c1f8d 100644 --- a/mcspring-examples/build.gradle.kts +++ b/mcspring-examples/build.gradle.kts @@ -1,6 +1,13 @@ plugins { kotlin("jvm") version "1.3.72" - id("in.kyle.mcspring") version "0.0.2" apply false + id("in.kyle.mcspring") version "0.1.0" apply false +} + +allprojects { + tasks.withType().configureEach { + kotlinOptions.suppressWarnings = true + kotlinOptions.jvmTarget = "1.8" + } } subprojects { @@ -18,5 +25,12 @@ subprojects { val spigotVersion = "1.15.2-R0.1-SNAPSHOT" compileOnly("org.spigotmc:spigot-api:$spigotVersion") implementation(kotlin("stdlib")) + + testImplementation("org.spigotmc:spigot-api:$spigotVersion") + testImplementation("org.junit.jupiter:junit-jupiter:5.6.2") + } + + tasks.test { + useJUnitPlatform() } } diff --git a/mcspring-examples/mcspring-plugin-manager/build.gradle.kts b/mcspring-examples/mcspring-plugin-manager/build.gradle.kts index aadd880..f6203fb 100644 --- a/mcspring-examples/mcspring-plugin-manager/build.gradle.kts +++ b/mcspring-examples/mcspring-plugin-manager/build.gradle.kts @@ -1,9 +1,12 @@ +import `in`.kyle.mcspring.mcspring + dependencies { - implementation("in.kyle.mcspring:mcspring-base:+") - implementation("in.kyle.mcspring:mcspring-commands-dsl:+") + implementation(mcspring("base")) + implementation(mcspring("commands-dsl")) + testImplementation(mcspring("e2e")) } mcspring { pluginAuthor = "kylepls" - pluginMainPackage = "test" + pluginMainPackage = "in.kyle.mcspring.pluginmanager" } diff --git a/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/BukkitPluginUnloader.kt b/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/BukkitPluginUnloader.kt index 490e7c8..fd61ace 100644 --- a/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/BukkitPluginUnloader.kt +++ b/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/BukkitPluginUnloader.kt @@ -4,34 +4,39 @@ import org.bukkit.command.Command import org.bukkit.command.CommandMap import org.bukkit.command.PluginCommand import org.bukkit.command.SimpleCommandMap +import org.bukkit.event.Event import org.bukkit.plugin.Plugin import org.bukkit.plugin.PluginManager +import org.bukkit.plugin.RegisteredListener import org.springframework.boot.autoconfigure.condition.ConditionalOnBean import org.springframework.context.annotation.Lazy import org.springframework.stereotype.Component import java.net.URLClassLoader +import java.util.* import javax.annotation.PostConstruct @Lazy @Component -@ConditionalOnBean(Plugin::class) class BukkitPluginUnloader( private val pluginManager: PluginManager, private val commandMap: CommandMap ) { - private val commands = mutableMapOf() - private val plugins = mutableListOf() - private val names = mutableMapOf() + private lateinit var commands: MutableMap + private lateinit var plugins: MutableList + private lateinit var names: MutableMap + private lateinit var listeners: MutableMap> @PostConstruct fun setup() { - plugins.addAll(getDeclaredField(pluginManager, "plugins")) - names.putAll(getDeclaredField(pluginManager, "lookupNames")) - val knownCommands = SimpleCommandMap::class.java.getDeclaredField("knownCommands") - knownCommands.isAccessible = true + plugins = getDeclaredField(pluginManager, "plugins") + names = getDeclaredField(pluginManager, "lookupNames") + listeners = getDeclaredField(pluginManager, "listeners") + val knownCommands = SimpleCommandMap::class.java.getDeclaredField("knownCommands").apply { + isAccessible = true + } @Suppress("UNCHECKED_CAST") - commands.putAll(knownCommands.get(commandMap) as Map) + commands = knownCommands.get(commandMap) as MutableMap } fun unload(plugin: Plugin): Boolean { @@ -39,6 +44,7 @@ class BukkitPluginUnloader( synchronized(pluginManager) { plugins.remove(plugin) names.remove(plugin.name) + unregisterListeners(plugin) unregisterCommands(plugin) closeClassLoader(plugin.javaClass.classLoader) } @@ -50,7 +56,12 @@ class BukkitPluginUnloader( val unregister = commands.entries .filter { (it.value as? PluginCommand)?.plugin === plugin } .toSet() - unregister.forEach { it.value.unregister(commandMap); commands.remove(it.key) } + unregister.forEach { it.value.unregister(commandMap) } + unregister.forEach { commands.remove(it.key) } + } + + private fun unregisterListeners(plugin: Plugin) { + listeners.entries.removeIf { entry -> entry.value.all { it.plugin == plugin } } } private fun closeClassLoader(classLoader: ClassLoader) { diff --git a/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/LogFileController.kt b/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/LogFileController.kt index 589ecb3..35a1833 100644 --- a/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/LogFileController.kt +++ b/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/LogFileController.kt @@ -8,7 +8,6 @@ import java.nio.file.Paths import javax.annotation.PostConstruct @Controller -@ConditionalOnBean(Plugin::class) internal class LogFileController { private val logsFolder = Paths.get("logs") diff --git a/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/PluginController.kt b/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/PluginController.kt index e1ef618..603a868 100644 --- a/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/PluginController.kt +++ b/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/PluginController.kt @@ -14,7 +14,6 @@ import java.util.logging.Logger import java.util.stream.Collectors @Controller -@ConditionalOnBean(Plugin::class) class PluginController( private val pluginManager: PluginManager, private val pluginLoader: PluginLoader, @@ -22,9 +21,10 @@ class PluginController( private val logger: Logger ) { + val pluginsFolder = Paths.get("plugins") + val loadablePlugins: Map get() { - val pluginsFolder = Paths.get("plugins") return Files.list(pluginsFolder) .filter { it.toString().endsWith(".jar") } .filter { getPluginName(it) != null } @@ -66,6 +66,24 @@ class PluginController( return false } + fun isPluginJar(jar: Path): Boolean { + return try { + pluginLoader.getPluginDescription(jar.toFile()) + true + } catch (_: InvalidDescriptionException) { + false + } + } + + fun reload(jar: Path) { + val description = pluginLoader.getPluginDescription(jar.toFile()) + val name = description.name + val plugin = getPlugin(name) + requireNotNull(plugin) {"Plugin $name is not loaded and therefore cannot be reloaded."} + unload(plugin) + load(jar) + } + fun getPlugin(name: String): Plugin? { return pluginManager.getPlugin(name) } diff --git a/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/PluginFileWatcher.kt b/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/PluginFileWatcher.kt new file mode 100644 index 0000000..805da49 --- /dev/null +++ b/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/PluginFileWatcher.kt @@ -0,0 +1,47 @@ +package `in`.kyle.mcspring.manager.controller + +import org.springframework.scheduling.annotation.Scheduled +import org.springframework.stereotype.Service +import java.nio.file.FileSystems +import java.nio.file.Path +import java.nio.file.StandardWatchEventKinds.ENTRY_CREATE +import java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY +import java.nio.file.WatchKey +import java.nio.file.WatchService +import javax.annotation.PostConstruct + +@Service +class PluginFileWatcher( + private val pluginController: PluginController +) { + + lateinit var watcher: WatchService + lateinit var key: WatchKey + + @PostConstruct + fun setup() { + startWatcher() + } + + private fun startWatcher() { + watcher = FileSystems.getDefault().newWatchService() + key = pluginController.pluginsFolder.register(watcher, ENTRY_CREATE, ENTRY_MODIFY) + } + + @Scheduled(fixedRate = 100) + fun pollWatcher() { + for (event in key.pollEvents()) { + val context = event.context() + require(context is Path) { "Context must be a path" } + + if (context.endsWith(".jar") && context == pluginController.pluginsFolder.resolve(context.fileName)) { + if (pluginController.isPluginJar(context)) { + when (event.kind()) { + ENTRY_CREATE -> pluginController.load(context) + ENTRY_MODIFY -> pluginController.reload(context) + } + } + } + } + } +} diff --git a/mcspring-examples/mcspring-plugin-manager/src/test/kotlin/in/kyle/mcspring/manager/Test.kt b/mcspring-examples/mcspring-plugin-manager/src/test/kotlin/in/kyle/mcspring/manager/Test.kt new file mode 100644 index 0000000..92afc12 --- /dev/null +++ b/mcspring-examples/mcspring-plugin-manager/src/test/kotlin/in/kyle/mcspring/manager/Test.kt @@ -0,0 +1,24 @@ +package `in`.kyle.mcspring.manager + +import `in`.kyle.mcspring.e2e.SpigotServerTest +import org.bukkit.Bukkit +import org.bukkit.Material +import org.bukkit.util.Vector +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +//@ExtendWith(SpigotServerTest::class) +class Test { + + @Test + fun testSetABlock() { +// val server = Bukkit.getServer() +// val world = server.worlds[0] +// +// val location = Vector(0, 0, 0).toLocation(world) +// location.block.type = Material.ORANGE_WOOL +// +// Assertions.assertEquals(Material.ORANGE_WOOL, location.block.type) + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 39f6ad6..dd969fb 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -11,9 +11,9 @@ include(":mcspring-api:mcspring-vault") include(":mcspring-api:mcspring-rx") include(":mcspring-api:mcspring-guis") include(":mcspring-api:mcspring-chat") - -include(":mcspring-build") -include(":mcspring-build:mcspring-gradle-plugin") +include(":mcspring-api:mcspring-chat-actions") +include(":mcspring-api:mcspring-gradle-plugin") +include(":mcspring-api:mcspring-e2e") gradleEnterprise { buildScan { From 46a27f13818c85c1818c433a4d5677ac2a1ffde4 Mon Sep 17 00:00:00 2001 From: kylepls Date: Thu, 30 Jul 2020 16:21:22 -0600 Subject: [PATCH 50/54] Updates to bean registration logic --- .../main/kotlin/in/kyle/mcspring/event/EventHandlerSupport.kt | 1 - .../src/main/kotlin/in/kyle/mcspring/event/EventService.kt | 1 - .../in/kyle/mcspring/scheduler/ScheduledAnnotationSupport.kt | 1 - .../main/kotlin/in/kyle/mcspring/scheduler/SchedulerService.kt | 1 - 4 files changed, 4 deletions(-) diff --git a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/event/EventHandlerSupport.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/event/EventHandlerSupport.kt index bb14b43..8b4946a 100644 --- a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/event/EventHandlerSupport.kt +++ b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/event/EventHandlerSupport.kt @@ -14,7 +14,6 @@ import org.springframework.stereotype.Component import java.lang.reflect.Method @Component -@ConditionalOnBean(Plugin::class) internal class EventHandlerSupport( private val eventService: EventService, private val scanner: SpringScanner diff --git a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/event/EventService.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/event/EventService.kt index ab6633a..189e660 100644 --- a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/event/EventService.kt +++ b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/event/EventService.kt @@ -13,7 +13,6 @@ import java.lang.reflect.Method @Lazy @Service -@ConditionalOnBean(Plugin::class) internal class EventService( private val server: Server, private val plugin: Plugin diff --git a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/scheduler/ScheduledAnnotationSupport.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/scheduler/ScheduledAnnotationSupport.kt index 782c77a..5350797 100644 --- a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/scheduler/ScheduledAnnotationSupport.kt +++ b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/scheduler/ScheduledAnnotationSupport.kt @@ -14,7 +14,6 @@ import java.util.concurrent.ScheduledFuture @Lazy @Component -@ConditionalOnBean(Plugin::class) internal class ScheduledAnnotationSupport( private val scheduler: SchedulerService ) : ThreadPoolTaskScheduler() { diff --git a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/scheduler/SchedulerService.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/scheduler/SchedulerService.kt index bab2e68..1d2b8b3 100644 --- a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/scheduler/SchedulerService.kt +++ b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/scheduler/SchedulerService.kt @@ -13,7 +13,6 @@ import org.springframework.stereotype.Service */ @Lazy @Service -@ConditionalOnBean(Plugin::class) class SchedulerService( private val scheduler: BukkitScheduler, private val plugin: Plugin From 83f61561cf168d8124ba8b9f8bec65d4cdfbbf4e Mon Sep 17 00:00:00 2001 From: kylepls Date: Thu, 30 Jul 2020 16:21:22 -0600 Subject: [PATCH 51/54] Updates to bean registration logic --- .../kotlin/in/kyle/mcspring/manager/commands/CommandPlugin.kt | 1 - .../kotlin/in/kyle/mcspring/manager/commands/CommandReload.kt | 1 - 2 files changed, 2 deletions(-) diff --git a/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandPlugin.kt b/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandPlugin.kt index 0f5596a..fc8f25f 100644 --- a/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandPlugin.kt +++ b/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandPlugin.kt @@ -10,7 +10,6 @@ import org.springframework.stereotype.Component import java.nio.file.Path @Component -@ConditionalOnBean(Plugin::class) internal class CommandPlugin(private val pluginController: PluginController) { @Command( diff --git a/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandReload.kt b/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandReload.kt index 90842b1..5bff977 100644 --- a/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandReload.kt +++ b/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/commands/CommandReload.kt @@ -9,7 +9,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean import org.springframework.stereotype.Component @Component -@ConditionalOnBean(Plugin::class) class CommandReload { @Command( From 331e5abf23ea801df414c8f59b223f26161dc8f9 Mon Sep 17 00:00:00 2001 From: kylepls Date: Thu, 30 Jul 2020 16:23:27 -0600 Subject: [PATCH 52/54] Add additional mapper to MapParser --- .../in/kyle/mcspring/commands/dsl/parsers/MapParser.kt | 4 +++- .../kyle/mcspring/commands/dsl/parsers/TestMapParser.kt | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/MapParser.kt b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/MapParser.kt index dc6cb68..867cb10 100644 --- a/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/MapParser.kt +++ b/mcspring-api/mcspring-commands-dsl/src/main/kotlin/in/kyle/mcspring/commands/dsl/parsers/MapParser.kt @@ -3,7 +3,9 @@ package `in`.kyle.mcspring.commands.dsl.parsers import `in`.kyle.mcspring.commands.dsl.CommandContext class MapParser(context: CommandContext, stringArg: String?) : BaseParser(context, stringArg) { - fun map(vararg pairs: Pair) = map(mapOf(*pairs)) + fun map(vararg pairs: Pair) = map(mapOf(*pairs)) fun map(map: Map) = mapped(map.keys) { map[it] } + fun map(mapper: (String) -> R?) = mapped(mapper = mapper) + } diff --git a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/TestMapParser.kt b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/TestMapParser.kt index 08c21ca..e4eebc2 100644 --- a/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/TestMapParser.kt +++ b/mcspring-api/mcspring-commands-dsl/src/test/kotlin/in/kyle/mcspring/commands/dsl/parsers/TestMapParser.kt @@ -54,4 +54,12 @@ class TestMapParser : FreeSpec({ } } shouldBe listOf("one", "two", "three") } + + "test map function" - { + makeBuilder("1").mapArg { + parser { + map { it.toIntOrNull() } + } + } shouldBe 1 + } }) From 20a4cf96eabf476c5e509bd61e7396678448d6b0 Mon Sep 17 00:00:00 2001 From: kylepls Date: Sun, 6 Sep 2020 11:54:34 -0600 Subject: [PATCH 53/54] Update inventory gui functionality --- .../kotlin/in/kyle/mcspring/chat/Message.kt | 45 +++- .../in/kyle/mcspring/tasks/BuildPluginJar.kt | 2 + mcspring-api/mcspring-guis/build.gradle.kts | 1 + .../in/kyle/mcspring/guis/FunctionalGui.kt | 41 ++++ .../main/kotlin/in/kyle/mcspring/guis/Gui.kt | 197 ------------------ .../kotlin/in/kyle/mcspring/guis/GuiBase.kt | 164 +++++++++++++++ .../in/kyle/mcspring/guis/GuiBuilder.kt | 18 ++ .../in/kyle/mcspring/guis/chat/ChatGui.kt | 54 ++--- .../mcspring/guis/chat/FunctionalChatGui.kt | 57 +++++ .../guis/hotbar/FunctionalHotbarGui.kt | 74 +++++++ .../in/kyle/mcspring/guis/hotbar/HotbarGui.kt | 134 ++++-------- .../guis/inventory/FunctionalInventoryGui.kt | 68 ++++++ .../{invenotry => inventory}/InventoryGui.kt | 188 ++++++++--------- .../InventoryTitleHelper.kt | 2 +- .../manager/controller/PluginController.kt | 1 - 15 files changed, 599 insertions(+), 447 deletions(-) create mode 100644 mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/FunctionalGui.kt delete mode 100644 mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/Gui.kt create mode 100644 mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/GuiBase.kt create mode 100644 mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/GuiBuilder.kt create mode 100644 mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/chat/FunctionalChatGui.kt create mode 100644 mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/hotbar/FunctionalHotbarGui.kt create mode 100644 mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/inventory/FunctionalInventoryGui.kt rename mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/{invenotry => inventory}/InventoryGui.kt (51%) rename mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/{invenotry => inventory}/InventoryTitleHelper.kt (99%) diff --git a/mcspring-api/mcspring-chat/src/main/kotlin/in/kyle/mcspring/chat/Message.kt b/mcspring-api/mcspring-chat/src/main/kotlin/in/kyle/mcspring/chat/Message.kt index f5961e9..e88f931 100644 --- a/mcspring-api/mcspring-chat/src/main/kotlin/in/kyle/mcspring/chat/Message.kt +++ b/mcspring-api/mcspring-chat/src/main/kotlin/in/kyle/mcspring/chat/Message.kt @@ -1,5 +1,7 @@ package `in`.kyle.mcspring.chat +import `in`.kyle.mcspring.chat.CommandSenderSupport.sendMessage +import `in`.kyle.mcspring.chat.StringSupport.toTextComponent import `in`.kyle.mcspring.chat.TextComponentSupport.color import `in`.kyle.mcspring.chat.TextComponentSupport.command import `in`.kyle.mcspring.chat.TextComponentSupport.hover @@ -11,7 +13,23 @@ import org.bukkit.ChatColor import org.bukkit.command.CommandSender import org.bukkit.entity.Player +object PlayerSupport { + fun Player.sendMessage(vararg components: Any) = (this as CommandSender).sendMessage(*components) + fun Player.sendMessage(vararg components: TextComponent) = (this as CommandSender).sendMessage(*components) + fun Player.sendMessage(lambda: () -> TextComponent) = (this as CommandSender).sendMessage(lambda) +} + object CommandSenderSupport { + fun CommandSender.sendMessage(vararg components: Any) { + sendMessage(*components.map(::convertComponent).toTypedArray()) + } + + private fun convertComponent(component: Any) = when (component) { + is TextComponent -> component + is String -> component.toTextComponent() + else -> error("Invalid component for message: ${component::class.simpleName}:$component. Only strings and TextComponents are supported") + } + fun CommandSender.sendMessage(vararg components: TextComponent) { spigot().sendMessage(*components) } @@ -47,12 +65,13 @@ object StringSupport { object TextComponentSupport { infix fun TextComponent.color(color: ChatColor): TextComponent { this.color = color.asBungee() - return this + return TextComponent(this).apply { this.color = color.asBungee() } } infix fun TextComponent.hover(hover: TextComponent): TextComponent { - this.hoverEvent = HoverEvent(HoverEvent.Action.SHOW_TEXT, arrayOf(hover)) - return this + return TextComponent(this).apply { + this.hoverEvent = HoverEvent(HoverEvent.Action.SHOW_TEXT, arrayOf(hover)) + } } infix fun TextComponent.hover(lambda: () -> TextComponent): TextComponent { @@ -60,26 +79,30 @@ object TextComponentSupport { } infix fun TextComponent.hover(hover: String): TextComponent { - val hoverComponent = TextComponent(hover) - this.hoverEvent = HoverEvent(HoverEvent.Action.SHOW_TEXT, arrayOf(hoverComponent)) - return this + return hover(TextComponent(hover)) } infix fun TextComponent.command(command: String): TextComponent { - this.clickEvent = ClickEvent(ClickEvent.Action.RUN_COMMAND, command) - return this + return TextComponent(this).apply { + this.clickEvent = ClickEvent(ClickEvent.Action.RUN_COMMAND, command) + } } infix fun TextComponent.suggest(command: String): TextComponent { - this.clickEvent = ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, command) - return this + return TextComponent(this).apply { + this.clickEvent = ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, command) + } } operator fun TextComponent.plus(text: String): TextComponent { - return TextComponent(this, TextComponent(text)) + return plus(TextComponent(text)) } operator fun TextComponent.plus(text: TextComponent): TextComponent { return TextComponent(this, text) } + + operator fun TextComponent.plus(texts: Collection): TextComponent { + return TextComponent(this, *texts.toTypedArray()) + } } diff --git a/mcspring-api/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginJar.kt b/mcspring-api/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginJar.kt index 30df94e..b4c879b 100644 --- a/mcspring-api/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginJar.kt +++ b/mcspring-api/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginJar.kt @@ -24,6 +24,8 @@ open class BuildPluginJar : ShadowJar() { mainClassName = "ignored" classpath(Callable { project.getMainSourceSet().runtimeClasspath }) + isExcludeDevtools = false + archiveFileName.set("spring.jar") dependsOn(project.tasks.named("classes")) } diff --git a/mcspring-api/mcspring-guis/build.gradle.kts b/mcspring-api/mcspring-guis/build.gradle.kts index a186c34..d072cf6 100644 --- a/mcspring-api/mcspring-guis/build.gradle.kts +++ b/mcspring-api/mcspring-guis/build.gradle.kts @@ -1,4 +1,5 @@ dependencies { + api("org.apache.logging.log4j:log4j-api-kotlin:1.0.0") implementation(project(":mcspring-api:mcspring-chat")) implementation(project(":mcspring-api:mcspring-rx")) } diff --git a/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/FunctionalGui.kt b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/FunctionalGui.kt new file mode 100644 index 0000000..f4dc7da --- /dev/null +++ b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/FunctionalGui.kt @@ -0,0 +1,41 @@ +package `in`.kyle.mcspring.guis + +import io.reactivex.rxjava3.disposables.CompositeDisposable +import org.bukkit.entity.Player +import org.bukkit.plugin.Plugin + +abstract class FunctionalGui( + val player: Player, + val plugin: Plugin +) { + + val redrawLambdas = mutableSetOf Unit>() + val setupLambdas = mutableSetOf Unit>() + + abstract val gui: GuiType + + protected abstract fun makeDrawer(): Drawer + protected abstract fun makeSetup(): Setup + + fun runSetupLambdas() { + val setup = makeSetup() + setupLambdas.forEach { setup.it() } + } + + fun runRedrawLambdas() { + val drawer = makeDrawer() + redrawLambdas.forEach { drawer.it() } + } + + fun open() = gui.open() +} + +interface FunctionalGuiSetup : FunctionalGuiDrawer { + val listenerSubscription: CompositeDisposable + val globalSubscription: CompositeDisposable +} + +interface FunctionalGuiDrawer { + val plugin: Plugin + val player: Player +} diff --git a/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/Gui.kt b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/Gui.kt deleted file mode 100644 index 85a4414..0000000 --- a/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/Gui.kt +++ /dev/null @@ -1,197 +0,0 @@ -package `in`.kyle.mcspring.guis - -import `in`.kyle.mcspring.guis.chat.ChatGui -import `in`.kyle.mcspring.guis.chat.ChatGuiDrawer -import `in`.kyle.mcspring.guis.chat.ChatGuiSetup -import `in`.kyle.mcspring.guis.hotbar.HotbarDrawer -import `in`.kyle.mcspring.guis.hotbar.HotbarGui -import `in`.kyle.mcspring.guis.hotbar.HotbarSetup -import `in`.kyle.mcspring.guis.invenotry.InventoryDrawer -import `in`.kyle.mcspring.guis.invenotry.InventoryGui -import `in`.kyle.mcspring.guis.invenotry.InventorySetup -import `in`.kyle.mcspring.guis.invenotry.InventorySize -import `in`.kyle.mcspring.rx.observeEvent -import io.reactivex.rxjava3.disposables.CompositeDisposable -import io.reactivex.rxjava3.disposables.Disposable -import org.bukkit.entity.Player -import org.bukkit.event.player.PlayerQuitEvent -import org.bukkit.event.server.PluginDisableEvent -import org.bukkit.plugin.Plugin -import org.bukkit.plugin.java.JavaPlugin - -object Gui { - - val plugin = JavaPlugin.getProvidingPlugin(InventoryGui::class.java) - private val currentGuis = mutableMapOf>() - - fun inventory( - player: Player, - title: String, - size: InventorySize = InventorySize.ONE_LINE, - parent: ClickableGui? = null, - lambda: GuiBuilder.() -> Unit - ): Disposable { - require(parent?.isDisabled() ?: true) { "Cannot open GUIs over each other." } - val gui = InventoryGui( - size.slots, - title, - plugin, - player, - parent - ) - return setup(gui, lambda) - } - - fun inventoryWithParent( - player: Player, - title: String, - size: InventorySize = InventorySize.ONE_LINE, - lambda: GuiBuilder.() -> Unit - ) = inventory(player, title, size, currentGuis[player], lambda) - - fun hotbar( - player: Player, - parent: ClickableGui? = null, - lambda: GuiBuilder.() -> Unit - ): Disposable { - require(parent?.isDisabled() ?: true) { "Cannot open GUIs over each other." } - val gui = HotbarGui(plugin, player, parent) - return setup(gui, lambda) - } - - fun hotbarWithParent( - player: Player, - lambda: GuiBuilder.() -> Unit - ) = hotbar(player, currentGuis[player], lambda) - - fun chat( - player: Player, - parent: ClickableGui? = null, - lambda: GuiBuilder.() -> Unit - ): Disposable { - require(parent?.isDisabled() ?: true) { "Cannot open GUIs over each other." } - val gui = ChatGui(plugin, player, parent) - return setup(gui, lambda) - } - - fun chatWithParent( - player: Player, - lambda: GuiBuilder.() -> Unit - ) = chat(player, currentGuis[player], lambda) - - @Suppress("UNCHECKED_CAST") - private fun setup( - gui: ClickableGui, - lambda: GuiBuilder.() -> Unit - ): Disposable { - val builder = GuiBuilder() - builder.lambda() - builder.setup?.apply { gui.setupLambdas.add(this) } - builder.redraw?.apply { gui.redrawLambdas.add(this) } - currentGuis[gui.player] = gui as ClickableGui - val sub = gui.open() - gui.globalSubscription.add(Disposable.fromRunnable { - if (!gui.isDisabled()) gui.disable() - currentGuis.remove(gui.player) - gui.parent?.apply { - enable() - currentGuis[player] = this - } - }) - return sub - } -} - -@Target(AnnotationTarget.CLASS) -annotation class DslMark - -@DslMark -class GuiBuilder { - internal var setup: (Setup.() -> Unit)? = null - internal var redraw: (Drawer.() -> Unit)? = null - - fun setup(lambda: Setup.() -> Unit) { - setup = lambda - } - - fun redraw(lambda: Drawer.() -> Unit) { - redraw = lambda - } -} - -abstract class ClickableGui( - val plugin: Plugin, - val player: Player, - val parent: ClickableGui? -) { - - lateinit var globalSubscription: CompositeDisposable - lateinit var listenerSubscription: CompositeDisposable - - val redrawLambdas = mutableSetOf Unit>() - val setupLambdas = mutableSetOf Unit>() - - protected abstract fun registerListeners(): CompositeDisposable - protected abstract fun makeDrawer(): Drawer - protected abstract fun makeSetup(): Setup - - abstract fun clear() - - open fun redraw() { - clear() - redrawLambdas.forEach { it(makeDrawer()) } - } - - open fun open(): Disposable { - require(!this::globalSubscription.isInitialized) { "GUI can only be opened once" } - require(parent?.isDisabled() - ?: true) { "Parent must be disabled/closed before a new GUI can be opened" } - globalSubscription = makeDefaultSubscription() - enable() - return globalSubscription - } - - fun isDisabled() = listenerSubscription.isDisposed - - open fun close() { - require(!globalSubscription.isDisposed) { "GUI already closed" } - globalSubscription.dispose() - } - - open fun enable() { - require(this::globalSubscription.isInitialized) { "GUI enable called before open" } - require(!this::listenerSubscription.isInitialized || listenerSubscription.isDisposed) { - "Cannot enable, already enabled" - } - listenerSubscription = registerListeners() - setupLambdas.forEach { it(makeSetup()) } - redraw() - } - - open fun disable() { - require(this::globalSubscription.isInitialized) { "GUI disable called before open" } - require(!listenerSubscription.isDisposed) { "Cannot disable, already disabled" } - listenerSubscription.dispose() - globalSubscription.remove(listenerSubscription) - clear() - } - - private fun makeDefaultSubscription(): CompositeDisposable { - val subscription = CompositeDisposable() - - subscription.add(plugin.observeEvent(PlayerQuitEvent::class) - .filter { it.player == player } - .subscribe { subscription.dispose() }) - - subscription.add(plugin.observeEvent(PluginDisableEvent::class) - .filter { it.plugin == plugin } - .subscribe { subscription.dispose() }) - - return subscription - } -} - -open class ClickContext(val gui: ClickableGui<*, *>) { - val listenerSubscription: CompositeDisposable = gui.listenerSubscription - val globalSubscription: CompositeDisposable = gui.globalSubscription -} diff --git a/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/GuiBase.kt b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/GuiBase.kt new file mode 100644 index 0000000..33e2e5a --- /dev/null +++ b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/GuiBase.kt @@ -0,0 +1,164 @@ +package `in`.kyle.mcspring.guis + +import `in`.kyle.mcspring.guis.chat.ChatGui +import `in`.kyle.mcspring.guis.hotbar.HotbarGui +import `in`.kyle.mcspring.guis.inventory.InventoryGui +import `in`.kyle.mcspring.guis.inventory.InventorySize +import `in`.kyle.mcspring.rx.observeEvent +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.disposables.Disposable +import org.apache.logging.log4j.kotlin.Logging +import org.bukkit.entity.Player +import org.bukkit.event.player.PlayerQuitEvent +import org.bukkit.event.server.PluginDisableEvent +import org.bukkit.plugin.Plugin +import org.bukkit.plugin.java.JavaPlugin + +object Gui : Logging { + + internal val currentGuis = mutableMapOf() + internal val plugin = JavaPlugin.getProvidingPlugin(InventoryGui::class.java) + + fun getActiveGui(player: Player): GuiBase? { + val parentGui = currentGuis[player] + logger.debug { "Active GUI lookup for $player: $parentGui" } + return parentGui + } + + @Suppress("UNCHECKED_CAST") + fun < + Drawer : FunctionalGuiDrawer, + Setup : FunctionalGuiSetup, + GuiType : GuiBase + > setup( + gui: FunctionalGui, + lambda: GuiBuilder.() -> Unit + ): Disposable { + val builder = GuiBuilder() + builder.lambda() + builder.setup?.apply { gui.setupLambdas.add(this) } + builder.redraw?.apply { gui.redrawLambdas.add(this) } + return gui.open() + } + + abstract class Chat( + plugin: Plugin, + player: Player, + parent: GuiBase? = getActiveGui(player) + ) : ChatGui(plugin, player, parent) + + abstract class Hotbar( + plugin: Plugin, + player: Player, + parent: GuiBase? = getActiveGui(player) + ) : HotbarGui(plugin, player, parent) + + abstract class Inventory( + plugin: Plugin, + player: Player, + title: String, + size: Int = InventorySize.ONE_LINE, + parent: GuiBase? = getActiveGui(player) + ) : InventoryGui(plugin, player, title, size, parent) +} + +abstract class GuiBase( + val plugin: Plugin, + val player: Player, + var parent: GuiBase? = null +) : Disposable, Logging { + + lateinit var globalSubscription: CompositeDisposable + lateinit var listenerSubscription: CompositeDisposable + + protected open fun initialize() {} + protected open fun setup() {} + protected abstract fun registerListeners(): CompositeDisposable + + abstract fun redraw() + abstract fun clear() + + open fun open(): Disposable { + logger.debug { "Opening gui for ${player.name}" } + require(!this::globalSubscription.isInitialized) { "GUI can only be opened once" } + if (parent == null) { + parent = Gui.currentGuis[player] + } + logger.debug { + parent?.let { "Created new GUI with parent ${it::class.simpleName}" } + ?: "Created new GUI with no parent" + } + require(parent?.isDisabled() + ?: true) { "Parent must be disabled/closed before a new GUI can be opened" } + globalSubscription = makeDefaultSubscription() + initialize() + enable() + return globalSubscription + } + + fun isDisabled() = listenerSubscription.isDisposed + + override fun isDisposed() = globalSubscription.isDisposed + + override fun dispose() = close() + + open fun close() { + logger.debug { "Closing gui for ${player.name}" } + require(!globalSubscription.isDisposed) { "GUI already closed" } + globalSubscription.dispose() + } + + open fun enable() { + logger.debug { "Enabling gui ${this::class.simpleName} for ${player.name}" } + logger.debug { "Setting current GUI for $player" } + Gui.currentGuis[player] = this + require(this::globalSubscription.isInitialized) { "GUI enable called before open" } + require(!this::listenerSubscription.isInitialized || listenerSubscription.isDisposed) { + "Cannot enable, already enabled" + } + listenerSubscription = registerListeners() + setup() + redraw() + } + + open fun disable() { + logger.debug { "Disabling gui ${this::class.simpleName} for ${player.name}" } + require(this::globalSubscription.isInitialized) { "GUI disable called before open" } + require(!listenerSubscription.isDisposed) { "Cannot disable, already disabled" } + listenerSubscription.dispose() + globalSubscription.remove(listenerSubscription) + clear() + } + + private fun makeDefaultSubscription(): CompositeDisposable { + val subscription = CompositeDisposable() + + subscription.add(plugin.observeEvent(PlayerQuitEvent::class) + .filter { it.player == player } + .subscribe { subscription.dispose() }) + + subscription.add(plugin.observeEvent(PluginDisableEvent::class) + .filter { it.plugin == plugin } + .subscribe { subscription.dispose() }) + + subscription.add(Disposable.fromRunnable { + logger.debug { "Running dispose for ${this::class.simpleName}: ${player.name}" } + if (!isDisabled()) { + logger.debug { "GUI ${this::class.simpleName} not disabled, disabling..." } + disable() + } + + logger.debug { "Removing current GUI ${this::class.simpleName} for $player" } + Gui.currentGuis.remove(player) + + val tempParent = parent + if (tempParent != null) { + logger.debug { "Parent GUI found, enabling ${tempParent::class.simpleName}..." } + tempParent.enable() + } else { + logger.debug { "No parent GUI, skipping parent enable" } + } + }) + return subscription + } +} diff --git a/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/GuiBuilder.kt b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/GuiBuilder.kt new file mode 100644 index 0000000..66059d6 --- /dev/null +++ b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/GuiBuilder.kt @@ -0,0 +1,18 @@ +package `in`.kyle.mcspring.guis + +@Target(AnnotationTarget.CLASS) +annotation class DslMark + +@DslMark +class GuiBuilder { + internal var setup: (Setup.() -> Unit)? = null + internal var redraw: (Drawer.() -> Unit)? = null + + fun setup(lambda: Setup.() -> Unit) { + setup = lambda + } + + fun redraw(lambda: Drawer.() -> Unit) { + redraw = lambda + } +} diff --git a/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/chat/ChatGui.kt b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/chat/ChatGui.kt index 9296881..dbe1d98 100644 --- a/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/chat/ChatGui.kt +++ b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/chat/ChatGui.kt @@ -1,7 +1,6 @@ package `in`.kyle.mcspring.guis.chat -import `in`.kyle.mcspring.guis.ClickContext -import `in`.kyle.mcspring.guis.ClickableGui +import `in`.kyle.mcspring.guis.GuiBase import `in`.kyle.mcspring.rx.observeEvent import `in`.kyle.mcspring.rx.syncScheduler import io.reactivex.rxjava3.disposables.CompositeDisposable @@ -9,54 +8,29 @@ import org.bukkit.entity.Player import org.bukkit.event.player.AsyncPlayerChatEvent import org.bukkit.plugin.Plugin -class ChatGui( - plugin: Plugin, - player: Player, - parent: ClickableGui? -) : ClickableGui(plugin, player, parent) { +abstract class ChatGui( + plugin: Plugin, + player: Player, + parent: GuiBase? = null +) : GuiBase(plugin, player, parent) { - val actions = mutableListOf<(String) -> Unit>() + abstract fun onMessage(message: String) override fun registerListeners(): CompositeDisposable { val subscription = CompositeDisposable() subscription.add(plugin.observeEvent(AsyncPlayerChatEvent::class) - .filter { it.player == player } - .observeOn(plugin.syncScheduler()) - .subscribeOn(plugin.syncScheduler()) - .subscribe { event -> - actions.toList().forEach { it(event.message) } - }) + .filter { it.player == player } + .doOnNext { it.isCancelled = true } + .observeOn(plugin.syncScheduler()) + .subscribeOn(plugin.syncScheduler()) + .map { it.message } + .subscribe(::onMessage)) return subscription } - override fun makeDrawer() = ChatGuiDrawer(this) - - override fun makeSetup() = ChatGuiSetup(this) - override fun clear() { - actions.clear() - repeat(100) { - player.sendMessage(" ") - } - } -} - -open class ChatGuiDrawer(val chat: ChatGui) : ClickContext(chat) { - - fun message(lambda: (String) -> Unit) { - chat.actions.add(lambda) - } - - fun clear() { repeat(101) { - chat.player.sendMessage(" ") + player.sendMessage(" ") } } - - fun redraw() = chat.redraw() - fun close() = chat.close() - fun enable() = chat.enable() - fun disable() = chat.disable() } - -class ChatGuiSetup(chat: ChatGui) : ChatGuiDrawer(chat) diff --git a/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/chat/FunctionalChatGui.kt b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/chat/FunctionalChatGui.kt new file mode 100644 index 0000000..00d938d --- /dev/null +++ b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/chat/FunctionalChatGui.kt @@ -0,0 +1,57 @@ +package `in`.kyle.mcspring.guis.chat + +import `in`.kyle.mcspring.guis.* +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.disposables.Disposable +import org.bukkit.entity.Player +import org.bukkit.plugin.Plugin + +class FunctionalChatGui( + plugin: Plugin, + player: Player, + parent: GuiBase? +) : FunctionalGui(player, plugin) { + + override val gui: ChatGui = object : ChatGui(plugin, player, parent) { + override fun onMessage(message: String) = messageLambdas.forEach { it(message) } + override fun setup() = runSetupLambdas() + override fun redraw() = runRedrawLambdas() + } + + internal val messageLambdas = mutableListOf<(String) -> Unit>() + + override fun makeDrawer() = ChatGuiDrawer(this) + + override fun makeSetup() = ChatGuiSetup(this) + +} + +open class ChatGuiDrawer(private val chat: FunctionalChatGui): FunctionalGuiDrawer { + + override val plugin = chat.plugin + override val player = chat.player + + fun message(lambda: (String) -> Unit) { + chat.messageLambdas.add(lambda) + } + + fun clear() = chat.gui.clear() + fun redraw() = chat.gui.redraw() + fun close() = chat.gui.close() + fun enable() = chat.gui.enable() + fun disable() = chat.gui.disable() + +} + +class ChatGuiSetup(chat: FunctionalChatGui) : ChatGuiDrawer(chat), FunctionalGuiSetup { + override val listenerSubscription = chat.gui.listenerSubscription + override val globalSubscription = chat.gui.globalSubscription +} + +fun Gui.chat( + player: Player, + lambda: GuiBuilder.() -> Unit +): Disposable { + val functionalChatGui = FunctionalChatGui(plugin, player, getActiveGui(player)) + return setup(functionalChatGui, lambda) +} diff --git a/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/hotbar/FunctionalHotbarGui.kt b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/hotbar/FunctionalHotbarGui.kt new file mode 100644 index 0000000..bc4aad7 --- /dev/null +++ b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/hotbar/FunctionalHotbarGui.kt @@ -0,0 +1,74 @@ +package `in`.kyle.mcspring.guis.hotbar + +import `in`.kyle.mcspring.guis.GuiBase +import `in`.kyle.mcspring.guis.FunctionalGui +import `in`.kyle.mcspring.guis.FunctionalGuiDrawer +import `in`.kyle.mcspring.guis.FunctionalGuiSetup +import `in`.kyle.mcspring.guis.item.ItemBuilder +import org.bukkit.entity.Player +import org.bukkit.inventory.ItemStack +import org.bukkit.plugin.Plugin + +class FunctionalHotbarGui( + plugin: Plugin, + player: Player, + parent: GuiBase? +) : FunctionalGui(player, plugin) { + + internal val actions = mutableMapOf Unit>() + + override val gui = object : HotbarGui(plugin, player, parent) { + override fun onClick(slot: Int): Boolean { + val action = actions[slot] + action?.invoke() + return action != null + } + + override fun setup() = runSetupLambdas() + override fun redraw() = runRedrawLambdas() + } + + override fun makeDrawer() = HotbarGuiDrawer(this) + override fun makeSetup() = HotbarGuiSetup(this) +} + +class HotbarButtonBuilder { + + internal lateinit var item: ItemStack + internal var click: (() -> Unit)? = null + + fun onClick(click: () -> Unit) { + this.click = click + } + + fun itemStack(lambda: ItemBuilder.() -> Unit) { + this.item = ItemBuilder.create(lambda) + } +} + + +open class HotbarGuiDrawer(private val hotbar: FunctionalHotbarGui) : FunctionalGuiDrawer { + + override val plugin = hotbar.plugin + override val player = hotbar.player + + fun button(slot: Int, lambda: HotbarButtonBuilder.() -> Unit) { + val builder = HotbarButtonBuilder().apply(lambda) + val click = builder.click + if (click != null) { + hotbar.actions[slot] = click + } + player.inventory.setItem(slot, builder.item) + } + + fun redraw() = hotbar.gui.redraw() + fun close() = hotbar.gui.close() + fun clear() = hotbar.gui.clear() + fun enable() = hotbar.gui.enable() + fun disable() = hotbar.gui.disable() +} + +class HotbarGuiSetup(hotbar: FunctionalHotbarGui) : HotbarGuiDrawer(hotbar), FunctionalGuiSetup { + override val listenerSubscription = hotbar.gui.listenerSubscription + override val globalSubscription = hotbar.gui.globalSubscription +} diff --git a/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/hotbar/HotbarGui.kt b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/hotbar/HotbarGui.kt index 81962dd..9ab87f1 100644 --- a/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/hotbar/HotbarGui.kt +++ b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/hotbar/HotbarGui.kt @@ -1,14 +1,12 @@ package `in`.kyle.mcspring.guis.hotbar -import `in`.kyle.mcspring.guis.ClickContext -import `in`.kyle.mcspring.guis.ClickableGui -import `in`.kyle.mcspring.guis.item.ItemBuilder +import `in`.kyle.mcspring.guis.GuiBase import `in`.kyle.mcspring.rx.observeEvent import io.reactivex.rxjava3.disposables.CompositeDisposable import org.bukkit.Material import org.bukkit.entity.Player import org.bukkit.event.Cancellable -import org.bukkit.event.block.Action.PHYSICAL +import org.bukkit.event.block.Action import org.bukkit.event.block.BlockBreakEvent import org.bukkit.event.block.BlockPlaceEvent import org.bukkit.event.inventory.InventoryClickEvent @@ -18,99 +16,20 @@ import org.bukkit.event.player.PlayerDropItemEvent import org.bukkit.event.player.PlayerInteractEvent import org.bukkit.inventory.ItemStack import org.bukkit.plugin.Plugin -import kotlin.collections.set -open class HotbarDrawer(val hotbar: HotbarGui) { +abstract class HotbarGui(plugin: Plugin, player: Player, parent: GuiBase?) : GuiBase(plugin, player, parent) { - val player = hotbar.player - - fun button(slot: Int, lambda: HotbarButtonBuilder.() -> Unit) { - val builder = HotbarButtonBuilder() - builder.lambda() - if (builder.click != null) { - hotbar.actions[slot] = builder.click!! - } - player.inventory.setItem(slot, builder.item) - } - - fun redraw() = hotbar.redraw() - - fun close() = hotbar.close() - - fun enable() = hotbar.enable() - - fun disable() = hotbar.disable() -} - -class HotbarSetup(hotbar: HotbarGui) : HotbarDrawer(hotbar) { - val listenerSubscription = hotbar.listenerSubscription - val globalSubscription = hotbar.globalSubscription -} - -class HotbarGui( - plugin: Plugin, player: Player, parent: ClickableGui? -) : ClickableGui(plugin, player, parent) { - - val actions = mutableMapOf Unit>() private var lastClickTime = -1L - override fun clear() { - actions.clear() - for (i in 0..8) { - player.inventory.setItem(i, null) - } - player.updateInventory() - } - - override fun redraw() { - super.redraw() - player.updateInventory() - } - private val clickEventHandler = { event: Cancellable -> val slot = player.inventory.heldItemSlot - if (slot in actions) { + if (notSpamClick()) { + onClick(slot) event.isCancelled = true - if (notSpamClick()) { - actions[slot]?.invoke(ClickContext(this)) - } } } - override fun registerListeners(): CompositeDisposable { - val listeners = CompositeDisposable() - listeners.add(plugin.observeEvent(PlayerDropItemEvent::class) - .filter { it.player == player } - .subscribe(clickEventHandler)) - - listeners.add(plugin.observeEvent(InventoryCreativeEvent::class, InventoryClickEvent::class) - .filter { player == it.whoClicked } - .filter { it.clickedInventory?.type == InventoryType.PLAYER } - .subscribe { - it.isCancelled = true - player.setItemOnCursor(ItemStack(Material.AIR)) - player.updateInventory() - }) - - listeners.add(plugin.observeEvent(PlayerInteractEvent::class) - .filter { player == it.player } - .filter { it.action !== PHYSICAL } - .subscribe(clickEventHandler)) - - listeners.add(plugin.observeEvent(BlockPlaceEvent::class) - .filter { it.player == player } - .subscribe(clickEventHandler)) - - listeners.add(plugin.observeEvent(BlockBreakEvent::class) - .filter { it.player == player } - .subscribe(clickEventHandler)) - - return listeners - } - - override fun makeDrawer() = HotbarDrawer(this) - - override fun makeSetup() = HotbarSetup(this) + abstract fun onClick(slot: Int): Boolean private fun notSpamClick(): Boolean { return if (lastClickTime == -1L) { @@ -126,19 +45,42 @@ class HotbarGui( } } } -} -class HotbarButtonBuilder { + override fun registerListeners(): CompositeDisposable { + val listeners = CompositeDisposable() + listeners.add(plugin.observeEvent(PlayerDropItemEvent::class) + .filter { it.player == player } + .subscribe(clickEventHandler)) + + listeners.add(plugin.observeEvent(InventoryCreativeEvent::class, InventoryClickEvent::class) + .filter { player == it.whoClicked } + .filter { it.clickedInventory?.type == InventoryType.PLAYER } + .subscribe { + it.isCancelled = true + player.setItemOnCursor(ItemStack(Material.AIR)) + player.updateInventory() + }) + + listeners.add(plugin.observeEvent(PlayerInteractEvent::class) + .filter { player == it.player } + .filter { it.action !== Action.PHYSICAL } + .subscribe(clickEventHandler)) - internal lateinit var item: ItemStack - internal var click: (ClickContext.() -> Unit)? = null + listeners.add(plugin.observeEvent(BlockPlaceEvent::class) + .filter { it.player == player } + .subscribe(clickEventHandler)) + + listeners.add(plugin.observeEvent(BlockBreakEvent::class) + .filter { it.player == player } + .subscribe(clickEventHandler)) - fun onClick(click: ClickContext.() -> Unit) { - this.click = click + return listeners } - fun itemStack(lambda: ItemBuilder.() -> Unit) { - this.item = ItemBuilder.create(lambda) + override fun clear() { + for (i in 0..8) { + player.inventory.setItem(i, null) + } + player.updateInventory() } } - diff --git a/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/inventory/FunctionalInventoryGui.kt b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/inventory/FunctionalInventoryGui.kt new file mode 100644 index 0000000..88e2367 --- /dev/null +++ b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/inventory/FunctionalInventoryGui.kt @@ -0,0 +1,68 @@ +package `in`.kyle.mcspring.guis.inventory + +import `in`.kyle.mcspring.guis.* +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.disposables.Disposable +import org.bukkit.entity.Player +import org.bukkit.plugin.Plugin + +class FunctionalInventoryGui( + player: Player, + title: String, + size: Int, + plugin: Plugin, + parent: GuiBase? +) : FunctionalGui(player, plugin) { + + internal val actions = mutableMapOf Unit>() + + override val gui = object : InventoryGui(plugin, player, title, size, parent) { + override fun onClick(slot: Int) = actions[slot]?.invoke() ?: Unit + override fun setup() = runSetupLambdas() + override fun redraw() = runRedrawLambdas() + } + + override fun makeDrawer() = InventoryGuiDrawer(this) + override fun makeSetup() = InventoryGuiSetup(this) +} + +class InventoryGuiSetup( + inventory: FunctionalInventoryGui +) : InventoryGuiDrawer(inventory), FunctionalGuiSetup { + override val listenerSubscription: CompositeDisposable = inventory.gui.listenerSubscription + override val globalSubscription: CompositeDisposable = inventory.gui.globalSubscription +} + +open class InventoryGuiDrawer(val inventory: FunctionalInventoryGui) : FunctionalGuiDrawer { + override val plugin = inventory.gui.plugin + override val player = inventory.player + + val lastItemIndex: Int + get() = inventory.gui.lastItemIndex + + fun button(x: Int, y: Int, lambda: InventoryItemBuilder.() -> Unit) = inventory.gui.button(x, y, lambda) + + fun button(slot: Int, lambda: InventoryItemBuilder.() -> Unit) = inventory.gui.button(slot, lambda) + + fun title(lambda: () -> String) { + inventory.gui.title = lambda() + } + + fun redraw() = inventory.gui.redraw() + fun close() = inventory.gui.close() + fun enable() = inventory.gui.enable() + fun disable(closeInventory: Boolean = true) = inventory.gui.disable(closeInventory) + fun closeInventoryOnDisable(boolean: Boolean) { + inventory.gui.closeInventoryOnDisable = boolean + } +} + +fun Gui.inventory( + player: Player, + title: String, + size: Int = InventorySize.ONE_LINE, + lambda: GuiBuilder.() -> Unit +): Disposable { + val functionalInventoryGui = FunctionalInventoryGui(player, title, size, plugin, getActiveGui(player)) + return setup(functionalInventoryGui, lambda) +} diff --git a/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/invenotry/InventoryGui.kt b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/inventory/InventoryGui.kt similarity index 51% rename from mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/invenotry/InventoryGui.kt rename to mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/inventory/InventoryGui.kt index 16c47dc..de11ea4 100644 --- a/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/invenotry/InventoryGui.kt +++ b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/inventory/InventoryGui.kt @@ -1,8 +1,7 @@ -package `in`.kyle.mcspring.guis.invenotry +package `in`.kyle.mcspring.guis.inventory import `in`.kyle.mcspring.chat.translateColorCodes -import `in`.kyle.mcspring.guis.ClickContext -import `in`.kyle.mcspring.guis.ClickableGui +import `in`.kyle.mcspring.guis.GuiBase import `in`.kyle.mcspring.guis.item.ItemBuilder import `in`.kyle.mcspring.rx.observeEvent import `in`.kyle.mcspring.rx.syncScheduler @@ -10,85 +9,46 @@ import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.Disposable import org.bukkit.Bukkit import org.bukkit.entity.Player -import org.bukkit.event.inventory.ClickType.* -import org.bukkit.event.inventory.InventoryClickEvent -import org.bukkit.event.inventory.InventoryCloseEvent -import org.bukkit.event.inventory.InventoryDragEvent -import org.bukkit.event.inventory.InventoryEvent +import org.bukkit.event.inventory.* import org.bukkit.inventory.Inventory import org.bukkit.inventory.ItemStack import org.bukkit.plugin.Plugin import java.util.concurrent.TimeUnit -class InventorySetup(gui: InventoryGui) : InventoryDrawer(gui) { - val listenerSubscription: CompositeDisposable = gui.listenerSubscription - val globalSubscription: CompositeDisposable = gui.globalSubscription -} +abstract class InventoryGui( + plugin: Plugin, + player: Player, + title: String, + val size: Int, + parent: GuiBase? = null +) : GuiBase(plugin, player, parent) { -open class InventoryDrawer(val inventory: InventoryGui) { - val player = inventory.player - val plugin = inventory.plugin - val lastItemIndex: Int by lazy { inventory.bukkitInventory.size - 1 } + private val validClicks = listOf(ClickType.LEFT, ClickType.RIGHT, ClickType.MIDDLE) - fun button(x: Int, y: Int, lambda: InventoryItemBuilder.() -> Unit) { - require(x < 9 && x > -1) { "x is out of range! $x" } - button(x + y * 9, lambda) - } + private val actions = mutableMapOf Unit>() - fun button(slot: Int, lambda: InventoryItemBuilder.() -> Unit) { - require(slot < inventory.bukkitInventory.size) { "Slot out of bounds $slot < ${inventory.bukkitInventory.size}" } - - val builder = InventoryItemBuilder() - builder.lambda() - - builder.click?.let { inventory.actions[slot] = it } - inventory.bukkitInventory.setItem(slot, builder.itemStack) - } - - fun redraw() = inventory.redraw() - fun close() = inventory.close() - fun title(lambda: () -> String) = inventory.title(lambda) - fun enable() = inventory.enable() - fun disable(closeInventory: Boolean = true) = inventory.disable(closeInventory) - fun closeInventoryOnDisable(boolean: Boolean) { inventory.closeInventoryOnDisable = boolean } -} - -class InventoryGui constructor( - val size: Int, - var title: String, - plugin: Plugin, - player: Player, - parent: ClickableGui? -) : ClickableGui(plugin, player, parent) { - - val actions = mutableMapOf Unit>() - - internal lateinit var bukkitInventory: Inventory + lateinit var bukkitInventory: Inventory var closeInventoryOnDisable = true - - init { - setupLambdas.add { - if (player.openInventory.topInventory != bukkitInventory) { - player.openInventory(bukkitInventory) - } + var title = title + set(value) { + updateViewingTitle(value) + field = value } - } + + val lastItemIndex: Int by lazy { bukkitInventory.size - 1 } + + abstract fun onClick(slot: Int) override fun close() { require(player.openInventory.topInventory == bukkitInventory) { "Inventory already closed" } super.close() } - fun title(title: () -> String) = - InventoryTitleHelper.sendInventoryTitle( - player, - title() - ) - override fun open(): Disposable { - if (parent is InventoryGui) { - title { title.translateColorCodes() } - bukkitInventory = parent.bukkitInventory + val tempParent = parent + if (tempParent is InventoryGui) { + updateViewingTitle(title) + bukkitInventory = tempParent.bukkitInventory } else { bukkitInventory = Bukkit.createInventory(player, size, title.translateColorCodes()) player.openInventory(bukkitInventory) @@ -97,13 +57,6 @@ class InventoryGui constructor( return super.open() } - fun disable(closeInventory: Boolean) { - val temp = closeInventoryOnDisable - closeInventoryOnDisable = closeInventory - disable() - closeInventoryOnDisable = temp - } - override fun disable() { super.disable() if (closeInventoryOnDisable) { @@ -111,6 +64,13 @@ class InventoryGui constructor( } } + fun disable(closeInventory: Boolean) { + val temp = closeInventoryOnDisable + closeInventoryOnDisable = closeInventory + disable() + closeInventoryOnDisable = temp + } + override fun enable() { if (player.openInventory.topInventory != bukkitInventory) { player.openInventory(bukkitInventory) @@ -118,20 +78,27 @@ class InventoryGui constructor( super.enable() } + override fun clear() { + bukkitInventory.clear() + player.updateInventory() + } + override fun registerListeners(): CompositeDisposable { val listeners = CompositeDisposable() listeners.add(plugin.observeEvent(InventoryClickEvent::class) - .filter { isCorrectPlayer(it) } - .subscribe { - if (it.click in listOf(LEFT, RIGHT, MIDDLE)) { - actions[it.rawSlot]?.invoke(ClickContext(this)) - } - it.isCancelled = true - }) + .filter { isCorrectPlayer(it) } + .subscribe { + if (it.click in validClicks) { + val slot = it.rawSlot + actions[slot]?.invoke() + onClick(slot) + } + it.isCancelled = true + }) listeners.add(plugin.observeEvent(InventoryDragEvent::class) - .filter { it.inventory == bukkitInventory } - .subscribe { it.isCancelled = true }) + .filter { it.inventory == bukkitInventory } + .subscribe { it.isCancelled = true }) listeners.add(plugin.observeEvent(InventoryCloseEvent::class) .filter { it.player == player } @@ -143,38 +110,39 @@ class InventoryGui constructor( return listeners } - private fun isCorrectPlayer(event: InventoryEvent): Boolean { - return event.inventory.holder != null - && event.inventory.holder == player - && this::bukkitInventory.isInitialized - && event.inventory == bukkitInventory + fun button(x: Int, y: Int, lambda: InventoryItemBuilder.() -> Unit) { + require(x < 9 && x > -1) { "x is out of range! $x" } + button(x + y * 9, lambda) } - override fun makeDrawer() = InventoryDrawer(this) + fun button(slot: Int, lambda: InventoryItemBuilder.() -> Unit) { + require(slot < bukkitInventory.size) { + "Slot out of bounds $slot < ${bukkitInventory.size}" + } - override fun makeSetup() = InventorySetup(this) + val builder = InventoryItemBuilder() + builder.lambda() - override fun clear() { - bukkitInventory.clear() - player.updateInventory() + builder.click?.let { actions[slot] = it } + bukkitInventory.setItem(slot, builder.itemStack) } -} - -enum class InventorySize { - ONE_LINE, TWO_LINE, THREE_LINE, FOUR_LINE, FIVE_LINE, SIX_LINE; - - val slots: Int = 9 * (ordinal + 1) + private fun isCorrectPlayer(event: InventoryEvent): Boolean { + return event.inventory.holder != null + && event.inventory.holder == player + && this::bukkitInventory.isInitialized + && event.inventory == bukkitInventory + } - companion object { - fun of(requiredSlots: Int) = values().first { it.slots >= requiredSlots } + private fun updateViewingTitle(title: String) { + InventoryTitleHelper.sendInventoryTitle(player, title.translateColorCodes()) } } class InventoryItemBuilder { internal var itemStack: ItemStack? = null - internal var click: (ClickContext.() -> Unit)? = null + internal var click: (() -> Unit)? = null fun itemStack(lambda: ItemBuilder.() -> Unit) { this.itemStack = ItemBuilder.create(lambda) @@ -184,8 +152,26 @@ class InventoryItemBuilder { this.itemStack = itemStack } - fun onClick(lambda: ClickContext.() -> Unit) { + fun onClick(lambda: () -> Unit) { click = lambda } } +@Suppress("MemberVisibilityCanBePrivate") +object InventorySize { + const val ONE_LINE = 9 * 1 + const val TWO_LINE = 9 * 2 + const val THREE_LINE = 9 * 3 + const val FOUR_LINE = 9 * 4 + const val FIVE_LINE = 9 * 5 + const val SIX_LINE = 9 * 6 + + fun of(requiredSlots: Int) = arrayOf( + ONE_LINE, + TWO_LINE, + THREE_LINE, + FOUR_LINE, + FIVE_LINE, + SIX_LINE + ).first { it >= requiredSlots } +} diff --git a/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/invenotry/InventoryTitleHelper.kt b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/inventory/InventoryTitleHelper.kt similarity index 99% rename from mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/invenotry/InventoryTitleHelper.kt rename to mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/inventory/InventoryTitleHelper.kt index 3aede52..ba7c414 100644 --- a/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/invenotry/InventoryTitleHelper.kt +++ b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/inventory/InventoryTitleHelper.kt @@ -1,6 +1,6 @@ @file:Suppress("LocalVariableName") -package `in`.kyle.mcspring.guis.invenotry +package `in`.kyle.mcspring.guis.inventory import com.google.common.base.Preconditions import org.bukkit.Bukkit diff --git a/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/PluginController.kt b/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/PluginController.kt index 603a868..b681bf4 100644 --- a/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/PluginController.kt +++ b/mcspring-examples/mcspring-plugin-manager/src/main/kotlin/in/kyle/mcspring/manager/controller/PluginController.kt @@ -4,7 +4,6 @@ import org.bukkit.plugin.InvalidDescriptionException import org.bukkit.plugin.Plugin import org.bukkit.plugin.PluginLoader import org.bukkit.plugin.PluginManager -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean import org.springframework.stereotype.Controller import java.nio.file.Files import java.nio.file.Path From c604c79c205e314260e681d16bdce105dad17f23 Mon Sep 17 00:00:00 2001 From: kylepls Date: Sun, 20 Feb 2022 15:50:28 -0800 Subject: [PATCH 54/54] Misc changes, make URLClassLoader 1.17 compatible --- README.md | 12 +- mcspring-api/mcspring-base/README.md | 33 +++ mcspring-api/mcspring-base/build.gradle.kts | 6 +- .../mcspring/javaplugin/SpringJavaPlugin.java | 83 ++++++-- .../scheduler/ScheduledAnnotationSupport.kt | 5 - mcspring-api/mcspring-chat-actions/README.md | 15 ++ mcspring-api/mcspring-chat/README.md | 2 +- mcspring-api/mcspring-commands-dsl/README.md | 2 +- mcspring-api/mcspring-e2e/README.md | 22 ++ .../in/kyle/mcspring/e2e/SpigotServerTest.kt | 2 +- .../in/kyle/mcspring/tasks/BuildPluginYml.kt | 83 ++++---- .../mcspring/guis/inventory/InventoryGui.kt | 2 +- mcspring-api/mcspring-nms/README.md | 66 ++++++ mcspring-api/mcspring-nms/build.gradle.kts | 5 + .../nms/MinecraftReflectionAnnotations.kt | 19 ++ .../mcspring/nms/MinecraftReflectionFacade.kt | 28 +++ .../mcspring/nms/internal/FriendlyStrings.kt | 17 ++ .../nms/internal/MinecraftPackages.kt | 37 ++++ ...necraftReflectionProxyInvocationHandler.kt | 127 ++++++++++++ .../internal/MinecraftReflectionProxyUtils.kt | 72 +++++++ .../ReflectionInvocationSearchCriteria.kt | 69 +++++++ mcspring-examples/build.gradle.kts | 6 +- settings.gradle.kts | 1 + src/orchid/resources/changelog/1.0.0.md | 3 - src/orchid/resources/config.yml | 48 ----- src/orchid/resources/homepage.md | 5 - src/orchid/resources/pages/changelog.md | 4 - src/orchid/resources/wiki/commands-api.md | 70 ------- src/orchid/resources/wiki/intro.md | 14 -- src/orchid/resources/wiki/setup.md | 29 --- src/orchid/resources/wiki/subcommands-api.md | 191 ------------------ src/orchid/resources/wiki/summary.md | 5 - 32 files changed, 644 insertions(+), 439 deletions(-) create mode 100644 mcspring-api/mcspring-chat-actions/README.md create mode 100644 mcspring-api/mcspring-e2e/README.md create mode 100644 mcspring-api/mcspring-nms/README.md create mode 100644 mcspring-api/mcspring-nms/build.gradle.kts create mode 100644 mcspring-api/mcspring-nms/src/main/kotlin/in/kyle/mcspring/nms/MinecraftReflectionAnnotations.kt create mode 100644 mcspring-api/mcspring-nms/src/main/kotlin/in/kyle/mcspring/nms/MinecraftReflectionFacade.kt create mode 100644 mcspring-api/mcspring-nms/src/main/kotlin/in/kyle/mcspring/nms/internal/FriendlyStrings.kt create mode 100644 mcspring-api/mcspring-nms/src/main/kotlin/in/kyle/mcspring/nms/internal/MinecraftPackages.kt create mode 100644 mcspring-api/mcspring-nms/src/main/kotlin/in/kyle/mcspring/nms/internal/MinecraftReflectionProxyInvocationHandler.kt create mode 100644 mcspring-api/mcspring-nms/src/main/kotlin/in/kyle/mcspring/nms/internal/MinecraftReflectionProxyUtils.kt create mode 100644 mcspring-api/mcspring-nms/src/main/kotlin/in/kyle/mcspring/nms/internal/ReflectionInvocationSearchCriteria.kt delete mode 100644 src/orchid/resources/changelog/1.0.0.md delete mode 100644 src/orchid/resources/config.yml delete mode 100644 src/orchid/resources/homepage.md delete mode 100644 src/orchid/resources/pages/changelog.md delete mode 100644 src/orchid/resources/wiki/commands-api.md delete mode 100644 src/orchid/resources/wiki/intro.md delete mode 100644 src/orchid/resources/wiki/setup.md delete mode 100644 src/orchid/resources/wiki/subcommands-api.md delete mode 100644 src/orchid/resources/wiki/summary.md diff --git a/README.md b/README.md index 6df1990..b6fb261 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Writing Bukkit plugins is a nightmare. I often lay awake in my bed late at night These are solved problems. Spring Boot took care of this issue ages ago. So how about we ditch this ridiculous programming model and hop on the Spring train. -```java +```kotlin @Component class Test { // We don't have to extend JavaPlugin. The plugin.yml is also generated for us. @@ -69,15 +69,13 @@ class Test { // We don't have to extend JavaPlugin. The plugin.yml is also gener * Schedulers are defined with `@Scheduler`. Another thing to schlep away somewhere. * `@EventHandler` now registers itself. About damn time. * Like money? Vault support is in the box `in.kyle.mcspring.economy.EconomyService` -* Want my hot take on sub-command handing? We've got you covered (see the wiki) +* Want my hot take on sub-command handling? We've got you covered (see the wiki) +* I've also added some other optionionated libraries to the project with some pretty neat functionality. Check out mcspring-chat-actions, mcspring-chat, mcspring-guis, mcspring-nms. ## Getting Started -I went ahead and wrote a full tutorial series for you newcomers. Get started [here](https://github.com/kylepls/mcspring/wiki/Getting-Setup) +Please use the simple-factions project as a reference. This can be found in the `/mcspring-examples` folder. -If you think you're too smart for the beginner tutorial; go to the -[wiki](https://github.com/kylepls/mcspring/wiki) and piece it together. - -If you're really really smart; check out the example plugins in the `mcspring-example` folder. +Each subproject has attached documentation. --- diff --git a/mcspring-api/mcspring-base/README.md b/mcspring-api/mcspring-base/README.md index e76db38..6776d17 100644 --- a/mcspring-api/mcspring-base/README.md +++ b/mcspring-api/mcspring-base/README.md @@ -1,3 +1,36 @@ mcspring-base + --- + _A lightweight Spring Boot wrapper for Bukkit_ + +This is the "core" of mcspring. Most of the magic sauce is in here (and the gradle plugin). + +* Adds support for `@Scheduled`, `@EventHandler` and plugin dependency annotations. +* Bootstraps Spring on top of the plugin system. + +### Quickstart + +#### Annotations: +* `@PluginDepend` - Add this annotation to a Spring component to add plugin dependencies to the generated `plugin.yml` +* `@SoftPluginDepend` - Same as above but for soft dependencies. + +#### Events: +Register a class as a component and event handlers will be automatically registered. + +```kotlin +@Component +class Demo { + @EventHandler + fun move(e: PlayerMoveEvent) { + println(e.getPlayer().getName().toString() + " moved") + } +} +``` + +#### Scheduling +There are 2 ways to schedule things depending on your use case. + +#1: Global Scheduling Annotation: See [The @Scheduled Annotation in Spring](https://www.baeldung.com/spring-scheduled-tasks) + +#2: A convenience wrapper for the Bukkit scheduler. See: in.kyle.mcspring.scheduler.SchedulerService. diff --git a/mcspring-api/mcspring-base/build.gradle.kts b/mcspring-api/mcspring-base/build.gradle.kts index 4ca660e..a5b344a 100644 --- a/mcspring-api/mcspring-base/build.gradle.kts +++ b/mcspring-api/mcspring-base/build.gradle.kts @@ -6,14 +6,18 @@ repositories { mavenCentral() } +configurations.getByName("api") { +// exclude("com.google.code.gson") +} + dependencies { // compileOnly("org.apache.logging.log4j:log4j-core:2.12.1") api(platform("org.springframework.boot:spring-boot-dependencies:2.3.1.RELEASE")) + api("org.springframework.boot:spring-boot-loader") { exclude("org.springframework.boot", "spring-boot-dependencies") } api("org.springframework.boot:spring-boot-starter") { -// exclude("org.springframework.boot", "spring-boot-starter-logging") exclude("org.springframework.boot", "spring-boot-dependencies") exclude("org.yaml", "snakeyaml") } diff --git a/mcspring-api/mcspring-base/src/main/java/org/springframework/boot/loader/in/kyle/mcspring/javaplugin/SpringJavaPlugin.java b/mcspring-api/mcspring-base/src/main/java/org/springframework/boot/loader/in/kyle/mcspring/javaplugin/SpringJavaPlugin.java index e3e86ca..678695a 100644 --- a/mcspring-api/mcspring-base/src/main/java/org/springframework/boot/loader/in/kyle/mcspring/javaplugin/SpringJavaPlugin.java +++ b/mcspring-api/mcspring-base/src/main/java/org/springframework/boot/loader/in/kyle/mcspring/javaplugin/SpringJavaPlugin.java @@ -1,26 +1,29 @@ // Package name for hiding the class in the final jar package org.springframework.boot.loader.in.kyle.mcspring.javaplugin; -import org.bukkit.plugin.java.JavaPlugin; -import org.springframework.boot.loader.JarLauncher; -import org.springframework.boot.loader.archive.Archive; - -import java.lang.reflect.Method; +import java.lang.reflect.Field; import java.net.URL; import java.net.URLClassLoader; +import java.util.Collection; import java.util.Iterator; +import org.bukkit.plugin.java.JavaPlugin; +import org.springframework.boot.loader.JarLauncher; +import org.springframework.boot.loader.archive.Archive; +import org.springframework.lang.NonNull; + import in.kyle.mcspring.SpringLoader; // This has to be written in Java for loading public class SpringJavaPlugin extends JavaPlugin { - + private SpringLoader impl; - + @Override public void onEnable() { try { new McSpringLoader().launch(getClassLoader()); + impl = new SpringLoader(this, getClassLoader()); impl.onEnable(); } catch (Exception e) { @@ -28,26 +31,78 @@ public void onEnable() { throw new RuntimeException(e); } } - + @Override public void onDisable() { impl.onDisable(); } - + static class McSpringLoader extends JarLauncher { public void launch(ClassLoader classLoader) throws Exception { Iterator activeArchives = getClassPathArchivesIterator(); - Method addURL = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); - addURL.setAccessible(true); - + // Method addURL = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); + // addURL.setAccessible(true); + UnsafeClassLoaderAccess unsafe = new UnsafeClassLoaderAccess((URLClassLoader) classLoader); while (activeArchives.hasNext()) { - addURL.invoke(classLoader, activeArchives.next().getUrl()); + // addURL.invoke(classLoader, activeArchives.next().getUrl()); + unsafe.addURL(activeArchives.next().getUrl()); } } - + @Override protected String getMainClass() { return ""; } } + + // https://github.com/slimjar/slimjar + private static class UnsafeClassLoaderAccess { + private static final sun.misc.Unsafe UNSAFE; + + static { + try { + Field unsafeField = sun.misc.Unsafe.class.getDeclaredField("theUnsafe"); + unsafeField.setAccessible(true); + UNSAFE = (sun.misc.Unsafe) unsafeField.get(null); + } catch (Throwable t) { + throw new Error("Unable to find unsafe", t); + } + } + + private final Collection unopenedURLs; + private final Collection pathURLs; + + @SuppressWarnings("unchecked") + UnsafeClassLoaderAccess(URLClassLoader classLoader) { + Collection unopenedURLs; + Collection pathURLs; + try { + Object ucp = fetchField(URLClassLoader.class, classLoader, "ucp"); + unopenedURLs = (Collection) fetchField(ucp.getClass(), ucp, "unopenedUrls"); + pathURLs = (Collection) fetchField(ucp.getClass(), ucp, "path"); + } catch (Throwable e) { + unopenedURLs = null; + pathURLs = null; + } + + this.unopenedURLs = unopenedURLs; + this.pathURLs = pathURLs; + } + + private static Object fetchField(final Class clazz, final Object object, final String name) + throws NoSuchFieldException { + Field field = clazz.getDeclaredField(name); + long offset = UNSAFE.objectFieldOffset(field); + return UNSAFE.getObject(object, offset); + } + + public void addURL(@NonNull URL url) { + if (this.unopenedURLs == null || this.pathURLs == null) { + throw new NullPointerException("unopenedURLs or pathURLs"); + } + + this.unopenedURLs.add(url); + this.pathURLs.add(url); + } + } } diff --git a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/scheduler/ScheduledAnnotationSupport.kt b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/scheduler/ScheduledAnnotationSupport.kt index 5350797..5c82071 100644 --- a/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/scheduler/ScheduledAnnotationSupport.kt +++ b/mcspring-api/mcspring-base/src/main/kotlin/in/kyle/mcspring/scheduler/ScheduledAnnotationSupport.kt @@ -1,15 +1,10 @@ package `in`.kyle.mcspring.scheduler -import org.bukkit.plugin.Plugin -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean import org.springframework.context.annotation.Lazy import org.springframework.scheduling.Trigger import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler import org.springframework.stereotype.Component -import org.springframework.util.concurrent.ListenableFuture import java.util.* -import java.util.concurrent.Callable -import java.util.concurrent.Future import java.util.concurrent.ScheduledFuture @Lazy diff --git a/mcspring-api/mcspring-chat-actions/README.md b/mcspring-api/mcspring-chat-actions/README.md new file mode 100644 index 0000000..14942dd --- /dev/null +++ b/mcspring-api/mcspring-chat-actions/README.md @@ -0,0 +1,15 @@ +mcspring-chat-actions +--- +Simplifies the process of adding interactable elements to chat messages. + +### Examples + +```kotlin +player.sendMessage { + "hover for details".red() hover { + ("click to run ".gray()) + ("/test".yellow()) + } command "/test" +} +``` + +_This may be used independently of mcspring_ diff --git a/mcspring-api/mcspring-chat/README.md b/mcspring-api/mcspring-chat/README.md index 0e32b36..b472dee 100644 --- a/mcspring-api/mcspring-chat/README.md +++ b/mcspring-api/mcspring-chat/README.md @@ -2,7 +2,7 @@ mcspring-chat --- Provides some basic wrappers around the TextComponent API -### Usage +### Examples ```kotlin player.sendMessage { diff --git a/mcspring-api/mcspring-commands-dsl/README.md b/mcspring-api/mcspring-commands-dsl/README.md index ed89097..c9df245 100644 --- a/mcspring-api/mcspring-commands-dsl/README.md +++ b/mcspring-api/mcspring-commands-dsl/README.md @@ -1,6 +1,6 @@ mcspring-commands-dsl --- -_A sane way to write commands_ +_My hot take on command parsing, completely optional._ ### Project Goals One of the worst parts of writing plugins is writing command parsers. diff --git a/mcspring-api/mcspring-e2e/README.md b/mcspring-api/mcspring-e2e/README.md new file mode 100644 index 0000000..2f5656a --- /dev/null +++ b/mcspring-api/mcspring-e2e/README.md @@ -0,0 +1,22 @@ +mcspring-e2e +--- +This is my attempt to create E2E testing for plugins. It's not complete, but it's a cool concept. +Maybe you should create a PR? + +### Examples + +This isn't complete yet, but the idea is the following: +1. Instrument a JUnit test to start a Spigot instance +```kotlin +class MyTest : SpigotServerTest { + @Test + fun test() { + // wait for Spigot to start + // attach and run some code as a plugin + // verify the behavior of your existing plugin + // kill Spigot + } +} +``` + +_This may be used independently of mcspring_ diff --git a/mcspring-api/mcspring-e2e/src/main/kotlin/in/kyle/mcspring/e2e/SpigotServerTest.kt b/mcspring-api/mcspring-e2e/src/main/kotlin/in/kyle/mcspring/e2e/SpigotServerTest.kt index fb80678..019b540 100644 --- a/mcspring-api/mcspring-e2e/src/main/kotlin/in/kyle/mcspring/e2e/SpigotServerTest.kt +++ b/mcspring-api/mcspring-e2e/src/main/kotlin/in/kyle/mcspring/e2e/SpigotServerTest.kt @@ -74,7 +74,7 @@ class SpigotServerTest : BeforeAllCallback, AfterAllCallback, InvocationIntercep System.setProperty("com.mojang.eula.agree", "true") val args = arrayOf("-nogui", "-o=false") - Thread(Runnable { + Thread({ try { Paperclip.main(args) } catch (e: Exception) { diff --git a/mcspring-api/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt b/mcspring-api/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt index 6da907d..a2c0aff 100644 --- a/mcspring-api/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt +++ b/mcspring-api/mcspring-gradle-plugin/src/main/kotlin/in/kyle/mcspring/tasks/BuildPluginYml.kt @@ -46,14 +46,16 @@ open class BuildPluginYml : DefaultTask() { writeNonNull("loadbefore", pluginLoadBefore) } + logger.info("Getting plugin sources, ${project.getMainSourceSet().runtimeClasspath.files}") + project.getMainSourceSet().runtimeClasspath.files - .apply { - val classLoader = URLClassLoader(this.map { it.toURI().toURL() }.toTypedArray()) - addDependencies(classLoader) - addCommands(classLoader) - addSpringBootMain(classLoader) - classLoader.close() - } + .apply { + val classLoader = URLClassLoader(this.map { it.toURI().toURL() }.toTypedArray()) + addDependencies(classLoader) + addCommands(classLoader) + addSpringBootMain(classLoader) + classLoader.close() + } writeYmlFile() } @@ -68,19 +70,23 @@ open class BuildPluginYml : DefaultTask() { val main = "$pluginMainPackage.SpringJavaPlugin" if (!name.matches("[a-zA-Z0-9_-]+".toRegex())) { - throw GradleException(""" + throw GradleException( + """ Invalid plugin name: $name The plugin name must consist of all alphanumeric characters and underscores (a-z,A-Z,0-9,_) Update the project name OR Update the generated name by setting the `pluginName` in the mcspring extension block. - """.trimIndent()) + """.trimIndent() + ) } - val pattern = "(\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*\\.)*\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*" + val pattern = + "(\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*\\.)*\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*" if (!main.matches(pattern.toRegex())) { - throw GradleException(""" + throw GradleException( + """ Invalid main class location: $main Refer to the Java specification for valid package/class names. The main class location is set using: {project group}.{project location}.SpringJavaPlugin @@ -88,7 +94,8 @@ open class BuildPluginYml : DefaultTask() { Update the project group and project name OR Update the generated main package by setting the `pluginMainPackage` in the mcspring extension block. - """.trimIndent()) + """.trimIndent() + ) } attributes["name"] = name @@ -106,29 +113,31 @@ open class BuildPluginYml : DefaultTask() { val nowFormatted = LocalDateTime.now().format(formatter) if (pluginYml.exists()) pluginYml.delete() pluginYml.createNewFile() - pluginYml.appendText(""" + pluginYml.appendText( + """ # File auto generated by mcspring on $nowFormatted # https://github.com/kylepls/mcspring - """.trimIndent()) + """.trimIndent() + ) pluginYml.appendText(Yaml().dumpAsMap(attributes)) } private fun addDependencies(classLoader: ClassLoader) { val scanResult = ClassGraph() - .overrideClassLoaders(classLoader) - .enableAnnotationInfo() - .scan() + .overrideClassLoaders(classLoader) + .enableAnnotationInfo() + .scan() scanResult.use { fun getPluginDependencies(annotation: String) = - scanResult.allClasses.filter { it.isStandardClass && it.hasAnnotation(annotation) } - .map { it.getAnnotationInfo(annotation).parameterValues } - .flatMap { (it["plugins"].value as Array).toList() } + scanResult.allClasses.filter { it.isStandardClass && it.hasAnnotation(annotation) } + .map { it.getAnnotationInfo(annotation).parameterValues } + .flatMap { (it["plugins"].value as Array).toList() } fun addAnnotationAttributeList(string: String, clazz: KClass<*>) = - getPluginDependencies(clazz.qualifiedName!!) - .takeIf { it.isNotEmpty() } - ?.apply { attributes[string] = this } + getPluginDependencies(clazz.qualifiedName!!) + .takeIf { it.isNotEmpty() } + ?.apply { attributes[string] = this } addAnnotationAttributeList("softdepend", SoftPluginDepend::class) addAnnotationAttributeList("depend", PluginDepend::class) @@ -137,19 +146,19 @@ open class BuildPluginYml : DefaultTask() { private fun addCommands(classLoader: ClassLoader) { val scanResult = ClassGraph() - .overrideClassLoaders(classLoader) - .enableAnnotationInfo() - .enableMethodInfo() - .enableClassInfo() - .scan() + .overrideClassLoaders(classLoader) + .enableAnnotationInfo() + .enableMethodInfo() + .enableClassInfo() + .scan() val annotations = scanResult.use { fun getAnnotations(annotation: KClass<*>): List { val methods = scanResult.getClassesWithMethodAnnotation(annotation.qualifiedName!!) - .flatMap { it.methodInfo.filter { it.hasAnnotation(annotation.qualifiedName!!) } } + .flatMap { it.methodInfo.filter { it.hasAnnotation(annotation.qualifiedName!!) } } val annotations = - methods.map { it.getAnnotationInfo(annotation.qualifiedName!!) } - .map { it.parameterValues } + methods.map { it.getAnnotationInfo(annotation.qualifiedName!!) } + .map { it.parameterValues } return annotations } @@ -183,14 +192,14 @@ open class BuildPluginYml : DefaultTask() { private fun addSpringBootMain(classLoader: ClassLoader) { val scanResult = ClassGraph() - .overrideClassLoaders(classLoader) - .enableAnnotationInfo() - .scan(4) + .overrideClassLoaders(classLoader) + .enableAnnotationInfo() + .scan(4) val mains = scanResult.use { scanResult - .allStandardClasses - .filter { it.hasAnnotation(SpringBootApplication::class.qualifiedName) } - .map { it.name } + .allStandardClasses + .filter { it.hasAnnotation(SpringBootApplication::class.qualifiedName) } + .map { it.name } } require(mains.size == 1) { """ diff --git a/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/inventory/InventoryGui.kt b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/inventory/InventoryGui.kt index de11ea4..1b713dc 100644 --- a/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/inventory/InventoryGui.kt +++ b/mcspring-api/mcspring-guis/src/main/kotlin/in/kyle/mcspring/guis/inventory/InventoryGui.kt @@ -37,7 +37,7 @@ abstract class InventoryGui( val lastItemIndex: Int by lazy { bukkitInventory.size - 1 } - abstract fun onClick(slot: Int) + open fun onClick(slot: Int) {} override fun close() { require(player.openInventory.topInventory == bukkitInventory) { "Inventory already closed" } diff --git a/mcspring-api/mcspring-nms/README.md b/mcspring-api/mcspring-nms/README.md new file mode 100644 index 0000000..dbeb67a --- /dev/null +++ b/mcspring-api/mcspring-nms/README.md @@ -0,0 +1,66 @@ +mcspring-guis +--- +Provides a straightforward NMS API. + +Here's the idea, we define an interface for the reflective access we need. Then we can use the interface as a proxy object to interact with the objects at runtime. + +### To use... +1. First define the object you want reflective access to, in this instance the OBC world. +2. Add a method that matches the method signature in the OBC class. This method can have another _accessor_ class in-place of the OBC/NMS class. + +```kotlin +@ForClass("{obc}.CraftWorld") // the path to the class, {obc} and {nms} are filled in at runtime +interface CraftWorldAccessor { + + fun save(any: Any): Any + + fun addEntity(entity: NmsEntityAccessor, reason: CreatureSpawnEvent.SpawnReason): Entity + + fun addEntity(entity: CraftEntityAccessor, reason: CreatureSpawnEvent.SpawnReason) = addEntity(entity.getHandle(), reason) + + fun createEntity(location: Location, entityClass: Class): NmsEntity + +} + +@ForClass("{obc}.entity.CraftEntity") +interface CraftEntity { + + fun save(): NbtTagCompound + + fun getNbtString() = save().toString() + + fun getHandle(): NmsEntity + + @StaticMethod + fun getEntity(server: Server, entity: NmsEntity): Entity + + @StaticMethod + fun getEntity(nmsEntity: NmsEntity): Entity = getEntity(Bukkit.getServer(), nmsEntity) + + @UnwrapMethod + fun unwrap(): Entity + +} + +// helper method, not needed +val World.craftWorld + get() = this.obc() +``` + +Then you just have to call the `obc/nms` or `obcStatic/nmsStatic` methods to obtain an instance of the accessor. + +```kotlin + fun makeEntityNms( + entityType: Class, + bukkitWorld: World, + nbt: NbtTagCompound? = null + ): T { + val craftWorld = bukkitWorld.obc() // get the OBC world + val nmsEntity = craftWorld.createEntity(bukkitWorld.spawnLocation, entityType) // call the createEntity method on the accessor object - calls the OBC method, returns an accessor object + if (nbt != null) { + nmsEntity.load(nbt) + } + val bukkitEntity = obcStatic().getEntity(nmsEntity) // obtains an accessor for calling static class methods + return bukkitEntity as T + } +``` diff --git a/mcspring-api/mcspring-nms/build.gradle.kts b/mcspring-api/mcspring-nms/build.gradle.kts new file mode 100644 index 0000000..10654dd --- /dev/null +++ b/mcspring-api/mcspring-nms/build.gradle.kts @@ -0,0 +1,5 @@ +plugins { + `kotlin-dsl` + id("com.gradle.plugin-publish") version "0.12.0" + id("java-gradle-plugin") +} diff --git a/mcspring-api/mcspring-nms/src/main/kotlin/in/kyle/mcspring/nms/MinecraftReflectionAnnotations.kt b/mcspring-api/mcspring-nms/src/main/kotlin/in/kyle/mcspring/nms/MinecraftReflectionAnnotations.kt new file mode 100644 index 0000000..cdeb7f8 --- /dev/null +++ b/mcspring-api/mcspring-nms/src/main/kotlin/in/kyle/mcspring/nms/MinecraftReflectionAnnotations.kt @@ -0,0 +1,19 @@ +package `in`.kyle.mcspring.nms + +import kotlin.reflect.KClass + +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.CLASS) +annotation class ForClass(val name: String = "", val clazz: KClass<*> = Any::class) + +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.FUNCTION) +annotation class ObfuscatedName + +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.FUNCTION) +annotation class StaticMethod + +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.FUNCTION) +annotation class UnwrapMethod diff --git a/mcspring-api/mcspring-nms/src/main/kotlin/in/kyle/mcspring/nms/MinecraftReflectionFacade.kt b/mcspring-api/mcspring-nms/src/main/kotlin/in/kyle/mcspring/nms/MinecraftReflectionFacade.kt new file mode 100644 index 0000000..47e9c73 --- /dev/null +++ b/mcspring-api/mcspring-nms/src/main/kotlin/in/kyle/mcspring/nms/MinecraftReflectionFacade.kt @@ -0,0 +1,28 @@ +package `in`.kyle.mcspring.nms + +import `in`.kyle.mcspring.nms.internal.MinecraftReflectionProxyUtils +import `in`.kyle.mcspring.nms.internal.MinecraftReflectionProxyUtils.tryUnwrapProxyArgs +import `in`.kyle.mcspring.nms.internal.MinecraftReflectionProxyUtils.unwrapReflectionProxyType +import `in`.kyle.mcspring.nms.internal.ReflectionInvocationSearchCriteria + +inline fun Any.obc(): T = this.nms() +inline fun Any.nms(): T = MinecraftReflectionProxyUtils.createProxy(this, T::class.java) + +object StaticObject + +inline fun obcStatic(): T = StaticObject.obc() +inline fun nmsStatic(): T = StaticObject.nms() + +inline fun obcConstructor(vararg args: Any) = nmsConstructor(args) +inline fun nmsConstructor(vararg args: Any): T { + val type = unwrapReflectionProxyType(T::class.java) + + val passArgs = tryUnwrapProxyArgs(args).requireNoNulls() + + val constructor = ReflectionInvocationSearchCriteria( + parameterCount = args.size, + parameterTypes = passArgs.map { it.javaClass } + ).matchSingleConstructor(type) + + return constructor.newInstance(*passArgs).nms() +} diff --git a/mcspring-api/mcspring-nms/src/main/kotlin/in/kyle/mcspring/nms/internal/FriendlyStrings.kt b/mcspring-api/mcspring-nms/src/main/kotlin/in/kyle/mcspring/nms/internal/FriendlyStrings.kt new file mode 100644 index 0000000..ef3647e --- /dev/null +++ b/mcspring-api/mcspring-nms/src/main/kotlin/in/kyle/mcspring/nms/internal/FriendlyStrings.kt @@ -0,0 +1,17 @@ +package `in`.kyle.mcspring.nms.internal + +import `in`.kyle.mcspring.nms.internal.MinecraftPackages.replacePackagesWithPrefix +import java.lang.reflect.Method + +internal object FriendlyStrings { + fun getMethodString(method: Method): String { + val returnType = replacePackagesWithPrefix(method.returnType.canonicalName) + val methodName = "${replacePackagesWithPrefix(method.declaringClass.canonicalName)}.${method.name}" + + val args = method.parameterTypes + .joinToString(", ") { replacePackagesWithPrefix(it.canonicalName) } + .let { "($it)" } + + return "$returnType $methodName$args" + } +} diff --git a/mcspring-api/mcspring-nms/src/main/kotlin/in/kyle/mcspring/nms/internal/MinecraftPackages.kt b/mcspring-api/mcspring-nms/src/main/kotlin/in/kyle/mcspring/nms/internal/MinecraftPackages.kt new file mode 100644 index 0000000..6536db2 --- /dev/null +++ b/mcspring-api/mcspring-nms/src/main/kotlin/in/kyle/mcspring/nms/internal/MinecraftPackages.kt @@ -0,0 +1,37 @@ +package `in`.kyle.mcspring.nms.internal + +import org.bukkit.Bukkit + +internal object MinecraftPackages { + + val OBC_PREFIX = Bukkit.getServer().javaClass.getPackage().name + val NMS_PREFIX = OBC_PREFIX.replace("org.bukkit.craftbukkit", "net.minecraft.server") + val VERSION = OBC_PREFIX.replace("org.bukkit.craftbukkit", "").replace(".", "") + + private val replaceVariables = mapOf( + "{obc}" to OBC_PREFIX, + "{nms}" to NMS_PREFIX, + "{version}" to VERSION + ) + + private val classCache = mutableMapOf>() + + fun getClass(nameWithVariables: String): Class<*> { + return classCache.getOrPut(nameWithVariables) { + val canonicalClass = replaceVariables.toList() + .fold(nameWithVariables) { acc, (variable, replacement) -> + acc.replace(variable, replacement) + } + Class.forName(canonicalClass) + } + } + + fun replacePackagesWithPrefix(string: String): String { + return replaceVariables.toList() + .associate { (key, value) -> value to key } + .toList() + .fold(string) { acc, (variable, replacement) -> + acc.replace(variable, replacement) + } + } +} diff --git a/mcspring-api/mcspring-nms/src/main/kotlin/in/kyle/mcspring/nms/internal/MinecraftReflectionProxyInvocationHandler.kt b/mcspring-api/mcspring-nms/src/main/kotlin/in/kyle/mcspring/nms/internal/MinecraftReflectionProxyInvocationHandler.kt new file mode 100644 index 0000000..4673808 --- /dev/null +++ b/mcspring-api/mcspring-nms/src/main/kotlin/in/kyle/mcspring/nms/internal/MinecraftReflectionProxyInvocationHandler.kt @@ -0,0 +1,127 @@ +package `in`.kyle.mcspring.nms.internal + +import `in`.kyle.mcspring.nms.ObfuscatedName +import `in`.kyle.mcspring.nms.StaticMethod +import `in`.kyle.mcspring.nms.UnwrapMethod +import `in`.kyle.mcspring.nms.internal.MinecraftReflectionProxyUtils.isReflectionProxyType +import `in`.kyle.mcspring.nms.internal.MinecraftReflectionProxyUtils.tryUnwrapProxyArgs +import `in`.kyle.mcspring.nms.internal.MinecraftReflectionProxyUtils.tryUnwrapProxyClass +import `in`.kyle.mcspring.nms.internal.MinecraftReflectionProxyUtils.unwrapReflectionProxyType +import java.lang.invoke.MethodHandles +import java.lang.reflect.InvocationHandler +import java.lang.reflect.InvocationTargetException +import java.lang.reflect.Method +import kotlin.reflect.KProperty1 +import kotlin.reflect.full.declaredMemberProperties +import kotlin.reflect.full.memberProperties +import kotlin.reflect.jvm.javaGetter + +internal class MinecraftReflectionProxyInvocationHandler( + val instance: Any, + private val interfaceClass: Class +) : InvocationHandler { + + init { + require(!MinecraftReflectionProxyUtils.isReflectionProxyInstance(instance)) { "Cannot create nested proxy types" } + } + + override fun invoke(proxy: Any, proxyMethod: Method, args: Array?): Any? { + return when { + proxyMethod.isDefault -> invokeDefaultMethod(proxy, proxyMethod, args) + proxyMethod.isAnnotationPresent(UnwrapMethod::class.java) -> instance + else -> callMethodOrProperty(proxyMethod, args) + } + } + + private fun callMethodOrProperty(proxyMethod: Method, args: Array?): Any? { + val kotlinProperty = findKotlinProperty(proxyMethod) + return if (kotlinProperty != null) { + val clazz = unwrapReflectionProxyType(interfaceClass) + val fieldType = tryUnwrapProxyClass(kotlinProperty.javaGetter!!.returnType) + val properties = + interfaceClass.kotlin.declaredMemberProperties.filter { + unwrapReflectionProxyType(it.javaGetter!!.returnType) == fieldType + } + val index = properties.indexOf(kotlinProperty) + require(index >= 0) { "Could not find property $kotlinProperty on kotlin class ${interfaceClass.kotlin}" } + val field = + clazz.declaredFields.filter { it.type == fieldType }.elementAtOrNull(index) + ?: error( + """ + ${index + 1} field(s) of type $fieldType not found on class $clazz + was expecting at least ${index + 1} + """.trimIndent() + ) + + field.isAccessible = true + if (proxyMethod.name.startsWith("get")) { + val result = field[instance] + return if (isReflectionProxyType(proxyMethod.returnType)) { + MinecraftReflectionProxyUtils.createProxy(result, proxyMethod.returnType) + } else { + result + } + } else { + field[instance] = args!![0] + } + } else { + callRealMethod(proxyMethod, args) + } + } + + private fun findKotlinProperty(proxyMethod: Method): KProperty1? { + return if (proxyMethod.name.length > 3) { + interfaceClass.kotlin.memberProperties.singleOrNull { + val propertyName = proxyMethod.name.substring(3) + it.name.equals(propertyName, ignoreCase = true) + } + } else { + null + } + } + + private fun callRealMethod(proxyMethod: Method, args: Array?): Any? { + val actualMethod = lookupRealMethod(proxyMethod) + val mappedArgs = tryUnwrapProxyArgs(args) + + return try { + val result = actualMethod.invoke(instance, *mappedArgs) + if (isReflectionProxyType(proxyMethod.returnType)) { + MinecraftReflectionProxyUtils.createProxy(result, proxyMethod.returnType) + } else { + result + } + } catch (e: Exception) { + val methodString = FriendlyStrings.getMethodString(actualMethod) + val argTypes = mappedArgs.map { it?.javaClass } + throw InvocationTargetException(e, "Could not invoke $methodString with $argTypes") + } + } + + private fun lookupRealMethod(proxyMethod: Method): Method { + val isIgnoreName = proxyMethod.isAnnotationPresent(ObfuscatedName::class.java) + val isStatic = proxyMethod.isAnnotationPresent(StaticMethod::class.java) + + val searchCriteria = ReflectionInvocationSearchCriteria( + proxyMethod.name.takeUnless { isIgnoreName }, + proxyMethod.parameterCount, + proxyMethod.returnType, + proxyMethod.parameterTypes.toList(), + isStatic + ) + + val clazz = unwrapReflectionProxyType(interfaceClass) + return searchCriteria.matchSingleMethod(clazz).apply { isAccessible = true } + } + + private fun invokeDefaultMethod(proxy: Any, method: Method, args: Array?): Any? { + method.isAccessible = true + return MethodHandles.privateLookupIn(interfaceClass, MethodHandles.lookup()) + .`in`(interfaceClass) + .unreflectSpecial(method, interfaceClass) + .bindTo(proxy) + .invokeWithArguments(args?.toList() ?: emptyList()) + } + + override fun toString() = "MinecraftReflection[$interfaceClass: $instance]" +} diff --git a/mcspring-api/mcspring-nms/src/main/kotlin/in/kyle/mcspring/nms/internal/MinecraftReflectionProxyUtils.kt b/mcspring-api/mcspring-nms/src/main/kotlin/in/kyle/mcspring/nms/internal/MinecraftReflectionProxyUtils.kt new file mode 100644 index 0000000..4a9abfa --- /dev/null +++ b/mcspring-api/mcspring-nms/src/main/kotlin/in/kyle/mcspring/nms/internal/MinecraftReflectionProxyUtils.kt @@ -0,0 +1,72 @@ +package `in`.kyle.mcspring.nms.internal + +import `in`.kyle.mcspring.nms.ForClass +import java.lang.reflect.Proxy + +object MinecraftReflectionProxyUtils { + + fun createProxy(obj: Any, clazz: Class): T { + val classLoader = object : Any() {}.javaClass.classLoader + @Suppress("UNCHECKED_CAST") + return Proxy.newProxyInstance( + classLoader, + arrayOf(clazz), + MinecraftReflectionProxyInvocationHandler(obj, clazz) + ) as T + } + + fun isReflectionProxyInstance(obj: Any) = try { + Proxy.getInvocationHandler(obj) is MinecraftReflectionProxyInvocationHandler<*> + } catch (_: IllegalArgumentException) { + false + } + + fun isReflectionProxyType(type: Class<*>) = type.isAnnotationPresent(ForClass::class.java) && type.isInterface + + fun unwrapReflectionProxyType(proxyType: Class<*>): Class<*> { + val forClassAnnotation = proxyType.getAnnotation(ForClass::class.java) + ?: error("ForClass annotation missing on ${proxyType.canonicalName}") + val className = forClassAnnotation.name + return if (className.isNotEmpty()) { + MinecraftPackages.getClass(className) + } else { + forClassAnnotation.clazz.java + } + } + + fun unwrapReflectionProxyInstance(proxy: Any): Any { + val invocationHandler = Proxy.getInvocationHandler(proxy) + return if (invocationHandler is MinecraftReflectionProxyInvocationHandler<*>) { + invocationHandler.instance + } else { + proxy + } + } + + fun tryUnwrapProxyArgs(args: Array?): Array { + return args?.map(MinecraftReflectionProxyUtils::tryUnwrapProxyArg) + ?.toTypedArray() + ?: emptyArray() + } + + private fun tryUnwrapProxyArg(arg: Any?): Any? { + if (arg != null) { + if (isReflectionProxyInstance(arg)) { + return unwrapReflectionProxyInstance(arg) + } + } + return arg + } + + fun tryUnwrapProxyClasses(classes: List>): List> { + return classes.map(this::tryUnwrapProxyClass) + } + + fun tryUnwrapProxyClass(clazz: Class<*>): Class<*> { + return if (isReflectionProxyType(clazz)) { + unwrapReflectionProxyType(clazz) + } else { + clazz + } + } +} diff --git a/mcspring-api/mcspring-nms/src/main/kotlin/in/kyle/mcspring/nms/internal/ReflectionInvocationSearchCriteria.kt b/mcspring-api/mcspring-nms/src/main/kotlin/in/kyle/mcspring/nms/internal/ReflectionInvocationSearchCriteria.kt new file mode 100644 index 0000000..a02eaf1 --- /dev/null +++ b/mcspring-api/mcspring-nms/src/main/kotlin/in/kyle/mcspring/nms/internal/ReflectionInvocationSearchCriteria.kt @@ -0,0 +1,69 @@ +package `in`.kyle.mcspring.nms.internal + +import java.lang.reflect.Constructor +import java.lang.reflect.Method +import java.lang.reflect.Modifier + +data class ReflectionInvocationSearchCriteria( + private val name: String? = null, + private val parameterCount: Int? = null, + private val returnType: Class<*>? = null, + private val parameterTypes: List>? = null, + private val static: Boolean? = null +) { + + fun matchSingleMethod(clazz: Class<*>): Method { + val matchMethods = matchMethods(clazz) + return matchMethods.singleOrNull() + ?: error("Did not find exact match for criteria: $this on ${clazz.name}, matches: $matchMethods") + } + + fun matchSingleConstructor(clazz: Class<*>): Constructor<*> { + val matchConstructors = matchConstructors(clazz) + return matchConstructors.singleOrNull() + ?: error("Did not find exact match for criteria: $this on ${clazz.name}, matches: $matchConstructors") + } + + fun matchMethods(clazz: Class<*>): List { + return clazz.declaredMethods.filter { matchesMethod(clazz, it) } + } + + fun matchConstructors(clazz: Class<*>): List> { + return clazz.declaredConstructors.filter(::matchesConstructor) + } + + fun matchesMethod(clazz: Class<*>, method: Method): Boolean { + return (name == null || method.name == name) + && (parameterCount == null || method.parameterCount == parameterCount) + && (returnType == null || typeMatches(returnType, method.returnType)) + && (parameterTypes == null || typeArraysMatch(parameterTypes, method.parameterTypes.toList())) + && (static == null || Modifier.isStatic(method.modifiers) == static) + && method.declaringClass == clazz + } + + fun matchesConstructor(constructor: Constructor<*>): Boolean { + require(name == null) { "Cannot have named constructor" } + require(returnType == null) { "Constructor return type implicitly given" } + return (parameterCount == null || constructor.parameterCount == parameterCount) + && (parameterTypes == null || typeArraysMatch(parameterTypes, constructor.parameterTypes.toList())) + } + + private fun typeMatches(proxyType: Class<*>, targetType: Class<*>): Boolean { + return typeArraysMatch(listOf(proxyType), listOf(targetType)) + } + + private fun typeArraysMatch(proxyTypes: List>, targetTypes: List>): Boolean { + return proxyTypes.zip(targetTypes) + .all { (proxyType, targetType) -> + if (MinecraftReflectionProxyUtils.isReflectionProxyType(proxyType)) { + targetType.isAssignableFrom( + MinecraftReflectionProxyUtils.unwrapReflectionProxyType( + proxyType + ) + ) + } else { + true + } + } + } +} diff --git a/mcspring-examples/build.gradle.kts b/mcspring-examples/build.gradle.kts index 59c1f8d..d9ab36d 100644 --- a/mcspring-examples/build.gradle.kts +++ b/mcspring-examples/build.gradle.kts @@ -1,3 +1,5 @@ +import org.jetbrains.kotlin.js.translate.context.Namer.kotlin + plugins { kotlin("jvm") version "1.3.72" id("in.kyle.mcspring") version "0.1.0" apply false @@ -24,10 +26,10 @@ subprojects { dependencies { val spigotVersion = "1.15.2-R0.1-SNAPSHOT" compileOnly("org.spigotmc:spigot-api:$spigotVersion") - implementation(kotlin("stdlib")) + implementation("org.jetbrains.kotlin:kotlin-stdlib:1.6.0") testImplementation("org.spigotmc:spigot-api:$spigotVersion") - testImplementation("org.junit.jupiter:junit-jupiter:5.6.2") + testImplementation("org.junit.jupiter:junit-jupiter:5.8.2") } tasks.test { diff --git a/settings.gradle.kts b/settings.gradle.kts index dd969fb..daee2ec 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -14,6 +14,7 @@ include(":mcspring-api:mcspring-chat") include(":mcspring-api:mcspring-chat-actions") include(":mcspring-api:mcspring-gradle-plugin") include(":mcspring-api:mcspring-e2e") +include(":mcspring-api:mcspring-nms") gradleEnterprise { buildScan { diff --git a/src/orchid/resources/changelog/1.0.0.md b/src/orchid/resources/changelog/1.0.0.md deleted file mode 100644 index fc1a599..0000000 --- a/src/orchid/resources/changelog/1.0.0.md +++ /dev/null @@ -1,3 +0,0 @@ -* Moved over to Kotlin -* Moved over to Gradle -* Revamped subcommand/tab handling diff --git a/src/orchid/resources/config.yml b/src/orchid/resources/config.yml deleted file mode 100644 index c1b245e..0000000 --- a/src/orchid/resources/config.yml +++ /dev/null @@ -1,48 +0,0 @@ -site: - about: - siteName: mcspring - siteDescription: Add Spring to any plugin -Editorial: - social: - github: 'kylepls/mcspring' - legacySearch: false - metaComponents: - - type: 'orchidSearch' - menu: - - type: 'separator' - title: 'Wiki' - - type: 'wiki' - - type: 'page' - itemId: 'Changelog' - - type: 'separator' - title: 'API Docs' - - type: 'kotlindocClasses' - moduleType: 'kotlindocClasses' - node: 'classes' - asSubmenu: true - submenuTitle: 'Classes' - - type: 'kotlindocPackages' - moduleType: 'kotlindoc' - node: 'packages' - asSubmenu: true - submenuTitle: 'Packages' - -kotlindoc: - sourceDirs: - - './../../../mcspring-api/mcspring-base/src/main/kotlin' - - './../../../mcspring-api/mcspring-subcommands/src/main/kotlin' - - './../../../mcspring-api/mcspring-vault/src/main/kotlin' - # modules: -# - name: 'mcspring-base' -# moduleGroup: 'group-one' -# sourceDirs: -# - './../../../../mcspring-api/mcspring-base/src/main/kotlin' -# - name: 'mcspring-subcommands' -# sourceDirs: -# - './../../../../../mcspring-api/mcspring-subcommands/src/main/kotlin' -# sourcePages: -# menu: -# - type: 'sourcedocPageLinks' -# moduleType: 'kotlindoc' -# itemTitleType: 'SIGNATURE' -# includeItems: true diff --git a/src/orchid/resources/homepage.md b/src/orchid/resources/homepage.md deleted file mode 100644 index 7678aae..0000000 --- a/src/orchid/resources/homepage.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -components: - - type: 'pageContent' - - type: 'readme' ---- diff --git a/src/orchid/resources/pages/changelog.md b/src/orchid/resources/pages/changelog.md deleted file mode 100644 index 6463454..0000000 --- a/src/orchid/resources/pages/changelog.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -components: - - type: changelog ---- diff --git a/src/orchid/resources/wiki/commands-api.md b/src/orchid/resources/wiki/commands-api.md deleted file mode 100644 index 04be492..0000000 --- a/src/orchid/resources/wiki/commands-api.md +++ /dev/null @@ -1,70 +0,0 @@ -## The Basics -Commands can be a pain in the ass to write. We tried our best to mitigate most of that pain. - -Annotating any method with `@Command(String)` inside a Spring bean will automatically register that command upon the plugin loading. If the method returns a value, that value will be converted to a string and sent to the CommandSender. There are also three command-specific injections: -* `Player` - The player that sent the command -* `String` - The label of the command -* `String[]` - The args array of the command - -These can be injected in any order or not injected at all if you don't need them. - -Here is an example command, we'll walk through what it actually does: - -```java -@Command("test-command") -public String test(String label, Plugin plugin, Player player) { - return String.format("The command label was '%s'\nThe plugin was '%s'\nThe sender was '%s'", label, plugin.getName(), player.getName(); -} -``` - -This command is run by typing `/test-command` in the console. `String label` corresponds to the label of the command. In this case, it's just `test-command`. `Plugin plugin` corresponds to the plugin the command is registered to. `Plugin` is defined as a Spring bean by mcspring so it can be automatically injected in this method. `Player player` corresponds to the player that sent the command (e.g.: the sender). - -The return type of this command is a String. So the output of this method is then sent to the command sender. - -_It should also be noted that this method may be public/package-protected/protected depending on your preference. It doesn't matter._ - -## Things To Watch Out For -There are some gimmicks to writing these commands. We're trying our best to get these polished out. - -### Do not duplicate `@Command` annotations - -Consider the following example: - -```java -@Command("test") -public String test(CommandSender sender) { - return "Only players can run this"; -} - -@Command("test") -public String test(Player player) { - ... -} -``` - -This will register just 1 command method. - -Instead do: - -```java -@Command("test") -public String test(CommandSender sender) { - if (sender instanceof Player) { - ... - } else { - return "Only players can execute this command"; - } -} -``` - -### The command injection only injects typed beans - -Beans that are referenced by `@Qualifier` and the like are not distinguished. Therefore you should avoid using named beans with command-method injection. - -**NON-WORKING EXAMPLE** -```java -@Command("test") -public String test(@Qualifier("my-bean-1") Api api1, @Qualifier("my-bean-2") Api api2) { - // api1 and api2 will not be injected properly -} -``` diff --git a/src/orchid/resources/wiki/intro.md b/src/orchid/resources/wiki/intro.md deleted file mode 100644 index 0d5512b..0000000 --- a/src/orchid/resources/wiki/intro.md +++ /dev/null @@ -1,14 +0,0 @@ -Long story short, don't make anything extend JavaPlugin and don't create a plugin.yml. - ---- - -### Extending JavaPlugin -You don't need a main class nor do you need to extend JavaPlugin. I know what you're thinking, "what about onEnable/onDisable?" We have you covered. -Instead of using the `onEnable` and `onDisable` methods use the `@PostConstract` and `@PreDestroy` annotations on any method anywhere in the project. You can make as many of these methods as you like. I suggest you create one on every Spring bean that needs something setup (e.g.: connecting to Mongo/MySQL). - -### The plugin.yml -Don't bother with creating `plugin.yml` either, mcspring will take care of that. - -If you need to add a dependency to the `plugin.yml` instead use the `@PluginDepends` annotation somewhere in your project and it will be automatically added for you. - -If you need to set the plugin version, name, or description add the `@SpringPlugin` annotation to a class somewhere inside your plugin. diff --git a/src/orchid/resources/wiki/setup.md b/src/orchid/resources/wiki/setup.md deleted file mode 100644 index edb0045..0000000 --- a/src/orchid/resources/wiki/setup.md +++ /dev/null @@ -1,29 +0,0 @@ -I tried to make this as easy as possible. If you're using IntelliJ just do the following. - -1. Create a new project -2. In the create dialog select `Maven -> create from archetype` -3. Click `Add archetype` on the right-hand side -4. Type in the following: - - 1. group: `in.kyle.mcspring` - 2. artifact: `mcspring-starter` - 3. version: `0.0.7` <-- Change this to the latest release over [here](https://github.com/kylepls/mcspring/releases) if I forget to update it. - -Then a project will be created for you with a project-specific Spigot folder already set up for you. - -Next: - -1. Run a Maven install to create the required files -2. Create a new JAR run configuration -3. Select the downloaded `spigot.jar` in the `spigot` folder as the target -4. Change the run environment to the `spigot` folder -5. Add the following **VM flag** `-DIReallyKnowWhatIAmDoingISwear` -6. Add the following **Program Argument** `--nogui` - -![](https://i.imgur.com/waoHVTz.png) - -7. To update the plugin on the server, just run a maven install and restart the server. - ---- - -Now that you're all set up, head over [here](https://github.com/kylepls/mcspring/wiki/The-Commands-API) and read about how to define commands in mcspring. Also, head over [here](https://github.com/kylepls/mcspring/wiki/The-plugin.yml-and-Extending-JavaPlugin) to read about what to do now that you don't have a main class or plugin.yml. diff --git a/src/orchid/resources/wiki/subcommands-api.md b/src/orchid/resources/wiki/subcommands-api.md deleted file mode 100644 index 41fcbcc..0000000 --- a/src/orchid/resources/wiki/subcommands-api.md +++ /dev/null @@ -1,191 +0,0 @@ -## Getting Setup -Writing sub-commands is #1 on my list of things that are tedious and annoying. How about we change that? - -mcspring has an optional dependency called `mcspring-subcommands`. If it is not already; you can add it to your project with the following dependency. **MAKE SURE TO REPLACE THE VERSION WITH YOUR MCSPRING VERSION** - -```xml - - in.kyle.mcspring - mcspring-subcommands - REPLACE WITH X.X.X - -``` - -Now we're ready to begin. - -## The Basics - -Sub-commands are defined in two parts. Firstly, there is the structure of the command. This defines how the command is read in and performs validation on the input. Secondly, there is the executor. This takes in the parsed values and does something on the server. If it's not clear, don't worry, just keep reading and the examples will do the talking. - -For this example, we will make a plot command to manage a plot plugin. This will look something like the following: -* `/plot ` -* `/plot create` -* `/plot add ` -* `/plot tp ` - -### The root method -Firstly, you will start with an `@Command` annotated method. This will have a return type of `void` and a parameter of type `PluginCommand`. - -```java -@Command("plot") -void plot(PluginCommand command) { -} -``` - -Now we want to define our first three sub-commands. These are the `` parts. A sub-command can be thought of as its own command in mcspring. We will then string up these individual commands to compose the full command. - -The `PluginCommand#on(String, Consumer)` method allows the creation of these sub-commands. For a consumer, I suggest you define a new method in the same class to keep things organized. - -```java -@Command("plot") -void plot(PluginCommand command) { - command.on("create", this::plotCreate); - command.on("add", this::plotAdd); - command.on("tp", this::plotTp); -} - -void plotCreate(PluginCommand command) { -} - -void plotAdd(PluginCommand command) { -} - -void plotTp(PluginCommand command) { -} -``` - -Now we treat `plotCreate` as its own command and start over. There is no additional parsing to be done so we can now create that plot for the user. If permissions checking, database lookups, etc.. have to be done; it should be in the execute method that we will soon define. - -The `PluginCommand#then(...)` method allows you to inject all parsed parameters into another method an run it. This is where your "work" should be done. - -```java -@Command("plot") -void plot(PluginCommand command) { - command.on("create", this::plotCreate); - command.on("add", this::plotAdd); - command.on("tp", this::plotTp); -} - -void plotCreate(PluginCommand command) { - command.then(this::plotCreateExecutor); -} - -void plotAdd(PluginCommand command) { - command.then(this::plotAddExecutor); -} - -void plotTp(PluginCommand command) { - command.then(this::plotTpExecutor); -} - -void plotCreateExecutor(Player sender) { - // create the plot for the sender -} - -void plotAddExecutor(Player sender) { - // add the player to the plot -} - -void plotTpExecutor(Player sender) { - // tp the player to the plot -} -``` - -Now we need to parse out the actual information in the commands. The `with...` methods allow you to parse individual arguments of the command and inject them into the executor method. These `with` calls should be put before the `execute` call. The `with...` parameters are injected into the method before anything else **IN ORDER**. - -```java -@Command("plot") -void plot(PluginCommand command) { - command.on("create", this::plotCreate); - command.on("add", this::plotAdd); - command.on("tp", this::plotTp); -} - -void plotCreate(PluginCommand command) { - command.then(this::plotCreateExecutor); -} - -void plotAdd(PluginCommand command) { - command.withPlayer("Argument was not a player"); - command.then(this::plotAddExecutor); -} - -void plotTp(PluginCommand command) { - command.withInt("Argument was not an integer"); // parse x - command.withInt("Argument was not an integer"); // parse y - command.then(this::plotTpExecutor); -} - -void plotCreateExecutor(Player sender) { - // create the plot for the sender -} - -void plotAddExecutor(Player target, Player sender) { - // add the player to the plot -} -``` - -### Some final touches -We actually are already done. Just a few more things to make it better. - -Here are a few issues: -* In the event a player runs `/plot asdflksjadf` nothing will happen. -* In the event a player runs `/plot` nothing will happen. -* In the event a player runs `/plot add` nothing will happen. -* In the event a player runs `/plot tp` nothing will happen. - -We need to add fallbacks for these cases. - -For an invalid sub-command being passed; we use `onInvalid`. The `Function` argument is called with the players sub-command if no valid sub-command was yet called. This should be called after all the `on` and `with` methods. - -For no sub-command being passed; we use `otherwise`. As the name notes, this runs if nothing yet has happened. It is the ultimate fallback. This should be called **LAST**. - -```java -@Command("plot") -void plot(PluginCommand command) { - command.on("create", this::plotCreate); - command.on("add", this::plotAdd); - command.on("tp", this::plotTp); - command.onInvalid(s -> String.format("%s is not a valid sub-command", s)); - command.otherwise("Usage: /plot "); -} - -void plotCreate(PluginCommand command) { - command.then(this::plotCreateExecutor); -} - -void plotAdd(PluginCommand command) { - command.withPlayer("Argument was not a player"); - command.then(this::plotAddExecutor); - command.otherwise("Usage: /plot add "); -} - -void plotTp(PluginCommand command) { - command.withInt("Argument was not an integer"); // parse x - command.withInt("Argument was not an integer"); // parse y - command.then(this::plotTpExecutor); - command.otherwise("Usage: /plot tp "); -} - -void plotCreateExecutor(Player sender) { - // create the plot for the sender -} - -void plotAddExecutor(Player target, Player sender) { - // add the player to the plot -} - -void plotTpExecutor(Player sender, int x, int y) { - // tp the player to the plot -} -``` - -## But Wait... There's More. - -Good job on making it this far. You've surely read a lot already so I'll make this last part short. - -Tab completion is automatically calculated for every command using the sub-command API. This is done by calling the command method with a dummy object that records the valid options for a command and then sending that to the player. That is why it is **SUPER IMPORTANT** that you **NEVER** put expensive command logic or anything other than `PluginCommand` calls inside the parsing methods. - ---- - -Holy smokes, you read all of that? Well if you have not yet read enough head over [here](https://github.com/kylepls/mcspring/wiki/The-Sub-Commands-API) to learn how to create sub-commands with tab completions. diff --git a/src/orchid/resources/wiki/summary.md b/src/orchid/resources/wiki/summary.md deleted file mode 100644 index e6e3eb6..0000000 --- a/src/orchid/resources/wiki/summary.md +++ /dev/null @@ -1,5 +0,0 @@ -- [Setup](setup.md) -- [The plugin.yml and the main class](intro.md) -- Commands - - [Basic Commands API](commands-api.md) - - [Sub-commands API](subcommands-api.md)