Skip to content

Commit f3a6a0c

Browse files
authored
feat: add Folia Support (#217)
* feat!: initial Folia support - fixes #213 * feat: add Folia plugin management * chore: amend to review * chore: suppress unused in deprecated bootstrap
1 parent ab7d62d commit f3a6a0c

File tree

19 files changed

+789
-14
lines changed

19 files changed

+789
-14
lines changed

annotations/build.gradle.kts

-6
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,4 @@ dependencies {
3737
}
3838

3939
implementation(libs.gson)
40-
}
41-
42-
java {
43-
toolchain {
44-
languageVersion.set(JavaLanguageVersion.of(8))
45-
}
4640
}

annotations/src/main/java/dev/hypera/chameleon/annotations/processing/ChameleonAnnotationProcessor.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import dev.hypera.chameleon.annotations.processing.generation.Generator;
2929
import dev.hypera.chameleon.annotations.processing.generation.bukkit.BukkitGenerator;
3030
import dev.hypera.chameleon.annotations.processing.generation.bungeecord.BungeeCordGenerator;
31+
import dev.hypera.chameleon.annotations.processing.generation.folia.FoliaGenerator;
3132
import dev.hypera.chameleon.annotations.processing.generation.minestom.MinestomGenerator;
3233
import dev.hypera.chameleon.annotations.processing.generation.nukkit.NukkitGenerator;
3334
import dev.hypera.chameleon.annotations.processing.generation.sponge.SpongeGenerator;
@@ -48,7 +49,7 @@
4849
* Chameleon Annotation Processor.
4950
*/
5051
@SupportedAnnotationTypes("dev.hypera.chameleon.annotations.Plugin")
51-
@SupportedSourceVersion(SourceVersion.RELEASE_8)
52+
@SupportedSourceVersion(SourceVersion.RELEASE_11)
5253
public class ChameleonAnnotationProcessor extends AbstractProcessor {
5354

5455
/**
@@ -80,6 +81,9 @@ public synchronized boolean process(Set<? extends TypeElement> annotations, Roun
8081
case Platform.BUNGEECORD:
8182
generator = new BungeeCordGenerator();
8283
break;
84+
case Platform.FOLIA:
85+
generator = new FoliaGenerator();
86+
break;
8387
case Platform.MINESTOM:
8488
generator = new MinestomGenerator();
8589
break;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/*
2+
* This file is a part of the Chameleon Framework, licensed under the MIT License.
3+
*
4+
* Copyright (c) 2021-2023 The Chameleon Framework Authors.
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
package dev.hypera.chameleon.annotations.processing.generation.folia;
25+
26+
import com.squareup.javapoet.FieldSpec;
27+
import com.squareup.javapoet.JavaFile;
28+
import com.squareup.javapoet.MethodSpec;
29+
import com.squareup.javapoet.TypeSpec;
30+
import dev.hypera.chameleon.annotations.Plugin;
31+
import dev.hypera.chameleon.annotations.exception.ChameleonAnnotationException;
32+
import dev.hypera.chameleon.annotations.processing.generation.Generator;
33+
import dev.hypera.chameleon.annotations.utils.MapBuilder;
34+
import dev.hypera.chameleon.exception.instantiation.ChameleonInstantiationException;
35+
import dev.hypera.chameleon.platform.Platform;
36+
import java.io.BufferedWriter;
37+
import java.io.IOException;
38+
import java.nio.file.Files;
39+
import java.nio.file.Paths;
40+
import java.util.Arrays;
41+
import java.util.Objects;
42+
import java.util.logging.Level;
43+
import java.util.stream.Collectors;
44+
import javax.annotation.processing.ProcessingEnvironment;
45+
import javax.lang.model.element.Modifier;
46+
import javax.lang.model.element.PackageElement;
47+
import javax.lang.model.element.TypeElement;
48+
import javax.tools.StandardLocation;
49+
import org.jetbrains.annotations.NotNull;
50+
import org.yaml.snakeyaml.Yaml;
51+
52+
/**
53+
* Bukkit plugin main class and 'paper-plugin.yml' description file generator.
54+
*/
55+
public final class FoliaGenerator extends Generator {
56+
57+
private static final @NotNull String DESCRIPTION_FILE = "paper-plugin.yml";
58+
59+
/**
60+
* Generate Bukkit plugin main class and 'plugin.yml' description file.
61+
*
62+
* @param data {@link Plugin} data
63+
* @param plugin Chameleon plugin main class
64+
* @param env Processing environment
65+
*
66+
* @throws ChameleonAnnotationException if something goes wrong while creating the files.
67+
*/
68+
@Override
69+
public void generate(@NotNull Plugin data, @NotNull TypeElement plugin, @NotNull ProcessingEnvironment env) throws ChameleonAnnotationException {
70+
MethodSpec constructorSpec = MethodSpec.constructorBuilder()
71+
.addModifiers(Modifier.PUBLIC)
72+
.beginControlFlow("try")
73+
.addStatement(createPluginData(data))
74+
.addStatement("this.$N = $T.createFoliaBootstrap($T.class, this, $N).load()", CHAMELEON_VAR, clazz("dev.hypera.chameleon.platform.folia", "FoliaChameleon"), plugin, "pluginData")
75+
.nextControlFlow("catch ($T ex)", ChameleonInstantiationException.class)
76+
.addStatement("getLogger().log($T.SEVERE, \"An error occurred while loading Chameleon\", $N)", Level.class, "ex")
77+
.addStatement("throw new $T($N)", clazz("dev.hypera.chameleon.exception", "ChameleonRuntimeException"), "ex")
78+
.endControlFlow()
79+
.build();
80+
81+
MethodSpec enableSpec = MethodSpec.methodBuilder("onEnable")
82+
.addAnnotation(Override.class).addModifiers(Modifier.PUBLIC)
83+
.addStatement("this.$N.onEnable()", CHAMELEON_VAR).build();
84+
85+
MethodSpec disableSpec = MethodSpec.methodBuilder("onDisable")
86+
.addAnnotation(Override.class).addModifiers(Modifier.PUBLIC)
87+
.addStatement("this.$N.onDisable()", CHAMELEON_VAR).build();
88+
89+
TypeSpec foliaMainClassSpec = TypeSpec.classBuilder(plugin.getSimpleName() + "Folia")
90+
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
91+
.superclass(clazz("org.bukkit.plugin.java", "JavaPlugin"))
92+
.addField(FieldSpec.builder(clazz("dev.hypera.chameleon.platform.folia", "FoliaChameleon"), CHAMELEON_VAR, Modifier.PRIVATE).build())
93+
.addMethod(constructorSpec)
94+
.addMethod(enableSpec)
95+
.addMethod(disableSpec)
96+
.build();
97+
98+
String packageName = Objects.requireNonNull((PackageElement) plugin.getEnclosingElement()).getQualifiedName().toString();
99+
if (packageName.endsWith("core") || packageName.endsWith("common")) {
100+
packageName = packageName.substring(0, packageName.lastIndexOf("."));
101+
}
102+
packageName = packageName + ".platform.folia";
103+
104+
try {
105+
JavaFile.builder(packageName, foliaMainClassSpec).indent(INDENT).build().writeTo(env.getFiler());
106+
generateDescriptionFile(data, plugin, env, packageName);
107+
} catch (IOException ex) {
108+
throw new ChameleonAnnotationException("Failed to write main class or description file", ex);
109+
}
110+
}
111+
112+
private void generateDescriptionFile(@NotNull Plugin data, @NotNull TypeElement plugin, @NotNull ProcessingEnvironment env, @NotNull String packageName) throws IOException {
113+
try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(env.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", DESCRIPTION_FILE).toUri()))) {
114+
new Yaml().dump(new MapBuilder<String, Object>().add("name", data.name().isEmpty() ? data.id() : data.name())
115+
.add("main", packageName + "." + plugin.getSimpleName() + "Folia")
116+
.add("version", data.version())
117+
.add("api-version", "1.19")
118+
.add("author", data.authors().length > 0 ? String.join(", ", data.authors()) : "Unknown")
119+
.add("authors", data.authors())
120+
.add("website", data.url())
121+
.add("dependencies", Arrays.stream(data.dependencies())
122+
.filter(d -> (d.platforms().length == 0 || Arrays.asList(d.platforms()).contains(Platform.FOLIA)))
123+
.map(d -> new MapBuilder<String, Object>()
124+
.add("name", d.name())
125+
.add("required", !d.soft())
126+
).collect(Collectors.toList()))
127+
.add("description", data.description())
128+
.add("folia-supported", "true"), writer);
129+
}
130+
}
131+
132+
}

api/src/main/java/dev/hypera/chameleon/platform/Platform.java

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public interface Platform {
3434

3535
@NotNull String BUKKIT = "Bukkit";
3636
@NotNull String BUNGEECORD = "BungeeCord";
37+
@NotNull String FOLIA = "Folia";
3738
@NotNull String MINESTOM = "Minestom";
3839
@NotNull String NUKKIT = "Nukkit";
3940
@NotNull String SPONGE = "Sponge";

build-logic/build.gradle.kts

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,6 @@ dependencies {
4242

4343
java {
4444
toolchain {
45-
languageVersion.set(JavaLanguageVersion.of(8))
45+
languageVersion.set(JavaLanguageVersion.of(11))
4646
}
4747
}

build-logic/src/main/kotlin/chameleon.base.gradle.kts

+7-1
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,16 @@ plugins {
3838
}
3939

4040
val libs = extensions.getByType<VersionCatalogsExtension>().named("libs")
41+
val requires17 = setOf(
42+
"chameleon-example",
43+
"chameleon-platform-folia",
44+
"chameleon-platform-minestom",
45+
"chameleon-platform-sponge",
46+
)
4147

4248
indra {
4349
javaVersions {
44-
if (project.name.contains("minestom") || project.name.contains("sponge") || project.name.contains("example")) {
50+
if (project.name in requires17) {
4551
target(17)
4652
testWith(17)
4753
} else {

example/build.gradle.kts

+4-2
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,15 @@ plugins {
3333
*/
3434
java {
3535
toolchain {
36-
languageVersion.set(JavaLanguageVersion.of(8))
36+
languageVersion.set(JavaLanguageVersion.of(11))
3737
targetCompatibility = JavaVersion.VERSION_17
3838
}
3939
}
4040

4141
repositories {
4242
maven("https://oss.sonatype.org/content/repositories/snapshots/") // Required for BungeeCord support
4343
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") // Required for Bukkit support
44+
maven("https://papermc.io/repo/repository/maven-public/") // Required for Folia support
4445
// maven("https://jitpack.io/") // Required for Minestom support
4546
maven("https://repo.spongepowered.org/maven/") // Required for Minestom/Sponge support
4647
maven("https://repo.opencollab.dev/main/") // Required for Nukkit support
@@ -60,6 +61,7 @@ dependencies {
6061
implementation(project(":chameleon-api")) // dev.hypera:chameleon-api
6162
implementation(project(":chameleon-platform-bukkit")) // dev.hypera:chameleon-platform-bukkit
6263
implementation(project(":chameleon-platform-bungeecord")) // dev.hypera:chameleon-platform-bungeecord
64+
implementation(project(":chameleon-platform-folia")) // dev.hypera:chameleon-platform-folia
6365
implementation(project(":chameleon-platform-nukkit")) // dev.hypera:chameleon-platform-nukkit
6466
implementation(project(":chameleon-platform-minestom")) // dev.hypera:chameleon-platform-minestom
6567
implementation(project(":chameleon-platform-velocity")) // dev.hypera:chameleon-platform-velocity
@@ -79,7 +81,7 @@ tasks {
7981
mergeServiceFiles()
8082

8183
/* IMPORTANT: Relocate all dependencies to avoid conflicts */
82-
relocate("dev.hypera.chameleon", "dev.hypera.chameleon.example.lib.chameleon")
84+
//relocate("dev.hypera.chameleon", "dev.hypera.chameleon.example.lib.chameleon") // Cannot relocate because example is in this package.
8385
relocate("net.kyori", "dev.hypera.chameleon.example.lib.kyori")
8486
relocate("com.google.gson", "dev.hypera.chameleon.example.lib.gson")
8587
}

example/src/main/java/dev/hypera/chameleon/example/ChameleonExample.java

+8-1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import dev.hypera.chameleon.example.event.ExampleCustomEvent;
3636
import dev.hypera.chameleon.logger.ChameleonLogger;
3737
import dev.hypera.chameleon.platform.Platform;
38+
import dev.hypera.chameleon.platform.PlatformPlugin;
3839
import dev.hypera.chameleon.scheduler.Schedule;
3940
import dev.hypera.chameleon.scheduler.Task;
4041
import java.time.Duration;
@@ -57,12 +58,13 @@
5758
@Dependency(
5859
name = "LuckPerms",
5960
soft = true,
60-
platforms = { Platform.BUKKIT }
61+
platforms = { Platform.BUKKIT, Platform.FOLIA }
6162
)
6263
},
6364
platforms = {
6465
Platform.BUKKIT,
6566
Platform.BUNGEECORD,
67+
Platform.FOLIA,
6668
Platform.MINESTOM,
6769
Platform.NUKKIT,
6870
Platform.SPONGE,
@@ -130,6 +132,11 @@ public void onEnable() {
130132
this.logger.info("This task will run twice!")
131133
).delay(Schedule.seconds(2)).repeat(Schedule.seconds(5)).cancelAfter(2).build());
132134

135+
/* Plugin Management */
136+
for (PlatformPlugin plugin : chameleon.getPluginManager().getPlugins()) {
137+
this.logger.info("Found plugin %s v%s", plugin.getName(), plugin.getVersion());
138+
}
139+
133140
this.logger.info(
134141
"Successfully started ChameleonExample plugin, took %s ms.",
135142
Duration.between(start, Instant.now()).toMillis()

gradle/libs.versions.toml

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ adventure-platform = "4.3.0"
99
# Platforms
1010
platform-bukkit = "1.19-R0.1-SNAPSHOT"
1111
platform-bungeecord = "1.19-R0.1-SNAPSHOT"
12+
platform-folia = "1.19.4-R0.1-SNAPSHOT"
1213
platform-minestom = "aebf72de90"
1314
platform-nukkit = "1.0-SNAPSHOT"
1415
platform-sponge = "9.0.0"
@@ -54,6 +55,7 @@ adventure-platform-bungeecord = { module = "net.kyori:adventure-platform-bungeec
5455
# Platforms
5556
platform-bukkit = { module = "org.spigotmc:spigot-api", version.ref = "platform-bukkit" }
5657
platform-bungeecord = { module = "net.md-5:bungeecord-api", version.ref = "platform-bungeecord" }
58+
platform-folia = { module = "dev.folia:folia-api", version.ref = "platform-folia" }
5759
platform-minestom = { module = "com.github.Minestom:Minestom", version.ref = "platform-minestom" }
5860
platform-nukkit = { module = "cn.nukkit:nukkit", version.ref = "platform-nukkit" }
5961
platform-sponge = { module = "org.spongepowered:spongeapi", version.ref = "platform-sponge" }

platform-bukkit/src/main/java/dev/hypera/chameleon/platform/bukkit/BukkitChameleon.java

+6-2
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,16 @@
4747
import org.bukkit.Bukkit;
4848
import org.bukkit.plugin.java.JavaPlugin;
4949
import org.jetbrains.annotations.ApiStatus.Internal;
50+
import org.jetbrains.annotations.ApiStatus.NonExtendable;
5051
import org.jetbrains.annotations.NotNull;
5152
import org.jetbrains.annotations.Nullable;
5253

5354
/**
5455
* Bukkit Chameleon implementation.
56+
* Not final to allow Folia implementation to extend this class.
5557
*/
56-
public final class BukkitChameleon extends Chameleon {
58+
@NonExtendable
59+
public class BukkitChameleon extends Chameleon {
5760

5861
private final @NotNull JavaPlugin plugin;
5962
private final @NotNull BukkitPlatform platform = new BukkitPlatform();
@@ -65,7 +68,8 @@ public final class BukkitChameleon extends Chameleon {
6568
private @Nullable ChameleonAudienceProvider audienceProvider;
6669

6770
@Internal
68-
BukkitChameleon(@NotNull Class<? extends ChameleonPlugin> chameleonPlugin, @NotNull Collection<ChameleonExtension<?>> extensions, @NotNull JavaPlugin bukkitPlugin, @NotNull ChameleonPluginData pluginData) throws ChameleonInstantiationException {
71+
// Protected to allow Folia to extend this class.
72+
protected BukkitChameleon(@NotNull Class<? extends ChameleonPlugin> chameleonPlugin, @NotNull Collection<ChameleonExtension<?>> extensions, @NotNull JavaPlugin bukkitPlugin, @NotNull ChameleonPluginData pluginData) throws ChameleonInstantiationException {
6973
super(chameleonPlugin, extensions, pluginData, new ChameleonJavaLogger(bukkitPlugin.getLogger()));
7074
this.plugin = bukkitPlugin;
7175
}

platform-folia/build.gradle.kts

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* This file is a part of the Chameleon Framework, licensed under the MIT License.
3+
*
4+
* Copyright (c) 2021-2023 The Chameleon Framework Authors.
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
plugins {
25+
id("chameleon.common")
26+
id("java-library")
27+
}
28+
29+
repositories {
30+
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/")
31+
maven("https://repo.papermc.io/repository/maven-public/")
32+
}
33+
34+
dependencies {
35+
api(project(":chameleon-platform-bukkit"))
36+
compileOnlyApi(libs.platform.folia)
37+
}

0 commit comments

Comments
 (0)