Added genref and messages modules

This commit is contained in:
redtea committed 2023-08-22 00:41:20 +03:00
1 parent ff6dc22d00
commit 1d45c1b718
30 files changed
+1395

No files matched your search

+2
View File
@@ -32,4 +32,6 @@ tasks.register("publishAll") {
dependsOn(tasks.getByPath("architecture:publish")) dependsOn(tasks.getByPath("architecture:publish"))
dependsOn(tasks.getByPath("extensions:publish")) dependsOn(tasks.getByPath("extensions:publish"))
dependsOn(tasks.getByPath("serialization:publish")) dependsOn(tasks.getByPath("serialization:publish"))
dependsOn(tasks.getByPath("genref:publish"))
dependsOn(tasks.getByPath("messages:publish"))
} }
+42
View File
@@ -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
+34
View File
@@ -0,0 +1,34 @@
Instruction for kotlin v. 1.8.21
<h3>Step 1 - configure build.gradle</h3>
<p>Add <b>Kotlin 1.8.21</b> and <b>KSP</b> plugins:</p>
```kotlin
kotlin("jvm") version "1.8.21"
id("com.google.devtools.ksp") version "1.8.21-1.0.11"
```
Add <b>CarcadeX GenRef</b> and <b>KotlinBukkitAPI</b> to project:</p>
```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]")
}
```
<h3>Step 2 - create plugin functions</h3>
```kotlin
@Plugin
fun KotlinPlugin.start() {
// Plugin enable logic
}
//Optional
@OnDisable
fun KotlinPlugin.stop() {
// Disable enable logic
}
```
+116
View File
@@ -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<MavenPublication>("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"))
}
@@ -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<FunSpec> = 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())
}
}
@@ -0,0 +1,5 @@
package me.redtea.carcadex.genref.plugin.annotations
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.SOURCE)
annotation class OnDisable()
@@ -0,0 +1,5 @@
package me.redtea.carcadex.genref.plugin.annotations
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.SOURCE)
annotation class Plugin()
@@ -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<KSAnnotated> {
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()
}
}
@@ -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)
}
}
@@ -0,0 +1 @@
me.redtea.carcadex.genref.processor.ProcessorProvider
+42
View File
@@ -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
+123
View File
@@ -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<MavenPublication>("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"))
}
Binary file not shown.
@@ -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;
/**
* <h1>Container of messages</h1>
* <h2>Create container by plugin with file messages.yml</h2>
* <h3>Step 1. Create file messages.yml in resources folder (in jar of plugin)</h3>
* <p>Write in file your messages in YAML format. Use MiniMessages format for color messages. <br>
* See <a href="https://docs.advntr.dev/minimessage/format.html">https://docs.advntr.dev/minimessage/format.html</a></p>
* <p>For example I will be use this content:</p>
* <pre>
* {@code
* error: "<red>Error!"
* someRoot:
* message1: "<green>Message"
* message2:
* - "<green>Message start!"
* - "<green>Message end!"
*
* }
* </pre>
* <h3>Step 2. Load Messages container in your onEnable method</h3>
* <pre>
* {@code
* Messages messages = Messages.of(this);
* }
* </pre>
* <h3>Step 3. Done! Now you can use the messages variable to manage messages.</h3>
* <h2>Create container by plugin with custom file</h2>
* <p>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:</p>
* <pre>
* {@code
* Messages messages = Messages.of(this, "YOURFILENAME.yml");
* }
* </pre>
* <h2>Create container by ConfigurationSection</h2>
* <p>Here are a couple of examples of how this can be done:</p>
* <pre>
* {@code
* Messages messages = Messages.of(getConfig().getConfigurationSection("messages"));
*
* //or so
*
* ConfigurationSection section = ...;
* Messages messages = Messages.of(section);
* }
* </pre>
* 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 {
/**
* <h1>Get message from container by key/yaml path</h1>
*
* <h2>For example</h2>
* <p><b>Yaml structure:</b></p>
* <pre>
* {@code
* error: "<red>Error!"
* someRoot:
* message1: "<green>Message"
* message2:
* - "<green>Message start!"
* - "<green>Message end!"
*
* }
* </pre>
*
* <p><b>Getting messages in code:</b></p>
* <pre>
* {@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
* }
* </pre>
*
* @param key key of message
* @return Message
* @see Message
*/
@NotNull Message get(@NotNull String key);
/**
* <h1>Put message to container</h1>
* @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);
/**
* <h1>Reload container with messages in section from args.</h1>
* @param section - ConfigurationSection with messages
*/
void reload(@NotNull ConfigurationSection section);
/**
* <h1>Sets custom message parse factory.</h1>
*/
void factory(@NotNull MessageFactory messageFactory);
/**
* <h1>Parses all messages in stored section</h1>
*/
void parse();
void verifier(MessageVerifier verifier);
/**
* <h1>Create container using configuration section</h1>
* Create container using messages from configuration section.
* <pre>
* {@code
* ConfigurationSection section = ...;
* Messages messages = Messages.of(section);
* }
* </pre>
* @param section - configuration section with messages
* @return container with messages
*/
static Messages of(ConfigurationSection section, MessageFactory factory) {
return new MessagesImpl(section, factory);
}
/**
* <h1>Create container using current plugin and custom filename</h1>
* 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.
* <pre>
* {@code
* Messages messages = Messages.of(this, "YOURFILENAME.yml");
* }
* </pre>
* @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;
}
/**
* <h1>Create container using current plugin</h1>
* 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
* <pre>
* {@code
* Messages messages = Messages.of(this);
* }
* </pre>
* @param plugin - plugin that creates a container
* @return container with messages
*/
static Messages of(Plugin plugin) {
return of("messages.yml", plugin);
}
/**
* <h1>Create container using file</h1>
* <pre>
* {@code
* File file = new File("msg_file.yml");
* Messages messages = Messages.of(file);
* }
* </pre>
* @param file - file with messages
* @return container with messages
*/
static Messages of(File file, Plugin plugin) {
return of(YamlConfiguration.loadConfiguration(file), new MessageFactoryImpl(plugin));
}
/**
* <h1>Create container using path</h1>
*
* @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();
}
@@ -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<String, Message> 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<Object> def = verifier.fromDefault(key);
if(def.isPresent()) {
Message message = def.get() instanceof List ? factory.message((List<String>) 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<String, Message> fromConfigurationToMap(@NotNull ConfigurationSection section) {
Map<String, Message> data = new HashMap<>();
section.getKeys(false).forEach(key -> {
if (section.isConfigurationSection(key)) {
Map<String, Message> 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<Message> 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);
}
}
@@ -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<String> unparsed);
Message message(String msg);
Message legacyMessage(List<String> unparsed);
Message legacyMessage(String msg);
default Message nullMessage() {
return Messages.NULL_MESSAGE;
}
}
@@ -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<String> 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<String> unparsed) {
return message(unparsed);
}
@Override
public Message legacyMessage(String msg) {
return message(Lists.newArrayList(msg));
}
}
@@ -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<String> 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<String> 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));
}
}
@@ -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)
@@ -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())
@@ -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<String> unparsed;
protected List<Component> parsed = null;
protected final BukkitAudiences adventure;
public AbstractMessage(List<String> 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<Component> 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<String> 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(""));
}
}
@@ -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;
/**
* <h1>Message</h1>
* 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<Component> asComponentList();
/**
* Get unparsed value of message
*/
@NotNull
List<String> 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));
}
}
@@ -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<String> 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);
}
}
@@ -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<Component> asComponentList() {
return new ArrayList<>();
}
@Override
public @NotNull List<String> 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("");
}
}
@@ -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<Component, TextComponent, String> serializer;
private final Plugin plugin;
public SerializedMessage(List<String> unparsed, ComponentSerializer<Component, TextComponent, String> 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);
}
}
@@ -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<String> unparsed, Plugin plugin) {
super(unparsed, LegacyComponentSerializer.builder().build(), plugin);
}
}
@@ -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());
}
}
@@ -0,0 +1,7 @@
package me.redtea.carcadex.messages.verifier;
import java.util.Optional;
public interface MessageVerifier {
Optional<Object> fromDefault(String key);
}
@@ -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<Object> fromDefault(String key) {
Optional<Object> 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);
}
}
+2
View File
@@ -4,3 +4,5 @@ include("architecture")
include("messages") include("messages")
include("extensions") include("extensions")
include("serialization") include("serialization")
include("genref")
include("messages")