diff --git a/build.gradle.kts b/build.gradle.kts index 25cfd6f..41b8049 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -32,4 +32,6 @@ tasks.register("publishAll") { dependsOn(tasks.getByPath("architecture:publish")) dependsOn(tasks.getByPath("extensions:publish")) dependsOn(tasks.getByPath("serialization:publish")) + dependsOn(tasks.getByPath("genref:publish")) + dependsOn(tasks.getByPath("messages:publish")) } \ No newline at end of file diff --git a/genref/.gitignore b/genref/.gitignore new file mode 100644 index 0000000..b63da45 --- /dev/null +++ b/genref/.gitignore @@ -0,0 +1,42 @@ +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/genref/README.md b/genref/README.md new file mode 100644 index 0000000..ba5649c --- /dev/null +++ b/genref/README.md @@ -0,0 +1,34 @@ +Instruction for kotlin v. 1.8.21 +

Step 1 - configure build.gradle

+

Add Kotlin 1.8.21 and KSP plugins:

+ +```kotlin +kotlin("jvm") version "1.8.21" +id("com.google.devtools.ksp") version "1.8.21-1.0.11" +``` + +Add CarcadeX GenRef and KotlinBukkitAPI to project:

+ +```kotlin +dependencies { + implementation("br.com.devsrsouza.kotlinbukkitapi:architecture:1.0.0-SNAPSHOT") + compileOnly("io.github.iredtea:carcadex-genref:[LATEST_VERSION]") + ksp("io.github.iredtea:carcadex-genref:[LATEST_VERSION]") +} +``` + +

Step 2 - create plugin functions

+ +```kotlin +@Plugin +fun KotlinPlugin.start() { + // Plugin enable logic +} + + +//Optional +@OnDisable +fun KotlinPlugin.stop() { + // Disable enable logic +} +``` diff --git a/genref/build.gradle.kts b/genref/build.gradle.kts new file mode 100644 index 0000000..3046568 --- /dev/null +++ b/genref/build.gradle.kts @@ -0,0 +1,116 @@ +plugins { + id("java") + kotlin("jvm") + id("maven-publish") + id("signing") + id("com.google.devtools.ksp") version "1.8.21-1.0.11" +} + +group = "tech.carcadex" +val globalArtifactId = "kotlinbukkitkit-genref" +val globalName = "kotlinbukkitkit-genref" + +repositories { + mavenCentral() + maven { + name = "spigotmc-repo" + url = uri("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") + } +} + +dependencies { + implementation("org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT") + implementation(project(":architecture")) + implementation("com.google.devtools.ksp:symbol-processing-api:1.8.21-1.0.11") + implementation("com.squareup:kotlinpoet:1.10.1") + implementation("com.squareup:kotlinpoet-ksp:1.10.1") + +} +tasks.test { + useJUnitPlatform() +} + +kotlin { + jvmToolchain(8) +} + +kotlin { + sourceSets.main { + kotlin.srcDir("build/generated/ksp/main/kotlin") + } + sourceSets.test { + kotlin.srcDir("build/generated/ksp/test/kotlin") + } +} + +java { + withJavadocJar() + withSourcesJar() +} + +publishing { + publications { + create("maven") { + artifactId = globalArtifactId + groupId = "tech.carcadex" + version = version + from(components["java"]) + versionMapping { + usage("java-api") { + fromResolutionOf("runtimeClasspath") + } + usage("java-runtime") { + fromResolutionResult() + } + } + pom { + artifactId = globalArtifactId + groupId = "tech.carcadex" + version = version + name.set(globalArtifactId) + description.set(globalArtifactId) + url.set("https://github.com/iRedTea/CarcadeX") + + licenses { + license { + name.set("GNU General Public License v3.0") + url.set("https://www.gnu.org/licenses/gpl-3.0.en.html") + } + } + developers { + developer { + id.set("itzRedTea") + name.set("Red Tea") + email.set("red__tea@outlook.com") + } + } + scm { + connection.set("scm:https://github.com/CarcadeX/$globalName.git") + developerConnection.set("git@github.com:CarcadeX/$globalName.git") + url.set("https://github.com/iRedTea/CarcadeX") + } + } + + } + } + + + + repositories { + maven { + val releasesRepoUrl = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/" + val snapshotsRepoUrl = "https://s01.oss.sonatype.org/content/repositories/snapshots/" + url = uri(if (version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl) + credentials { + username = "itzRedTea" + password = project.findProperty("password").toString() + } + } + + } +} + +signing { + useGpgCmd() + sign(publishing.publications.getByName("maven")) +} diff --git a/genref/src/main/kotlin/me/redtea/carcadex/genref/plugin/PluginVisitor.kt b/genref/src/main/kotlin/me/redtea/carcadex/genref/plugin/PluginVisitor.kt new file mode 100644 index 0000000..163190c --- /dev/null +++ b/genref/src/main/kotlin/me/redtea/carcadex/genref/plugin/PluginVisitor.kt @@ -0,0 +1,99 @@ +package me.redtea.carcadex.genref.plugin + +import com.google.devtools.ksp.KspExperimental +import com.google.devtools.ksp.getAnnotationsByType +import com.google.devtools.ksp.processing.CodeGenerator +import com.google.devtools.ksp.processing.KSPLogger +import com.google.devtools.ksp.symbol.KSFile +import com.google.devtools.ksp.symbol.KSFunctionDeclaration +import com.google.devtools.ksp.symbol.KSVisitorVoid +import com.squareup.kotlinpoet.* +import com.squareup.kotlinpoet.ksp.KotlinPoetKspPreview +import com.squareup.kotlinpoet.ksp.writeTo +import me.redtea.carcadex.genref.plugin.annotations.OnDisable +import me.redtea.carcadex.genref.plugin.annotations.Plugin +import tech.carcadex.kotlinbukkitkit.architecture.KotlinPlugin + +@OptIn(KotlinPoetKspPreview::class) +class PluginVisitor(private val codeGenerator: CodeGenerator, + private val logger: KSPLogger +) : KSVisitorVoid() { + + var funcs: MutableList = mutableListOf() + + override fun visitFile(file: KSFile, data: Unit) { + file.declarations.filter { it is KSFunctionDeclaration }.forEach { it.accept(this, Unit) } + + val packageName = file.packageName.asString() + val className = file.fileName.replace( + ".kt", + "" + ) + "Plugin" + + val fileSpec = FileSpec.builder(packageName, className).apply { + addProperty( + PropertySpec.builder( + "plugin", + ClassName(packageName, className) + ) + .mutable(true) + .setter(FunSpec.builder("set()").addModifiers(KModifier.PRIVATE).build()) + .addModifiers( + listOf( + KModifier.LATEINIT + ) + ) + .build() + ) + addType( + TypeSpec.Companion.classBuilder(className).primaryConstructor( + FunSpec.constructorBuilder().build() + ) + .addFunctions(funcs) + .superclass(KotlinPlugin::class) + .build() + ) + }.build() + try { + fileSpec.writeTo(codeGenerator, true) + } catch (ignored: FileAlreadyExistsException) { + } + } + + @OptIn(KspExperimental::class) + override fun visitFunctionDeclaration(function: KSFunctionDeclaration, data: Unit) { + if (function.getAnnotationsByType(OnDisable::class).any()) disable(function) + + if(function.getAnnotationsByType(Plugin::class).any()) enable(function) + + + function.returnType!!.accept(this, Unit) + } + + fun disable(function: KSFunctionDeclaration) { + funcs.add(FunSpec.builder("onPluginDisable") + .addCode(CodeBlock.of(""" + ${function.simpleName.getShortName()}() + """.trimIndent())) + .returns(Unit::class) + .addModifiers(KModifier.OVERRIDE) + .build()) + + } + + fun enable(function: KSFunctionDeclaration) { + funcs.add(FunSpec.builder("onPluginEnable") + .addCode(CodeBlock.of(""" + plugin = this + ${function.simpleName.getShortName()}() + + """.trimIndent())) + .returns(Unit::class) + .addModifiers(KModifier.OVERRIDE) + .build()) + } + + +} + + diff --git a/genref/src/main/kotlin/me/redtea/carcadex/genref/plugin/annotations/OnDisable.kt b/genref/src/main/kotlin/me/redtea/carcadex/genref/plugin/annotations/OnDisable.kt new file mode 100644 index 0000000..86adc59 --- /dev/null +++ b/genref/src/main/kotlin/me/redtea/carcadex/genref/plugin/annotations/OnDisable.kt @@ -0,0 +1,5 @@ +package me.redtea.carcadex.genref.plugin.annotations + +@Target(AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.SOURCE) +annotation class OnDisable() diff --git a/genref/src/main/kotlin/me/redtea/carcadex/genref/plugin/annotations/Plugin.kt b/genref/src/main/kotlin/me/redtea/carcadex/genref/plugin/annotations/Plugin.kt new file mode 100644 index 0000000..d28356f --- /dev/null +++ b/genref/src/main/kotlin/me/redtea/carcadex/genref/plugin/annotations/Plugin.kt @@ -0,0 +1,5 @@ +package me.redtea.carcadex.genref.plugin.annotations + +@Target(AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.SOURCE) +annotation class Plugin() diff --git a/genref/src/main/kotlin/me/redtea/carcadex/genref/processor/GenRefProcessor.kt b/genref/src/main/kotlin/me/redtea/carcadex/genref/processor/GenRefProcessor.kt new file mode 100644 index 0000000..579909c --- /dev/null +++ b/genref/src/main/kotlin/me/redtea/carcadex/genref/processor/GenRefProcessor.kt @@ -0,0 +1,29 @@ +package me.redtea.carcadex.genref.processor + +import com.google.devtools.ksp.KspExperimental +import com.google.devtools.ksp.isAnnotationPresent +import com.google.devtools.ksp.processing.CodeGenerator +import com.google.devtools.ksp.processing.KSPLogger +import com.google.devtools.ksp.processing.Resolver +import com.google.devtools.ksp.processing.SymbolProcessor +import com.google.devtools.ksp.symbol.KSAnnotated +import com.google.devtools.ksp.symbol.KSFunctionDeclaration +import com.google.devtools.ksp.validate +import me.redtea.carcadex.genref.plugin.PluginVisitor +import me.redtea.carcadex.genref.plugin.annotations.Plugin + +class GenRefProcessor(private val codeGenerator: CodeGenerator, + private val logger: KSPLogger) : SymbolProcessor { + @OptIn(KspExperimental::class) + override fun process(resolver: Resolver): List { + val files = resolver.getAllFiles().filter { it.declarations.any { it is KSFunctionDeclaration && it.validate() + && it.isAnnotationPresent(Plugin::class)} } + + files.let { files -> files.forEach { + it.accept(PluginVisitor(codeGenerator, logger), Unit) + } + } + return files.filter { it.validate() }.toList() + } + +} \ No newline at end of file diff --git a/genref/src/main/kotlin/me/redtea/carcadex/genref/processor/ProcessorProvider.kt b/genref/src/main/kotlin/me/redtea/carcadex/genref/processor/ProcessorProvider.kt new file mode 100644 index 0000000..0c583cc --- /dev/null +++ b/genref/src/main/kotlin/me/redtea/carcadex/genref/processor/ProcessorProvider.kt @@ -0,0 +1,12 @@ +package me.redtea.carcadex.genref.processor + +import com.google.devtools.ksp.processing.SymbolProcessor +import com.google.devtools.ksp.processing.SymbolProcessorEnvironment +import com.google.devtools.ksp.processing.SymbolProcessorProvider + +class ProcessorProvider : SymbolProcessorProvider { + override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor { + return GenRefProcessor(environment.codeGenerator, environment.logger) + } + +} \ No newline at end of file diff --git a/genref/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/genref/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider new file mode 100644 index 0000000..3b1b8f6 --- /dev/null +++ b/genref/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider @@ -0,0 +1 @@ +me.redtea.carcadex.genref.processor.ProcessorProvider \ No newline at end of file diff --git a/messages/.gitignore b/messages/.gitignore new file mode 100644 index 0000000..b63da45 --- /dev/null +++ b/messages/.gitignore @@ -0,0 +1,42 @@ +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/messages/build.gradle.kts b/messages/build.gradle.kts new file mode 100644 index 0000000..4432378 --- /dev/null +++ b/messages/build.gradle.kts @@ -0,0 +1,123 @@ +plugins { + id("java") + kotlin("jvm") + id("maven-publish") + id("signing") +} + +group = "tech.carcadex" +val globalArtifactId = "kotlinbukkitkit-messages" +val globalName = "kotlinbukkitkit-messages" + +repositories { + mavenCentral() + maven { + name = "papermc-repo" + url = uri("https://repo.papermc.io/repository/maven-public/") + } +} + +dependencies { + compileOnly("com.destroystokyo.paper:paper-api:1.16.5-R0.1-SNAPSHOT") + + compileOnly("org.projectlombok:lombok:1.18.26") + annotationProcessor("org.projectlombok:lombok:1.18.26") + + compileOnly("dev.triumphteam:triumph-gui:3.1.2") + + compileOnly("com.github.cryptomorin:XSeries:9.2.0") //add to libs + compileOnly("org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1") + + implementation("net.kyori:adventure-api:4.14.0") + implementation("net.kyori:adventure-platform-bukkit:4.3.0") + implementation(files(parent!!.files("messages/libs/MiniMessages.jar"))) //using universal minimessages, +} + +tasks.test { + useJUnitPlatform() +} + +kotlin { + jvmToolchain(8) +} + +kotlin { + sourceSets.main { + kotlin.srcDir("build/generated/ksp/main/kotlin") + } + sourceSets.test { + kotlin.srcDir("build/generated/ksp/test/kotlin") + } +} + +java { + withJavadocJar() + withSourcesJar() +} + +publishing { + publications { + create("maven") { + artifactId = globalArtifactId + groupId = "tech.carcadex" + version = version + from(components["java"]) + versionMapping { + usage("java-api") { + fromResolutionOf("runtimeClasspath") + } + usage("java-runtime") { + fromResolutionResult() + } + } + pom { + artifactId = globalArtifactId + groupId = "tech.carcadex" + version = version + name.set(globalArtifactId) + description.set(globalArtifactId) + url.set("https://github.com/iRedTea/CarcadeX") + + licenses { + license { + name.set("GNU General Public License v3.0") + url.set("https://www.gnu.org/licenses/gpl-3.0.en.html") + } + } + developers { + developer { + id.set("itzRedTea") + name.set("Red Tea") + email.set("red__tea@outlook.com") + } + } + scm { + connection.set("scm:https://github.com/CarcadeX/$globalName.git") + developerConnection.set("git@github.com:CarcadeX/$globalName.git") + url.set("https://github.com/iRedTea/CarcadeX") + } + } + + } + } + + + + repositories { + maven { + val releasesRepoUrl = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/" + val snapshotsRepoUrl = "https://s01.oss.sonatype.org/content/repositories/snapshots/" + url = uri(if (version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl) + credentials { + username = "itzRedTea" + password = project.findProperty("password").toString() + } + } + + } +} + +signing { + useGpgCmd() + sign(publishing.publications.getByName("maven")) +} diff --git a/messages/libs/MiniMessages.jar b/messages/libs/MiniMessages.jar new file mode 100644 index 0000000..7bdeb9e Binary files /dev/null and b/messages/libs/MiniMessages.jar differ diff --git a/messages/src/main/java/me/redtea/carcadex/messages/container/Messages.java b/messages/src/main/java/me/redtea/carcadex/messages/container/Messages.java new file mode 100644 index 0000000..7cb8ee7 --- /dev/null +++ b/messages/src/main/java/me/redtea/carcadex/messages/container/Messages.java @@ -0,0 +1,257 @@ +package me.redtea.carcadex.messages.container; + +import me.redtea.carcadex.messages.factory.impl.MessageFactoryImpl; +import me.redtea.carcadex.messages.model.Message; +import me.redtea.carcadex.messages.container.impl.MessagesImpl; +import me.redtea.carcadex.messages.factory.MessageFactory; +import me.redtea.carcadex.messages.factory.impl.LegacyFactoryImpl; +import me.redtea.carcadex.messages.model.impl.NullMessage; +import me.redtea.carcadex.messages.verifier.MessageVerifier; +import me.redtea.carcadex.messages.verifier.impl.FileMessageVerifier; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.nio.file.Path; + +/** + *

Container of messages

+ *

Create container by plugin with file messages.yml

+ *

Step 1. Create file messages.yml in resources folder (in jar of plugin)

+ *

Write in file your messages in YAML format. Use MiniMessages format for color messages.
+ * See https://docs.advntr.dev/minimessage/format.html

+ *

For example I will be use this content:

+ *
+ * {@code
+ * error: "Error!"
+ * someRoot:
+ *      message1: "Message"
+ *      message2:
+ *          - "Message start!"
+ *          - "Message end!"
+ *
+ * }
+ * 
+ *

Step 2. Load Messages container in your onEnable method

+ *
+ * {@code
+ * Messages messages = Messages.of(this);
+ * }
+ * 
+ *

Step 3. Done! Now you can use the messages variable to manage messages.

+ *

Create container by plugin with custom file

+ *

Everything is the same, the same, only in jar create a file with the name you need (or several such files) + * and change OnEnable like this:

+ *
+ * {@code
+ * Messages messages = Messages.of(this, "YOURFILENAME.yml");
+ * }
+ * 
+ *

Create container by ConfigurationSection

+ *

Here are a couple of examples of how this can be done:

+ *
+ * {@code
+ * Messages messages = Messages.of(getConfig().getConfigurationSection("messages"));
+ *
+ * //or so
+ *
+ * ConfigurationSection section = ...;
+ * Messages messages = Messages.of(section);
+ * }
+ * 
+ * Reloadable Container: + * section - ConfigurationSection with messages + * file - File with messages + * SectionProvider - provider of section with messages + * @since 1.0.0 + * @author itzRedTea + */ +public interface Messages { + /** + *

Get message from container by key/yaml path

+ * + *

For example

+ *

Yaml structure:

+ *
+     * {@code
+     * error: "Error!"
+     * someRoot:
+     *      message1: "Message"
+     *      message2:
+     *          - "Message start!"
+     *          - "Message end!"
+     *
+     * }
+     * 
+ * + *

Getting messages in code:

+ *
+     * {@code
+     * Messages messages = ...; //Init Messages
+     * CommandSender sender = ...; //Getting CommandSender
+     * messages.get("error").send(sender); //Getting error message
+     * message.get("someRoot.message1").send(sender); //Getting someRoot.message1 message
+     * message.get("someRoot.message2").send(sender); //Getting someRoot.message2 message
+     * }
+     * 
+ * + * @param key key of message + * @return Message + * @see Message + */ + @NotNull Message get(@NotNull String key); + + /** + *

Put message to container

+ * @param key key/yaml path to message + * @param message message to mut + * @return putted message + * @see Message + */ + @NotNull Message put(@NotNull String key, @NotNull Message message); + + /** + * @param key key/yaml path to message + * @return is there a message with such a key in the container? + */ + boolean has(String key); + + /** + *

Reload container with messages in section from args.

+ * @param section - ConfigurationSection with messages + */ + void reload(@NotNull ConfigurationSection section); + + /** + *

Sets custom message parse factory.

+ */ + void factory(@NotNull MessageFactory messageFactory); + + /** + *

Parses all messages in stored section

+ */ + void parse(); + + void verifier(MessageVerifier verifier); + + /** + *

Create container using configuration section

+ * Create container using messages from configuration section. + *
+     * {@code
+     * ConfigurationSection section = ...;
+     * Messages messages = Messages.of(section);
+     * }
+     * 
+ * @param section - configuration section with messages + * @return container with messages + */ + static Messages of(ConfigurationSection section, MessageFactory factory) { + return new MessagesImpl(section, factory); + } + + /** + *

Create container using current plugin and custom filename

+ * Create container using file with your name from jar entry by default. + * A new file in plugin folder will be created with content from your file. + *
+     * {@code
+     * Messages messages = Messages.of(this, "YOURFILENAME.yml");
+     * }
+     * 
+ * @param plugin - plugin that creates a container + * @return container with messages + */ + static Messages of(String fileName, Plugin plugin) { + File file = new File(plugin.getDataFolder(), fileName); + if(!file.exists()) plugin.saveResource(fileName, false); + Messages messages = of(YamlConfiguration.loadConfiguration(file), new MessageFactoryImpl(plugin)); + messages.verifier(new FileMessageVerifier(plugin.getResource(fileName), file)); + return messages; + } + + /** + *

Create container using current plugin

+ * Create container using messages.yml file by default from jar entry. + * A new file in plugin folder will be created with content from messages.yml + *
+     * {@code
+     * Messages messages = Messages.of(this);
+     * }
+     * 
+ * @param plugin - plugin that creates a container + * @return container with messages + */ + static Messages of(Plugin plugin) { + return of("messages.yml", plugin); + } + + /** + *

Create container using file

+ *
+     * {@code
+     * File file = new File("msg_file.yml");
+     * Messages messages = Messages.of(file);
+     * }
+     * 
+ * @param file - file with messages + * @return container with messages + */ + static Messages of(File file, Plugin plugin) { + return of(YamlConfiguration.loadConfiguration(file), new MessageFactoryImpl(plugin)); + } + + /** + *

Create container using path

+ * + * @param path - file with messages + * @return container with messages + * @see Messages#of(File, Plugin) + */ + static Messages of(Path path, Plugin plugin) { + return of(path.toFile(), plugin); + } + + /** + * Only legacy color codes. For versions lower than 1.18 + * @return container with messages + */ + static Messages legacy(ConfigurationSection section, Plugin plugin) { + return new MessagesImpl(section, new LegacyFactoryImpl(plugin)); + } + + /** @see Messages#legacy(ConfigurationSection, Plugin) */ + static Messages legacy(String fileName, Plugin plugin) { + File file = new File(plugin.getDataFolder(), fileName); + if(!file.exists()) plugin.saveResource(fileName, false); + Messages messages = new MessagesImpl(file, new LegacyFactoryImpl(plugin)); + messages.verifier(new FileMessageVerifier(plugin.getResource(fileName), file)); + return messages; + } + + /** @see Messages#legacy(ConfigurationSection, Plugin) */ + static Messages legacy(Plugin plugin) { + return legacy("messages.yml", plugin); + } + + /** @see Messages#legacy(ConfigurationSection, Plugin) */ + static Messages legacy(File file, Plugin plugin) { + return new MessagesImpl(file, new LegacyFactoryImpl(plugin)); + } + + /** + * @return empty messages container + * Use this with Reloader + */ + static @NotNull Messages empty() { + return new MessagesImpl(); + } + + /** + * Singleton NULL_MESSAGE + * @see NullMessage + */ + Message NULL_MESSAGE = new NullMessage(); +} diff --git a/messages/src/main/java/me/redtea/carcadex/messages/container/impl/MessagesImpl.java b/messages/src/main/java/me/redtea/carcadex/messages/container/impl/MessagesImpl.java new file mode 100644 index 0000000..4945ece --- /dev/null +++ b/messages/src/main/java/me/redtea/carcadex/messages/container/impl/MessagesImpl.java @@ -0,0 +1,120 @@ +package me.redtea.carcadex.messages.container.impl; + +import me.redtea.carcadex.messages.container.Messages; +import me.redtea.carcadex.messages.model.Message; +import me.redtea.carcadex.messages.factory.MessageFactory; +import me.redtea.carcadex.messages.model.impl.NullMessage; +import me.redtea.carcadex.messages.verifier.MessageVerifier; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.util.*; + +public class MessagesImpl implements Messages { + private Map messages; + + private MessageFactory factory; + + private final Message NULL_MESSAGE; + + private ConfigurationSection section; + + @Nullable + private MessageVerifier verifier; + + public MessagesImpl() { + NULL_MESSAGE = new NullMessage(); + } + + public MessagesImpl(@NotNull ConfigurationSection section, MessageFactory factory) { + this.section = section; + this.factory = factory; + NULL_MESSAGE = factory.nullMessage(); + parse(); + } + + public MessagesImpl(@NotNull File file, MessageFactory factory) { + this(YamlConfiguration.loadConfiguration(file), factory); + } + + @Override + public @NotNull Message get(@NotNull String key) { + if(!has(key) || messages.get(key) == null) { + if(verifier != null) { + Optional def = verifier.fromDefault(key); + if(def.isPresent()) { + Message message = def.get() instanceof List ? factory.message((List) def.get()) + : factory.message(def.get().toString()); + messages.put(key, message); + return message; + } + return NULL_MESSAGE; + } + return NULL_MESSAGE; + } + return messages.get(key) == null ? NULL_MESSAGE : messages.get(key); + } + + @Override + public @NotNull Message put(@NotNull String key, @NotNull Message message) { + if(!has(key)) return NULL_MESSAGE; + return Objects.requireNonNull(messages.put(key, message)); + } + + @Override + public boolean has(String key) { + if(key == null) return false; + return messages.containsKey(key); + } + + @Override + public void reload(@NotNull ConfigurationSection section) { + messages = fromConfigurationToMap(section); + } + + @Override + public void factory(@NotNull MessageFactory messageFactory) { + this.factory = messageFactory; + } + + @Override + public void parse() { + if(section != null) messages = fromConfigurationToMap(section); + } + + @Override + public void verifier(MessageVerifier verifier) { + this.verifier = verifier; + } + + + + private Map fromConfigurationToMap(@NotNull ConfigurationSection section) { + Map data = new HashMap<>(); + section.getKeys(false).forEach(key -> { + if (section.isConfigurationSection(key)) { + Map newMessages = fromConfigurationToMap(Objects. + requireNonNull(section.getConfigurationSection(key))); + newMessages.forEach((keyMessage, message) -> data.put(key + "." + keyMessage, message)); + } else { + fromSection(section, key).ifPresent(value -> data.put(key, value)); + } + }); + + return data; + } + + private Optional fromSection(@NotNull ConfigurationSection section, String key) { + Message message = null; + if (section.isString(key)) { + message = factory.message(section.getString(key)); + } else if (section.isList(key)) { + message = factory.message(section.getStringList(key)); + } + return Optional.ofNullable(message); + } + +} diff --git a/messages/src/main/java/me/redtea/carcadex/messages/factory/MessageFactory.java b/messages/src/main/java/me/redtea/carcadex/messages/factory/MessageFactory.java new file mode 100644 index 0000000..fb0d5b1 --- /dev/null +++ b/messages/src/main/java/me/redtea/carcadex/messages/factory/MessageFactory.java @@ -0,0 +1,17 @@ +package me.redtea.carcadex.messages.factory; + +import me.redtea.carcadex.messages.container.Messages; +import me.redtea.carcadex.messages.model.Message; + +import java.util.List; + +public interface MessageFactory { + Message message(List unparsed); + Message message(String msg); + Message legacyMessage(List unparsed); + Message legacyMessage(String msg); + + default Message nullMessage() { + return Messages.NULL_MESSAGE; + } +} diff --git a/messages/src/main/java/me/redtea/carcadex/messages/factory/impl/LegacyFactoryImpl.java b/messages/src/main/java/me/redtea/carcadex/messages/factory/impl/LegacyFactoryImpl.java new file mode 100644 index 0000000..f78d45e --- /dev/null +++ b/messages/src/main/java/me/redtea/carcadex/messages/factory/impl/LegacyFactoryImpl.java @@ -0,0 +1,45 @@ +package me.redtea.carcadex.messages.factory.impl; + +import com.google.common.collect.Lists; +import me.redtea.carcadex.messages.model.Message; +import me.redtea.carcadex.messages.utils.MD5ColorUtils; +import me.redtea.carcadex.messages.factory.MessageFactory; +import me.redtea.carcadex.messages.model.impl.serialized.impl.LegacyMessage; +import org.bukkit.plugin.Plugin; + +import java.util.List; +import java.util.stream.Collectors; + +public class LegacyFactoryImpl implements MessageFactory { + private final Plugin plugin; + + public LegacyFactoryImpl(Plugin plugin) { + this.plugin = plugin; + } + + @Override + public Message message(List unparsed) { + if(unparsed == null || unparsed.isEmpty()) return nullMessage(); + if(unparsed.stream().anyMatch(it -> it.contains("&"))) unparsed = + unparsed.stream() + .map(MD5ColorUtils::translateHexColorCodes) + .collect(Collectors.toList()); + return new LegacyMessage(unparsed, plugin); + } + + @Override + public Message message(String msg) { + if(msg == null) return nullMessage(); + return message(Lists.newArrayList(msg)); + } + + @Override + public Message legacyMessage(List unparsed) { + return message(unparsed); + } + + @Override + public Message legacyMessage(String msg) { + return message(Lists.newArrayList(msg)); + } +} diff --git a/messages/src/main/java/me/redtea/carcadex/messages/factory/impl/MessageFactoryImpl.java b/messages/src/main/java/me/redtea/carcadex/messages/factory/impl/MessageFactoryImpl.java new file mode 100644 index 0000000..854805d --- /dev/null +++ b/messages/src/main/java/me/redtea/carcadex/messages/factory/impl/MessageFactoryImpl.java @@ -0,0 +1,46 @@ +package me.redtea.carcadex.messages.factory.impl; + +import com.google.common.collect.Lists; +import me.redtea.carcadex.messages.model.Message; +import me.redtea.carcadex.messages.utils.MD5ColorUtils; +import me.redtea.carcadex.messages.factory.MessageFactory; +import me.redtea.carcadex.messages.model.impl.ComponentMessage; +import me.redtea.carcadex.messages.model.impl.serialized.impl.LegacyMessage; +import org.bukkit.plugin.Plugin; + +import java.util.List; +import java.util.stream.Collectors; + +public class MessageFactoryImpl implements MessageFactory { + private final Plugin plugin; + + public MessageFactoryImpl(Plugin plugin) { + this.plugin = plugin; + } + + @Override + public Message message(List unparsed) { + if(unparsed == null || unparsed.isEmpty()) return nullMessage(); + return new ComponentMessage(unparsed, plugin); + } + + @Override + public Message message(String msg) { + return message(Lists.newArrayList(msg)); + } + + @Override + public Message legacyMessage(List unparsed) { + if(unparsed == null || unparsed.isEmpty()) return nullMessage(); + if(unparsed.stream().anyMatch(it -> it.contains("&"))) unparsed = + unparsed.stream() + .map(MD5ColorUtils::translateHexColorCodes) + .collect(Collectors.toList()); + return new LegacyMessage(unparsed, plugin); + } + + @Override + public Message legacyMessage(String msg) { + return legacyMessage(Lists.newArrayList(msg)); + } +} diff --git a/messages/src/main/java/me/redtea/carcadex/messages/kotlin/MessagesExtensions.kt b/messages/src/main/java/me/redtea/carcadex/messages/kotlin/MessagesExtensions.kt new file mode 100644 index 0000000..77afa94 --- /dev/null +++ b/messages/src/main/java/me/redtea/carcadex/messages/kotlin/MessagesExtensions.kt @@ -0,0 +1,32 @@ +package me.redtea.carcadex.messages.kotlin + +import me.redtea.carcadex.messages.container.Messages +import me.redtea.carcadex.messages.model.Message +import me.redtea.carcadex.messages.verifier.MessageVerifier +import org.bukkit.command.CommandSender +import org.bukkit.plugin.Plugin +import java.io.File + +fun CommandSender.send(message: Message) = message.send(this) + +fun messages(init: MessagesBuilder.() -> Unit): Messages { + val builder = MessagesBuilder() + builder.init() + return builder.build() +} + +class MessagesBuilder(var legacy: Boolean = false, + var plugin: Plugin? = null, + var fileName: String? = null, + var verifier: MessageVerifier? = null, + var file: File? = null) { + fun build(): Messages { + if(plugin == null) throw RuntimeException("Missed plugin") + return if(file == null) { + return (if(legacy) Messages.legacy(fileName ?: "messages.yml", plugin!!) else Messages.of(fileName ?: "messages.yml", plugin!!)).also { m -> verifier?.let { m.verifier(it) } } + } else return (if(legacy) Messages.legacy(file!!, plugin!!) else Messages.of(file!!, plugin!!)).also { m -> verifier?.let { m.verifier(it) } } + } +} + +fun Plugin.loadMessages(): Messages = Messages.of(this) +fun Plugin.loadMessages(filename: String): Messages = Messages.of(filename, this) \ No newline at end of file diff --git a/messages/src/main/java/me/redtea/carcadex/messages/kotlin/TriumphGuiExtensions.kt b/messages/src/main/java/me/redtea/carcadex/messages/kotlin/TriumphGuiExtensions.kt new file mode 100644 index 0000000..5105cf6 --- /dev/null +++ b/messages/src/main/java/me/redtea/carcadex/messages/kotlin/TriumphGuiExtensions.kt @@ -0,0 +1,8 @@ +package me.redtea.carcadex.messages.kotlin + +import dev.triumphteam.gui.builder.item.ItemBuilder +import me.redtea.carcadex.messages.model.Message + +fun ItemBuilder.name(message: Message) = this.name(message.asComponent()) + +fun ItemBuilder.lore(message: Message) = this.lore(message.asComponentList()) \ No newline at end of file diff --git a/messages/src/main/java/me/redtea/carcadex/messages/model/AbstractMessage.java b/messages/src/main/java/me/redtea/carcadex/messages/model/AbstractMessage.java new file mode 100644 index 0000000..4d8404d --- /dev/null +++ b/messages/src/main/java/me/redtea/carcadex/messages/model/AbstractMessage.java @@ -0,0 +1,83 @@ +package me.redtea.carcadex.messages.model; + +import com.cryptomorin.xseries.XMaterial; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.platform.bukkit.BukkitAudiences; +import net.kyori.adventure.text.Component; +import org.bukkit.command.CommandSender; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public abstract class AbstractMessage implements Message { + protected final List unparsed; + + protected List parsed = null; + + protected final BukkitAudiences adventure; + + public AbstractMessage(List unparsed, Plugin plugin) { + this.unparsed = unparsed; + adventure = BukkitAudiences.create(plugin); + } + + protected abstract Component parse(String unparsed); + + private void parse() { + parsed = new ArrayList<>(); + for(String s : unparsed) { + parsed.add(parse(s)); + } + } + + @Override + public void send(@NotNull CommandSender sender) { + parse(); + Audience audience = adventure.sender(sender); + parsed.forEach(audience::sendMessage); + } + + private Component cacheComponent = null; + + @Override + @NotNull + public List asComponentList() { + parse(); + return parsed; + } + + @Override + public @NotNull Component asComponent() { + if(cacheComponent == null) { + parse(); + Component first = parsed.get(0); + for(int i = 1; i < parsed.size(); i++) { + first = first.append(parsed.get(i)).append(Component.newline()); + } + cacheComponent = first; + } + return cacheComponent; + } + + @Override + @NotNull + public List asUnparsedStringList() { + return unparsed; + } + + private String cacheString = null; + @Override + @NotNull + public String asUnparsedString(@NotNull String newLine) { + if(cacheString == null) cacheString = String.join(newLine, unparsed); + return cacheString; + } + + @Override + @NotNull + public XMaterial toXMaterial() { + return XMaterial.valueOf(asUnparsedString("")); + } +} diff --git a/messages/src/main/java/me/redtea/carcadex/messages/model/Message.java b/messages/src/main/java/me/redtea/carcadex/messages/model/Message.java new file mode 100644 index 0000000..8df3d69 --- /dev/null +++ b/messages/src/main/java/me/redtea/carcadex/messages/model/Message.java @@ -0,0 +1,79 @@ +package me.redtea.carcadex.messages.model; + +import com.cryptomorin.xseries.XMaterial; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.ComponentLike; +import org.bukkit.Material; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + *

Message

+ * This class provides message management methods. You can also convert it to Component. + * @since 1.0.0 + * @author itzRedTea + */ +public interface Message extends ComponentLike { + /** + * Send message to CommandSender + * @param sender recipient of the message + */ + void send(@NotNull CommandSender sender); + + /** + * Replaces all matches in unparsed message + * @param from what you want to replace + * @param to what do you want to replace + * @return copy of the Message with the changes applied to it + */ + @NotNull + Message replaceAll(@NotNull String from, @NotNull String to); + + /** + * Convert message to list of components + * @return list of components + */ + @NotNull + List asComponentList(); + /** + * Get unparsed value of message + */ + @NotNull + List asUnparsedStringList(); + /** + * Get unparsed value of message joined to one string + */ + @NotNull + String asUnparsedString(@NotNull String newLine); + + /** + * Convert message to XMaterial + * @return found material + */ + @NotNull + XMaterial toXMaterial(); + + /** + * @see Message#toXMaterial() + */ + default Material toMaterial() { + return toXMaterial().parseMaterial(); + } + + /** + * @see Message#asUnparsedString(String) () + */ + default String asUnparsedString() { + return asUnparsedString("\n"); + } + + /** + * @see Message#replaceAll(String, String) + */ + @NotNull + default Message replaceAll(@NotNull Object from, @NotNull Object to) { + return replaceAll(String.valueOf(from), String.valueOf(to)); + } +} diff --git a/messages/src/main/java/me/redtea/carcadex/messages/model/impl/ComponentMessage.java b/messages/src/main/java/me/redtea/carcadex/messages/model/impl/ComponentMessage.java new file mode 100644 index 0000000..bf4abdc --- /dev/null +++ b/messages/src/main/java/me/redtea/carcadex/messages/model/impl/ComponentMessage.java @@ -0,0 +1,31 @@ +package me.redtea.carcadex.messages.model.impl; + +import me.redtea.carcadex.messages.model.AbstractMessage; +import me.redtea.carcadex.messages.model.Message; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.stream.Collectors; + +public class ComponentMessage extends AbstractMessage { + private static final MiniMessage mm = MiniMessage.builder().build();; + private final Plugin plugin; + + public ComponentMessage(List unparsed, Plugin plugin) { + super(unparsed, plugin); + this.plugin = plugin; + } + + @Override + protected Component parse(String unparsed) { + return mm.deserialize(unparsed); + } + + @Override + public @NotNull Message replaceAll(@NotNull String from, @NotNull String to) { + return new ComponentMessage(unparsed.stream().map(it -> it.replaceAll(from, to)).collect(Collectors.toList()), plugin); + } +} diff --git a/messages/src/main/java/me/redtea/carcadex/messages/model/impl/NullMessage.java b/messages/src/main/java/me/redtea/carcadex/messages/model/impl/NullMessage.java new file mode 100644 index 0000000..4d1791b --- /dev/null +++ b/messages/src/main/java/me/redtea/carcadex/messages/model/impl/NullMessage.java @@ -0,0 +1,47 @@ +package me.redtea.carcadex.messages.model.impl; + +import com.cryptomorin.xseries.XMaterial; +import me.redtea.carcadex.messages.model.Message; +import net.kyori.adventure.text.Component; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public final class NullMessage implements Message { + @Override + public void send(@NotNull CommandSender sender) { + + } + + @Override + public @NotNull Message replaceAll(@NotNull String from, @NotNull String to) { + return this; + } + + @Override + public @NotNull List asComponentList() { + return new ArrayList<>(); + } + + @Override + public @NotNull List asUnparsedStringList() { + return new ArrayList<>(); + } + + @Override + public @NotNull String asUnparsedString(@NotNull String newLine) { + return ""; + } + + @Override + public @NotNull XMaterial toXMaterial() { + return XMaterial.AIR; + } + + @Override + public @NotNull Component asComponent() { + return Component.text(""); + } +} diff --git a/messages/src/main/java/me/redtea/carcadex/messages/model/impl/serialized/SerializedMessage.java b/messages/src/main/java/me/redtea/carcadex/messages/model/impl/serialized/SerializedMessage.java new file mode 100644 index 0000000..b6f103c --- /dev/null +++ b/messages/src/main/java/me/redtea/carcadex/messages/model/impl/serialized/SerializedMessage.java @@ -0,0 +1,33 @@ +package me.redtea.carcadex.messages.model.impl.serialized; + +import me.redtea.carcadex.messages.model.AbstractMessage; +import me.redtea.carcadex.messages.model.Message; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.serializer.ComponentSerializer; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.stream.Collectors; + +public class SerializedMessage extends AbstractMessage { + private final ComponentSerializer serializer; + private final Plugin plugin; + + public SerializedMessage(List unparsed, ComponentSerializer serializer, Plugin plugin) { + super(unparsed, plugin); + this.serializer = serializer; + this.plugin = plugin; + } + + @Override + protected Component parse(String unparsed) { + return serializer.deserialize(unparsed); + } + + @Override + public @NotNull Message replaceAll(@NotNull String from, @NotNull String to) { + return new SerializedMessage(unparsed.stream().map(it -> it.replaceAll(from, to)).collect(Collectors.toList()), serializer, plugin); + } +} diff --git a/messages/src/main/java/me/redtea/carcadex/messages/model/impl/serialized/impl/LegacyMessage.java b/messages/src/main/java/me/redtea/carcadex/messages/model/impl/serialized/impl/LegacyMessage.java new file mode 100644 index 0000000..a1fcf02 --- /dev/null +++ b/messages/src/main/java/me/redtea/carcadex/messages/model/impl/serialized/impl/LegacyMessage.java @@ -0,0 +1,13 @@ +package me.redtea.carcadex.messages.model.impl.serialized.impl; + +import me.redtea.carcadex.messages.model.impl.serialized.SerializedMessage; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import org.bukkit.plugin.Plugin; + +import java.util.List; + +public class LegacyMessage extends SerializedMessage { + public LegacyMessage(List unparsed, Plugin plugin) { + super(unparsed, LegacyComponentSerializer.builder().build(), plugin); + } +} diff --git a/messages/src/main/java/me/redtea/carcadex/messages/utils/MD5ColorUtils.java b/messages/src/main/java/me/redtea/carcadex/messages/utils/MD5ColorUtils.java new file mode 100644 index 0000000..b27c6da --- /dev/null +++ b/messages/src/main/java/me/redtea/carcadex/messages/utils/MD5ColorUtils.java @@ -0,0 +1,26 @@ +package me.redtea.carcadex.messages.utils; + +import net.md_5.bungee.api.ChatColor; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static net.md_5.bungee.api.ChatColor.COLOR_CHAR; + +public class MD5ColorUtils { + private static final Pattern HEX_PATTERN = Pattern.compile("&#([a-f0-9]{6})"); + + public static String translateHexColorCodes(String message) { + Matcher matcher = HEX_PATTERN.matcher(message); + StringBuffer buffer = new StringBuffer(message.length() + 4 * 8); + while (matcher.find()) { + String group = matcher.group(1); + matcher.appendReplacement(buffer, COLOR_CHAR + "x" + + COLOR_CHAR + group.charAt(0) + COLOR_CHAR + group.charAt(1) + + COLOR_CHAR + group.charAt(2) + COLOR_CHAR + group.charAt(3) + + COLOR_CHAR + group.charAt(4) + COLOR_CHAR + group.charAt(5) + ); + } + return ChatColor.translateAlternateColorCodes('&', matcher.appendTail(buffer).toString()); + } +} diff --git a/messages/src/main/java/me/redtea/carcadex/messages/verifier/MessageVerifier.java b/messages/src/main/java/me/redtea/carcadex/messages/verifier/MessageVerifier.java new file mode 100644 index 0000000..4e7692a --- /dev/null +++ b/messages/src/main/java/me/redtea/carcadex/messages/verifier/MessageVerifier.java @@ -0,0 +1,7 @@ +package me.redtea.carcadex.messages.verifier; + +import java.util.Optional; + +public interface MessageVerifier { + Optional fromDefault(String key); +} diff --git a/messages/src/main/java/me/redtea/carcadex/messages/verifier/impl/FileMessageVerifier.java b/messages/src/main/java/me/redtea/carcadex/messages/verifier/impl/FileMessageVerifier.java new file mode 100644 index 0000000..3a089e9 --- /dev/null +++ b/messages/src/main/java/me/redtea/carcadex/messages/verifier/impl/FileMessageVerifier.java @@ -0,0 +1,39 @@ +package me.redtea.carcadex.messages.verifier.impl; + +import lombok.SneakyThrows; +import me.redtea.carcadex.messages.verifier.MessageVerifier; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Optional; + +public class FileMessageVerifier implements MessageVerifier { + private final ConfigurationSection defaultSection; + + private final FileConfiguration toSaveSection; + private final File toSaveFile; + + @SneakyThrows + public FileMessageVerifier(InputStream defaultResource, File toSaveFile) { + defaultSection = YamlConfiguration.loadConfiguration(new InputStreamReader(defaultResource, "UTF-8")); + this.toSaveSection = YamlConfiguration.loadConfiguration(toSaveFile); + this.toSaveFile = toSaveFile; + } + + @Override + public Optional fromDefault(String key) { + Optional o = Optional.ofNullable(defaultSection.get(key)); + o.ifPresent(s -> saveToMain(key, s)); + return o; + } + + @SneakyThrows + public void saveToMain(String path, Object obj) { + toSaveSection.set(path, obj); + toSaveSection.save(toSaveFile); + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 07dc374..44eb7f6 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -4,3 +4,5 @@ include("architecture") include("messages") include("extensions") include("serialization") +include("genref") +include("messages")