diff --git a/annotations/build.gradle.kts b/annotations/build.gradle.kts index 3a4ff69d..f1d81578 100644 --- a/annotations/build.gradle.kts +++ b/annotations/build.gradle.kts @@ -37,10 +37,4 @@ dependencies { } implementation(libs.gson) -} - -java { - toolchain { - languageVersion.set(JavaLanguageVersion.of(8)) - } } \ No newline at end of file diff --git a/annotations/src/main/java/dev/hypera/chameleon/annotations/processing/ChameleonAnnotationProcessor.java b/annotations/src/main/java/dev/hypera/chameleon/annotations/processing/ChameleonAnnotationProcessor.java index d49c2595..22ba94a0 100644 --- a/annotations/src/main/java/dev/hypera/chameleon/annotations/processing/ChameleonAnnotationProcessor.java +++ b/annotations/src/main/java/dev/hypera/chameleon/annotations/processing/ChameleonAnnotationProcessor.java @@ -28,6 +28,7 @@ import dev.hypera.chameleon.annotations.processing.generation.Generator; import dev.hypera.chameleon.annotations.processing.generation.bukkit.BukkitGenerator; import dev.hypera.chameleon.annotations.processing.generation.bungeecord.BungeeCordGenerator; +import dev.hypera.chameleon.annotations.processing.generation.folia.FoliaGenerator; import dev.hypera.chameleon.annotations.processing.generation.minestom.MinestomGenerator; import dev.hypera.chameleon.annotations.processing.generation.nukkit.NukkitGenerator; import dev.hypera.chameleon.annotations.processing.generation.sponge.SpongeGenerator; @@ -48,7 +49,7 @@ * Chameleon Annotation Processor. */ @SupportedAnnotationTypes("dev.hypera.chameleon.annotations.Plugin") -@SupportedSourceVersion(SourceVersion.RELEASE_8) +@SupportedSourceVersion(SourceVersion.RELEASE_11) public class ChameleonAnnotationProcessor extends AbstractProcessor { /** @@ -80,6 +81,9 @@ public synchronized boolean process(Set annotations, Roun case Platform.BUNGEECORD: generator = new BungeeCordGenerator(); break; + case Platform.FOLIA: + generator = new FoliaGenerator(); + break; case Platform.MINESTOM: generator = new MinestomGenerator(); break; diff --git a/annotations/src/main/java/dev/hypera/chameleon/annotations/processing/generation/folia/FoliaGenerator.java b/annotations/src/main/java/dev/hypera/chameleon/annotations/processing/generation/folia/FoliaGenerator.java new file mode 100644 index 00000000..159bc00f --- /dev/null +++ b/annotations/src/main/java/dev/hypera/chameleon/annotations/processing/generation/folia/FoliaGenerator.java @@ -0,0 +1,132 @@ +/* + * This file is a part of the Chameleon Framework, licensed under the MIT License. + * + * Copyright (c) 2021-2023 The Chameleon Framework Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.hypera.chameleon.annotations.processing.generation.folia; + +import com.squareup.javapoet.FieldSpec; +import com.squareup.javapoet.JavaFile; +import com.squareup.javapoet.MethodSpec; +import com.squareup.javapoet.TypeSpec; +import dev.hypera.chameleon.annotations.Plugin; +import dev.hypera.chameleon.annotations.exception.ChameleonAnnotationException; +import dev.hypera.chameleon.annotations.processing.generation.Generator; +import dev.hypera.chameleon.annotations.utils.MapBuilder; +import dev.hypera.chameleon.exception.instantiation.ChameleonInstantiationException; +import dev.hypera.chameleon.platform.Platform; +import java.io.BufferedWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Objects; +import java.util.logging.Level; +import java.util.stream.Collectors; +import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; +import javax.tools.StandardLocation; +import org.jetbrains.annotations.NotNull; +import org.yaml.snakeyaml.Yaml; + +/** + * Bukkit plugin main class and 'paper-plugin.yml' description file generator. + */ +public final class FoliaGenerator extends Generator { + + private static final @NotNull String DESCRIPTION_FILE = "paper-plugin.yml"; + + /** + * Generate Bukkit plugin main class and 'plugin.yml' description file. + * + * @param data {@link Plugin} data + * @param plugin Chameleon plugin main class + * @param env Processing environment + * + * @throws ChameleonAnnotationException if something goes wrong while creating the files. + */ + @Override + public void generate(@NotNull Plugin data, @NotNull TypeElement plugin, @NotNull ProcessingEnvironment env) throws ChameleonAnnotationException { + MethodSpec constructorSpec = MethodSpec.constructorBuilder() + .addModifiers(Modifier.PUBLIC) + .beginControlFlow("try") + .addStatement(createPluginData(data)) + .addStatement("this.$N = $T.createFoliaBootstrap($T.class, this, $N).load()", CHAMELEON_VAR, clazz("dev.hypera.chameleon.platform.folia", "FoliaChameleon"), plugin, "pluginData") + .nextControlFlow("catch ($T ex)", ChameleonInstantiationException.class) + .addStatement("getLogger().log($T.SEVERE, \"An error occurred while loading Chameleon\", $N)", Level.class, "ex") + .addStatement("throw new $T($N)", clazz("dev.hypera.chameleon.exception", "ChameleonRuntimeException"), "ex") + .endControlFlow() + .build(); + + MethodSpec enableSpec = MethodSpec.methodBuilder("onEnable") + .addAnnotation(Override.class).addModifiers(Modifier.PUBLIC) + .addStatement("this.$N.onEnable()", CHAMELEON_VAR).build(); + + MethodSpec disableSpec = MethodSpec.methodBuilder("onDisable") + .addAnnotation(Override.class).addModifiers(Modifier.PUBLIC) + .addStatement("this.$N.onDisable()", CHAMELEON_VAR).build(); + + TypeSpec foliaMainClassSpec = TypeSpec.classBuilder(plugin.getSimpleName() + "Folia") + .addModifiers(Modifier.PUBLIC, Modifier.FINAL) + .superclass(clazz("org.bukkit.plugin.java", "JavaPlugin")) + .addField(FieldSpec.builder(clazz("dev.hypera.chameleon.platform.folia", "FoliaChameleon"), CHAMELEON_VAR, Modifier.PRIVATE).build()) + .addMethod(constructorSpec) + .addMethod(enableSpec) + .addMethod(disableSpec) + .build(); + + String packageName = Objects.requireNonNull((PackageElement) plugin.getEnclosingElement()).getQualifiedName().toString(); + if (packageName.endsWith("core") || packageName.endsWith("common")) { + packageName = packageName.substring(0, packageName.lastIndexOf(".")); + } + packageName = packageName + ".platform.folia"; + + try { + JavaFile.builder(packageName, foliaMainClassSpec).indent(INDENT).build().writeTo(env.getFiler()); + generateDescriptionFile(data, plugin, env, packageName); + } catch (IOException ex) { + throw new ChameleonAnnotationException("Failed to write main class or description file", ex); + } + } + + private void generateDescriptionFile(@NotNull Plugin data, @NotNull TypeElement plugin, @NotNull ProcessingEnvironment env, @NotNull String packageName) throws IOException { + try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(env.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", DESCRIPTION_FILE).toUri()))) { + new Yaml().dump(new MapBuilder().add("name", data.name().isEmpty() ? data.id() : data.name()) + .add("main", packageName + "." + plugin.getSimpleName() + "Folia") + .add("version", data.version()) + .add("api-version", "1.19") + .add("author", data.authors().length > 0 ? String.join(", ", data.authors()) : "Unknown") + .add("authors", data.authors()) + .add("website", data.url()) + .add("dependencies", Arrays.stream(data.dependencies()) + .filter(d -> (d.platforms().length == 0 || Arrays.asList(d.platforms()).contains(Platform.FOLIA))) + .map(d -> new MapBuilder() + .add("name", d.name()) + .add("required", !d.soft()) + ).collect(Collectors.toList())) + .add("description", data.description()) + .add("folia-supported", "true"), writer); + } + } + +} diff --git a/api/src/main/java/dev/hypera/chameleon/platform/Platform.java b/api/src/main/java/dev/hypera/chameleon/platform/Platform.java index 8f562d4e..facf052b 100644 --- a/api/src/main/java/dev/hypera/chameleon/platform/Platform.java +++ b/api/src/main/java/dev/hypera/chameleon/platform/Platform.java @@ -34,6 +34,7 @@ public interface Platform { @NotNull String BUKKIT = "Bukkit"; @NotNull String BUNGEECORD = "BungeeCord"; + @NotNull String FOLIA = "Folia"; @NotNull String MINESTOM = "Minestom"; @NotNull String NUKKIT = "Nukkit"; @NotNull String SPONGE = "Sponge"; diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts index a0b6c1a0..f65063cc 100644 --- a/build-logic/build.gradle.kts +++ b/build-logic/build.gradle.kts @@ -42,6 +42,6 @@ dependencies { java { toolchain { - languageVersion.set(JavaLanguageVersion.of(8)) + languageVersion.set(JavaLanguageVersion.of(11)) } } \ No newline at end of file diff --git a/build-logic/src/main/kotlin/chameleon.base.gradle.kts b/build-logic/src/main/kotlin/chameleon.base.gradle.kts index 1fbe8318..2ec87987 100644 --- a/build-logic/src/main/kotlin/chameleon.base.gradle.kts +++ b/build-logic/src/main/kotlin/chameleon.base.gradle.kts @@ -38,10 +38,16 @@ plugins { } val libs = extensions.getByType().named("libs") +val requires17 = setOf( + "chameleon-example", + "chameleon-platform-folia", + "chameleon-platform-minestom", + "chameleon-platform-sponge", +) indra { javaVersions { - if (project.name.contains("minestom") || project.name.contains("sponge") || project.name.contains("example")) { + if (project.name in requires17) { target(17) testWith(17) } else { diff --git a/example/build.gradle.kts b/example/build.gradle.kts index 11ed0005..06254ca4 100644 --- a/example/build.gradle.kts +++ b/example/build.gradle.kts @@ -33,7 +33,7 @@ plugins { */ java { toolchain { - languageVersion.set(JavaLanguageVersion.of(8)) + languageVersion.set(JavaLanguageVersion.of(11)) targetCompatibility = JavaVersion.VERSION_17 } } @@ -41,6 +41,7 @@ java { repositories { maven("https://oss.sonatype.org/content/repositories/snapshots/") // Required for BungeeCord support maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") // Required for Bukkit support + maven("https://papermc.io/repo/repository/maven-public/") // Required for Folia support // maven("https://jitpack.io/") // Required for Minestom support maven("https://repo.spongepowered.org/maven/") // Required for Minestom/Sponge support maven("https://repo.opencollab.dev/main/") // Required for Nukkit support @@ -60,6 +61,7 @@ dependencies { implementation(project(":chameleon-api")) // dev.hypera:chameleon-api implementation(project(":chameleon-platform-bukkit")) // dev.hypera:chameleon-platform-bukkit implementation(project(":chameleon-platform-bungeecord")) // dev.hypera:chameleon-platform-bungeecord + implementation(project(":chameleon-platform-folia")) // dev.hypera:chameleon-platform-folia implementation(project(":chameleon-platform-nukkit")) // dev.hypera:chameleon-platform-nukkit implementation(project(":chameleon-platform-minestom")) // dev.hypera:chameleon-platform-minestom implementation(project(":chameleon-platform-velocity")) // dev.hypera:chameleon-platform-velocity @@ -79,7 +81,7 @@ tasks { mergeServiceFiles() /* IMPORTANT: Relocate all dependencies to avoid conflicts */ - relocate("dev.hypera.chameleon", "dev.hypera.chameleon.example.lib.chameleon") + //relocate("dev.hypera.chameleon", "dev.hypera.chameleon.example.lib.chameleon") // Cannot relocate because example is in this package. relocate("net.kyori", "dev.hypera.chameleon.example.lib.kyori") relocate("com.google.gson", "dev.hypera.chameleon.example.lib.gson") } diff --git a/example/src/main/java/dev/hypera/chameleon/example/ChameleonExample.java b/example/src/main/java/dev/hypera/chameleon/example/ChameleonExample.java index 056e2c96..b4c136e7 100644 --- a/example/src/main/java/dev/hypera/chameleon/example/ChameleonExample.java +++ b/example/src/main/java/dev/hypera/chameleon/example/ChameleonExample.java @@ -35,6 +35,7 @@ import dev.hypera.chameleon.example.event.ExampleCustomEvent; import dev.hypera.chameleon.logger.ChameleonLogger; import dev.hypera.chameleon.platform.Platform; +import dev.hypera.chameleon.platform.PlatformPlugin; import dev.hypera.chameleon.scheduler.Schedule; import dev.hypera.chameleon.scheduler.Task; import java.time.Duration; @@ -57,12 +58,13 @@ @Dependency( name = "LuckPerms", soft = true, - platforms = { Platform.BUKKIT } + platforms = { Platform.BUKKIT, Platform.FOLIA } ) }, platforms = { Platform.BUKKIT, Platform.BUNGEECORD, + Platform.FOLIA, Platform.MINESTOM, Platform.NUKKIT, Platform.SPONGE, @@ -130,6 +132,11 @@ public void onEnable() { this.logger.info("This task will run twice!") ).delay(Schedule.seconds(2)).repeat(Schedule.seconds(5)).cancelAfter(2).build()); + /* Plugin Management */ + for (PlatformPlugin plugin : chameleon.getPluginManager().getPlugins()) { + this.logger.info("Found plugin %s v%s", plugin.getName(), plugin.getVersion()); + } + this.logger.info( "Successfully started ChameleonExample plugin, took %s ms.", Duration.between(start, Instant.now()).toMillis() diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 35cbe60b..ce8dc70d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,6 +9,7 @@ adventure-platform = "4.3.0" # Platforms platform-bukkit = "1.19-R0.1-SNAPSHOT" platform-bungeecord = "1.19-R0.1-SNAPSHOT" +platform-folia = "1.19.4-R0.1-SNAPSHOT" platform-minestom = "aebf72de90" platform-nukkit = "1.0-SNAPSHOT" platform-sponge = "9.0.0" @@ -54,6 +55,7 @@ adventure-platform-bungeecord = { module = "net.kyori:adventure-platform-bungeec # Platforms platform-bukkit = { module = "org.spigotmc:spigot-api", version.ref = "platform-bukkit" } platform-bungeecord = { module = "net.md-5:bungeecord-api", version.ref = "platform-bungeecord" } +platform-folia = { module = "dev.folia:folia-api", version.ref = "platform-folia" } platform-minestom = { module = "com.github.Minestom:Minestom", version.ref = "platform-minestom" } platform-nukkit = { module = "cn.nukkit:nukkit", version.ref = "platform-nukkit" } platform-sponge = { module = "org.spongepowered:spongeapi", version.ref = "platform-sponge" } diff --git a/platform-bukkit/src/main/java/dev/hypera/chameleon/platform/bukkit/BukkitChameleon.java b/platform-bukkit/src/main/java/dev/hypera/chameleon/platform/bukkit/BukkitChameleon.java index e87e4ba2..0c016bfb 100644 --- a/platform-bukkit/src/main/java/dev/hypera/chameleon/platform/bukkit/BukkitChameleon.java +++ b/platform-bukkit/src/main/java/dev/hypera/chameleon/platform/bukkit/BukkitChameleon.java @@ -47,13 +47,16 @@ import org.bukkit.Bukkit; import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.ApiStatus.Internal; +import org.jetbrains.annotations.ApiStatus.NonExtendable; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** * Bukkit Chameleon implementation. + * Not final to allow Folia implementation to extend this class. */ -public final class BukkitChameleon extends Chameleon { +@NonExtendable +public class BukkitChameleon extends Chameleon { private final @NotNull JavaPlugin plugin; private final @NotNull BukkitPlatform platform = new BukkitPlatform(); @@ -65,7 +68,8 @@ public final class BukkitChameleon extends Chameleon { private @Nullable ChameleonAudienceProvider audienceProvider; @Internal - BukkitChameleon(@NotNull Class chameleonPlugin, @NotNull Collection> extensions, @NotNull JavaPlugin bukkitPlugin, @NotNull ChameleonPluginData pluginData) throws ChameleonInstantiationException { + // Protected to allow Folia to extend this class. + protected BukkitChameleon(@NotNull Class chameleonPlugin, @NotNull Collection> extensions, @NotNull JavaPlugin bukkitPlugin, @NotNull ChameleonPluginData pluginData) throws ChameleonInstantiationException { super(chameleonPlugin, extensions, pluginData, new ChameleonJavaLogger(bukkitPlugin.getLogger())); this.plugin = bukkitPlugin; } diff --git a/platform-folia/build.gradle.kts b/platform-folia/build.gradle.kts new file mode 100644 index 00000000..6564f83a --- /dev/null +++ b/platform-folia/build.gradle.kts @@ -0,0 +1,37 @@ +/* + * This file is a part of the Chameleon Framework, licensed under the MIT License. + * + * Copyright (c) 2021-2023 The Chameleon Framework Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +plugins { + id("chameleon.common") + id("java-library") +} + +repositories { + maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") + maven("https://repo.papermc.io/repository/maven-public/") +} + +dependencies { + api(project(":chameleon-platform-bukkit")) + compileOnlyApi(libs.platform.folia) +} \ No newline at end of file diff --git a/platform-folia/src/main/java/dev/hypera/chameleon/platform/folia/FoliaChameleon.java b/platform-folia/src/main/java/dev/hypera/chameleon/platform/folia/FoliaChameleon.java new file mode 100644 index 00000000..b844acf9 --- /dev/null +++ b/platform-folia/src/main/java/dev/hypera/chameleon/platform/folia/FoliaChameleon.java @@ -0,0 +1,132 @@ +/* + * This file is a part of the Chameleon Framework, licensed under the MIT License. + * + * Copyright (c) 2021-2023 The Chameleon Framework Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.hypera.chameleon.platform.folia; + +import dev.hypera.chameleon.ChameleonPlugin; +import dev.hypera.chameleon.ChameleonPluginData; +import dev.hypera.chameleon.exception.instantiation.ChameleonInstantiationException; +import dev.hypera.chameleon.extension.ChameleonExtension; +import dev.hypera.chameleon.platform.Platform; +import dev.hypera.chameleon.platform.PluginManager; +import dev.hypera.chameleon.platform.bukkit.BukkitChameleon; +import dev.hypera.chameleon.platform.bukkit.BukkitChameleonBootstrap; +import dev.hypera.chameleon.platform.folia.platform.FoliaPlatform; +import dev.hypera.chameleon.platform.folia.platform.FoliaPluginManager; +import dev.hypera.chameleon.platform.folia.scheduler.FoliaScheduler; +import dev.hypera.chameleon.scheduler.Scheduler; +import java.util.Collection; +import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.ApiStatus.Experimental; +import org.jetbrains.annotations.ApiStatus.Internal; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Folia Chameleon implementation. + */ +// We need to extend BukkitChameleon because all Bukkit managers require an instance of it to instantiate. Once this is solved, this can be made its own class. +@Experimental +public final class FoliaChameleon extends BukkitChameleon { + + private final @Nullable FoliaPlatform platform; + private final @Nullable FoliaPluginManager pluginManager; + private final @Nullable FoliaScheduler scheduler; + + @Internal + FoliaChameleon(@NotNull Class chameleonPlugin, @NotNull Collection> extensions, @NotNull JavaPlugin foliaPlugin, @NotNull ChameleonPluginData pluginData) throws ChameleonInstantiationException { + super(chameleonPlugin, extensions, foliaPlugin, pluginData); + boolean isFolia = isFolia(); + this.platform = isFolia ? new FoliaPlatform() : null; + this.pluginManager = isFolia ? new FoliaPluginManager() : null; + this.scheduler = isFolia ? new FoliaScheduler(this) : null; + } + + /** + * Unsupported. + * + * @param chameleonPlugin Unsupported. + * @param bukkitPlugin Unsupported. + * @param pluginData Unsupported. + * @return Unsupported. + */ + @Deprecated + @SuppressWarnings("unused") + public static @NotNull BukkitChameleonBootstrap create(@NotNull Class chameleonPlugin, @NotNull JavaPlugin bukkitPlugin, @NotNull ChameleonPluginData pluginData) { + throw new UnsupportedOperationException("Folia does not support Bukkit."); + } + + /** + * Create a new Folia Chameleon bootstrap instance. + * + * @param chameleonPlugin Chameleon plugin to be loaded. + * @param foliaPlugin Folia JavaPlugin instance. + * @param pluginData Chameleon plugin data. + * + * @return new Folia Chameleon bootstrap. + */ + @Experimental + public static @NotNull FoliaChameleonBootstrap createFoliaBootstrap(@NotNull Class chameleonPlugin, @NotNull JavaPlugin foliaPlugin, @NotNull ChameleonPluginData pluginData) { + return new FoliaChameleonBootstrap(chameleonPlugin, foliaPlugin, pluginData); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Platform getPlatform() { + return this.platform != null ? this.platform : super.getPlatform(); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull PluginManager getPluginManager() { + return this.pluginManager != null ? this.pluginManager : super.getPluginManager(); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Scheduler getScheduler() { + return this.scheduler != null ? this.scheduler : super.getScheduler(); + } + + /** + * Check if Folia is present. + * + * @return true if Folia is present. + */ + private static boolean isFolia() { + try { + // find a better class to use for this? + Class.forName("io.papermc.paper.threadedregions.scheduler.AsyncScheduler"); + return true; + } catch (ClassNotFoundException e) { + return false; + } + } + +} diff --git a/platform-folia/src/main/java/dev/hypera/chameleon/platform/folia/FoliaChameleonBootstrap.java b/platform-folia/src/main/java/dev/hypera/chameleon/platform/folia/FoliaChameleonBootstrap.java new file mode 100644 index 00000000..de18d43b --- /dev/null +++ b/platform-folia/src/main/java/dev/hypera/chameleon/platform/folia/FoliaChameleonBootstrap.java @@ -0,0 +1,67 @@ +/* + * This file is a part of the Chameleon Framework, licensed under the MIT License. + * + * Copyright (c) 2021-2023 The Chameleon Framework Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.hypera.chameleon.platform.folia; + +import dev.hypera.chameleon.ChameleonBootstrap; +import dev.hypera.chameleon.ChameleonPlugin; +import dev.hypera.chameleon.ChameleonPluginData; +import dev.hypera.chameleon.exception.instantiation.ChameleonInstantiationException; +import dev.hypera.chameleon.extension.ChameleonExtension; +import dev.hypera.chameleon.logger.ChameleonJavaLogger; +import dev.hypera.chameleon.logger.ChameleonLogger; +import dev.hypera.chameleon.platform.folia.extension.ChameleonFoliaExtension; +import java.util.Collection; +import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.ApiStatus.Internal; +import org.jetbrains.annotations.NotNull; + +/** + * Folia Chameleon bootstrap implementation. + */ +public final class FoliaChameleonBootstrap extends ChameleonBootstrap> { + + private final @NotNull Class chameleonPlugin; + private final @NotNull JavaPlugin foliaPlugin; + private final @NotNull ChameleonPluginData pluginData; + + @Internal + FoliaChameleonBootstrap(@NotNull Class chameleonPlugin, @NotNull JavaPlugin foliaPlugin, @NotNull ChameleonPluginData pluginData) { + this.chameleonPlugin = chameleonPlugin; + this.foliaPlugin = foliaPlugin; + this.pluginData = pluginData; + } + + @Internal + @Override + protected @NotNull FoliaChameleon loadInternal(@NotNull Collection> extensions) throws ChameleonInstantiationException { + return new FoliaChameleon(this.chameleonPlugin, extensions, this.foliaPlugin, this.pluginData); + } + + @Internal + @Override + protected @NotNull ChameleonLogger createLogger() { + return new ChameleonJavaLogger(this.foliaPlugin.getLogger()); + } + +} diff --git a/platform-folia/src/main/java/dev/hypera/chameleon/platform/folia/extension/ChameleonFoliaExtension.java b/platform-folia/src/main/java/dev/hypera/chameleon/platform/folia/extension/ChameleonFoliaExtension.java new file mode 100644 index 00000000..c4a2b8d7 --- /dev/null +++ b/platform-folia/src/main/java/dev/hypera/chameleon/platform/folia/extension/ChameleonFoliaExtension.java @@ -0,0 +1,39 @@ +/* + * This file is a part of the Chameleon Framework, licensed under the MIT License. + * + * Copyright (c) 2021-2023 The Chameleon Framework Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.hypera.chameleon.platform.folia.extension; + +import dev.hypera.chameleon.extension.ChameleonExtension; +import dev.hypera.chameleon.extension.ChameleonPlatformExtension; +import dev.hypera.chameleon.extension.CustomPlatformExtension; +import dev.hypera.chameleon.platform.folia.FoliaChameleon; + +/** + * Chameleon Folia extension. + * + * @param Chameleon extension type. + * @param Chameleon platform extension type. + */ +public abstract class ChameleonFoliaExtension, C extends CustomPlatformExtension> extends ChameleonPlatformExtension { + +} diff --git a/platform-folia/src/main/java/dev/hypera/chameleon/platform/folia/platform/FoliaPlatform.java b/platform-folia/src/main/java/dev/hypera/chameleon/platform/folia/platform/FoliaPlatform.java new file mode 100644 index 00000000..2a89a4c3 --- /dev/null +++ b/platform-folia/src/main/java/dev/hypera/chameleon/platform/folia/platform/FoliaPlatform.java @@ -0,0 +1,62 @@ +/* + * This file is a part of the Chameleon Framework, licensed under the MIT License. + * + * Copyright (c) 2021-2023 The Chameleon Framework Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.hypera.chameleon.platform.folia.platform; + +import dev.hypera.chameleon.platform.Platform; +import dev.hypera.chameleon.platform.server.ServerPlatform; +import org.bukkit.Bukkit; +import org.jetbrains.annotations.ApiStatus.Internal; +import org.jetbrains.annotations.NotNull; + +/** + * Folia server platform implementation. + */ +@Internal +public final class FoliaPlatform implements ServerPlatform { + + /** + * {@inheritDoc} + */ + @Override + public @NotNull String getId() { + return Platform.FOLIA; + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull String getName() { + return Bukkit.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull String getVersion() { + return Bukkit.getVersion(); + } + +} diff --git a/platform-folia/src/main/java/dev/hypera/chameleon/platform/folia/platform/FoliaPluginManager.java b/platform-folia/src/main/java/dev/hypera/chameleon/platform/folia/platform/FoliaPluginManager.java new file mode 100644 index 00000000..48d5c94a --- /dev/null +++ b/platform-folia/src/main/java/dev/hypera/chameleon/platform/folia/platform/FoliaPluginManager.java @@ -0,0 +1,70 @@ +/* + * This file is a part of the Chameleon Framework, licensed under the MIT License. + * + * Copyright (c) 2021-2023 The Chameleon Framework Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.hypera.chameleon.platform.folia.platform; + +import dev.hypera.chameleon.platform.PlatformPlugin; +import dev.hypera.chameleon.platform.PluginManager; +import dev.hypera.chameleon.platform.folia.platform.objects.FoliaPlugin; +import java.util.Arrays; +import java.util.Collection; +import java.util.Optional; +import java.util.stream.Collectors; +import org.bukkit.Bukkit; +import org.jetbrains.annotations.ApiStatus.Internal; +import org.jetbrains.annotations.NotNull; + +/** + * Folia plugin manager implementation. + */ +@Internal +public final class FoliaPluginManager implements PluginManager { + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Collection getPlugins() { + return Arrays.stream(Bukkit.getPluginManager().getPlugins()) + .map(FoliaPlugin::new) + .collect(Collectors.toSet()); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Optional getPlugin(@NotNull String name) { + return Optional.ofNullable(Bukkit.getPluginManager().getPlugin(name)) + .map(FoliaPlugin::new); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isPluginEnabled(@NotNull String name) { + return Bukkit.getPluginManager().isPluginEnabled(name); + } + +} diff --git a/platform-folia/src/main/java/dev/hypera/chameleon/platform/folia/platform/objects/FoliaPlugin.java b/platform-folia/src/main/java/dev/hypera/chameleon/platform/folia/platform/objects/FoliaPlugin.java new file mode 100644 index 00000000..2e217800 --- /dev/null +++ b/platform-folia/src/main/java/dev/hypera/chameleon/platform/folia/platform/objects/FoliaPlugin.java @@ -0,0 +1,138 @@ +/* + * This file is a part of the Chameleon Framework, licensed under the MIT License. + * + * Copyright (c) 2021-2023 The Chameleon Framework Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.hypera.chameleon.platform.folia.platform.objects; + +import dev.hypera.chameleon.platform.PlatformPlugin; +import dev.hypera.chameleon.util.ChameleonUtil; +import java.nio.file.Path; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.ApiStatus.Internal; +import org.jetbrains.annotations.NotNull; + +/** + * Folia platform plugin implementation. + */ +@Internal +public final class FoliaPlugin implements PlatformPlugin { + + private final @NotNull Plugin plugin; + + /** + * Folia plugin constructor. + * + * @param plugin Plugin to be wrapped. + */ + @Internal + public FoliaPlugin(@NotNull Plugin plugin) { + this.plugin = plugin; + } + + + /** + * {@inheritDoc} + */ + @Override + public @NotNull String getName() { + return ChameleonUtil.getOrDefault(this.plugin.getPluginMeta().getName(), "unknown"); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull String getVersion() { + return ChameleonUtil.getOrDefault(this.plugin.getPluginMeta().getVersion(), "unknown"); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Optional getDescription() { + return Optional.ofNullable(this.plugin.getPluginMeta().getDescription()); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Class getMainClass() { + return this.plugin.getClass(); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Collection getAuthors() { + return ChameleonUtil.getOrDefault(this.plugin.getPluginMeta().getAuthors(), Collections.emptyList()); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Set getDependencies() { + return new HashSet<>(this.plugin.getPluginMeta().getPluginDependencies()); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Set getSoftDependencies() { + return new HashSet<>(this.plugin.getPluginMeta().getPluginSoftDependencies()); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Path getDataFolder() { + return this.plugin.getDataFolder().toPath().toAbsolutePath(); + } + + /** + * {@inheritDoc} + */ + @Override + public void enable() { + Bukkit.getPluginManager().enablePlugin(this.plugin); + } + + /** + * {@inheritDoc} + */ + @Override + public void disable() { + Bukkit.getPluginManager().disablePlugin(this.plugin); + } + +} diff --git a/platform-folia/src/main/java/dev/hypera/chameleon/platform/folia/scheduler/FoliaScheduler.java b/platform-folia/src/main/java/dev/hypera/chameleon/platform/folia/scheduler/FoliaScheduler.java new file mode 100644 index 00000000..103e7c8f --- /dev/null +++ b/platform-folia/src/main/java/dev/hypera/chameleon/platform/folia/scheduler/FoliaScheduler.java @@ -0,0 +1,77 @@ +/* + * This file is a part of the Chameleon Framework, licensed under the MIT License. + * + * Copyright (c) 2021-2023 The Chameleon Framework Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.hypera.chameleon.platform.folia.scheduler; + +import dev.hypera.chameleon.platform.folia.FoliaChameleon; +import dev.hypera.chameleon.scheduler.Schedule; +import dev.hypera.chameleon.scheduler.ScheduledTask; +import dev.hypera.chameleon.scheduler.Scheduler; +import io.papermc.paper.threadedregions.scheduler.AsyncScheduler; +import io.papermc.paper.threadedregions.scheduler.GlobalRegionScheduler; +import java.util.concurrent.TimeUnit; +import org.bukkit.Bukkit; +import org.jetbrains.annotations.ApiStatus.Internal; +import org.jetbrains.annotations.NotNull; + +/** + * Folia scheduler implementation. + */ +@Internal +public final class FoliaScheduler extends Scheduler { + + private final @NotNull FoliaChameleon chameleon; + + /** + * Folia scheduler constructor. + * + * @param chameleon Folia Chameleon implementation. + */ + @Internal + public FoliaScheduler(@NotNull FoliaChameleon chameleon) { + this.chameleon = chameleon; + } + + @Override + protected @NotNull ScheduledTask scheduleAsyncTask(@NotNull Runnable task, @NotNull Schedule delay, @NotNull Schedule repeat) { + AsyncScheduler scheduler = Bukkit.getAsyncScheduler(); + long period = repeat.toMillis(); + io.papermc.paper.threadedregions.scheduler.ScheduledTask foliaTask = period > 0 + ? scheduler.runAtFixedRate(this.chameleon.getPlatformPlugin(), t -> task.run(), delay.toMillis(), period, TimeUnit.MILLISECONDS) + : scheduler.runDelayed(this.chameleon.getPlatformPlugin(), t -> task.run(), delay.toMillis(), TimeUnit.MILLISECONDS); + + return foliaTask::cancel; + } + + @Override + protected @NotNull ScheduledTask scheduleSyncTask(@NotNull Runnable task, @NotNull Schedule delay, @NotNull Schedule repeat) { + GlobalRegionScheduler scheduler = Bukkit.getGlobalRegionScheduler(); + long period = repeat.toTicks(); + io.papermc.paper.threadedregions.scheduler.ScheduledTask foliaTask = period > 0 + ? scheduler.runAtFixedRate(this.chameleon.getPlatformPlugin(), t -> task.run(), delay.toTicks(), period) + : scheduler.runDelayed(this.chameleon.getPlatformPlugin(), t -> task.run(), delay.toTicks()); + + return foliaTask::cancel; + } + +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 0a4d8cdc..e1e6d2f3 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -37,6 +37,7 @@ sequenceOf( "example", "platform-bukkit", "platform-bungeecord", + "platform-folia", "platform-minestom", "platform-nukkit", "platform-sponge",