Compare commits

..

111 Commits

Author SHA1 Message Date
Auxilor
364550d228 Pain 2022-04-06 13:47:01 +01:00
Auxilor
d5e8cbaf33 Updated to 6.32.1 2022-04-06 13:35:34 +01:00
Auxilor
c4d532fda9 Removed MySQL relocation 2022-04-06 13:34:33 +01:00
Auxilor
aa097cf7e2 Fixed wrong MySQL driver class 2022-04-06 13:08:29 +01:00
Auxilor
6f4ca40a94 Changed load order for custom items 2022-04-05 16:29:13 +01:00
Auxilor
2ef9b4033c Updated to 6.32.0 2022-04-05 15:38:44 +01:00
Auxilor
9aa22ffc86 Added Items#mergeFrom 2022-04-05 15:38:34 +01:00
Auxilor
d81c1e6fcb Added comment 2022-04-05 13:22:13 +01:00
Auxilor
ac72e0770a Undid last commit 2022-04-05 13:17:29 +01:00
Auxilor
5e3e09c4bc Moved adventure api to api configuration 2022-04-05 13:17:07 +01:00
Auxilor
ee945d5901 Updated to 6.31.4 2022-04-05 13:14:48 +01:00
Auxilor
7f747f3afc Fixed & color code 2022-04-05 13:12:49 +01:00
Auxilor
2d47593f51 More build changes 2022-04-05 13:12:29 +01:00
Auxilor
df529ba239 Minimized use of the lib-loader 2022-04-05 13:01:05 +01:00
Auxilor
2fea736631 Updated to 6.31.3 2022-04-02 14:36:23 +01:00
Auxilor
9df4fae2dc Fixed colors in console on new versions of paper 2022-04-02 14:36:14 +01:00
Auxilor
ca964a1a40 Updated to 6.31.2 and fixed default key values 2022-03-29 16:17:59 +01:00
Auxilor
ada2832839 Fixed data.yml 2022-03-28 21:24:34 +01:00
Auxilor
4d92cbb7ff Fixed PersistentDataKeyType equality 2022-03-28 14:33:28 +01:00
Auxilor
d6ab36929b Changed data reading 2022-03-28 14:29:56 +01:00
Auxilor
e06623d3fe Fixed some config constructors 2022-03-28 14:23:27 +01:00
Auxilor
8b70a37459 Updated to 6.31.1 2022-03-28 14:21:19 +01:00
Auxilor
daab3829bc Config changes 2022-03-28 14:21:09 +01:00
Auxilor
d09021707b Fixed name arg parser 2022-03-27 13:20:53 +01:00
Auxilor
bea8cb5757 Updated to 6.31.0 2022-03-27 13:20:21 +01:00
Auxilor
bde546efcb Added Items#toLookupString 2022-03-27 13:19:48 +01:00
Auxilor
e1ffc851a3 Updated to 6.30.2 2022-03-26 16:02:09 +00:00
Auxilor
828229b3e5 Updated to 6.30.1 2022-03-26 15:43:34 +00:00
Auxilor
f36e2f5349 Fixed config header duplication bug 2022-03-26 15:43:24 +00:00
Auxilor
cdbe5b141a Fixed yaml printing 2022-03-24 18:13:11 +00:00
Auxilor
08c3fc0cfa Fixed config removing 2022-03-24 15:53:12 +00:00
Auxilor
8f758fb100 Changed bukkit -> eco config mapping 2022-03-24 12:21:15 +00:00
Auxilor
ec339b0ecd Removed debug println 2022-03-24 12:13:49 +00:00
Auxilor
9dedfb86a5 Fixed more config things 2022-03-24 12:13:02 +00:00
Auxilor
0146cff8d3 More config work 2022-03-24 11:26:41 +00:00
Auxilor
2454d99d84 Reworked getKeys 2022-03-24 10:50:35 +00:00
Auxilor
7d9b1bc266 Removed debug toggle 2022-03-24 10:46:52 +00:00
Auxilor
77c56c46a8 Keep the reworks coming 2022-03-24 10:41:09 +00:00
Auxilor
85303098a7 Reworked configs (again) 2022-03-24 09:46:27 +00:00
Auxilor
821dc62d56 More anti-anchor prevention 2022-03-23 19:44:23 +00:00
Auxilor
f9af4a9e66 Removed debug 2022-03-23 16:26:51 +00:00
Auxilor
41b9d6b01d Config cleanup 2022-03-23 16:24:17 +00:00
Auxilor
7a521acccf More config improvements (and codestyle) 2022-03-23 15:50:25 +00:00
Auxilor
49233aef88 Comment headers are now preserved 2022-03-23 15:32:43 +00:00
Auxilor
c5b47ed073 And they dont stop coming 2022-03-23 14:21:25 +00:00
Auxilor
ddcef7cf0c The config rewrites never stop 2022-03-23 13:53:56 +00:00
Auxilor
0f86e1c1c1 More config rewrites 2022-03-23 12:59:53 +00:00
Auxilor
f0f7e229ea Began complete config rewrite 2022-03-23 12:28:34 +00:00
Auxilor
ea674a3757 Merge remote-tracking branch 'origin/develop' into develop 2022-03-23 10:51:50 +00:00
Auxilor
b7c51eba5e Fixed more config bugs 2022-03-22 19:55:19 +00:00
Auxilor
1127bf1700 Renamed ConfigBuilder to BuildableConfig 2022-03-22 16:59:20 +00:00
Auxilor
376e3284fb Fixed config bugs 2022-03-22 16:53:16 +00:00
Auxilor
12f355b205 Fixed GUI and rendering bugs 2022-03-22 16:46:32 +00:00
Auxilor
f4b02591e8 Clarified javadoc 2022-03-22 16:25:18 +00:00
Auxilor
f843725cf5 Fixed missing method 2022-03-22 16:16:58 +00:00
Auxilor
ed0536c188 Fixed stupidity 2022-03-22 14:14:45 +00:00
Auxilor
849e005095 More oraxen tweaks 2022-03-22 14:12:39 +00:00
Auxilor
927d61dd6b Marked ConfigUpdater as @Documented 2022-03-22 14:09:36 +00:00
Auxilor
9285cffc56 Updated ConfigUpdater javadoc 2022-03-22 14:08:25 +00:00
Auxilor
1b6c90e87d Updated to 6.30.0 2022-03-22 14:07:14 +00:00
Auxilor
c79de6fbc1 Marked more deprecated things as for removal 2022-03-22 14:06:59 +00:00
Auxilor
fc83ebbb34 Removed all long-deprecated config methods 2022-03-22 14:03:02 +00:00
Auxilor
f330cc954c Added ConfigBuilder and more TransientConfig constructors 2022-03-22 14:01:18 +00:00
Auxilor
df4f98251c Improved setting with JSON configs 2022-03-22 12:58:20 +00:00
Auxilor
74861e9c01 Improved config setters 2022-03-22 11:36:16 +00:00
Auxilor
7dad9d7875 Fixed config setters 2022-03-22 11:24:27 +00:00
Auxilor
338b9b2d4e Fixed configs 2022-03-22 11:02:01 +00:00
Auxilor
f6d83867f3 Updated to 6.29.3 2022-03-22 10:41:52 +00:00
Auxilor
bb632ac849 Codestyle 2022-03-22 10:40:02 +00:00
Auxilor
793f946b44 Removed long-deprecated, for-removal configs 2022-03-22 10:39:20 +00:00
Auxilor
519a59cc88 Reworked configs, marked all deprecated configs for removal 2022-03-22 10:35:44 +00:00
Auxilor
79b1bff547 Recoded GUI internals 2022-03-22 09:55:10 +00:00
Auxilor
17ee1ac2ff Updated to 6.29.2 2022-03-20 15:44:06 +00:00
Auxilor
a6fa446d95 Fixed Head Database integration 2022-03-20 15:43:55 +00:00
Auxilor
2f98c0ace5 Updated to 6.29.1 2022-03-18 09:40:35 +00:00
Auxilor
60d7abcda8 Fixed oraxen integration 2022-03-18 09:40:26 +00:00
Auxilor
69a5fa81b4 Updated MythicMobs 2022-03-16 20:48:42 +00:00
Auxilor
0316e627e1 Updated to 6.29.0 2022-03-16 13:22:17 +00:00
Auxilor
5bc5b47bf8 Added Menu#refresh 2022-03-16 13:22:05 +00:00
Auxilor
a9c906843d Updated to 6.28.3 2022-03-13 16:59:14 +00:00
Auxilor
85861971d3 Added wildcard material testable items 2022-03-13 16:59:02 +00:00
Auxilor
bdb24e5a14 Updated to 6.28.2 2022-03-12 21:03:59 +00:00
Auxilor
cb481d4532 Fixed 1.17.1 and 1.18.1 errors 2022-03-12 21:03:45 +00:00
Auxilor
97fba3e243 Updated to 6.28.1 2022-03-11 16:29:15 +00:00
Auxilor
e47c6387a2 Injecting placeholders now clears config cache 2022-03-11 16:29:05 +00:00
Auxilor
00df39097c Fixed statics in expressions 2022-03-11 16:28:21 +00:00
Auxilor
efcb406e9a Revert "Updated to 6.28.1"
This reverts commit 9e92ea6062.
2022-03-11 10:52:36 +00:00
Auxilor
9e92ea6062 Updated to 6.28.1 2022-03-11 10:41:43 +00:00
Auxilor
9dbc25df6f Added PlaceholderInjectable#clearInjectedPlaceholders 2022-03-11 09:01:57 +00:00
Auxilor
bc85c5232e Fixed more placeholder injection stuff 2022-03-11 09:00:24 +00:00
Auxilor
2f4783f13e Fixed placeholder equality 2022-03-11 08:58:58 +00:00
Auxilor
614241a058 Fixed placeholder injection on config wrappers 2022-03-11 08:47:50 +00:00
Auxilor
c541adb557 Improved PlaceholderInjectable 2022-03-10 20:42:06 +00:00
Auxilor
a484eecc8f Reworked placeholder system 2022-03-10 20:38:09 +00:00
Auxilor
3933e38891 Updated to 6.28.0 2022-03-10 19:54:58 +00:00
Auxilor
1d5345b367 Merge remote-tracking branch 'origin/master' into develop
# Conflicts:
#	gradle.properties
2022-03-10 19:54:25 +00:00
Auxilor
f8513ff1e9 Updated to 6.27.4 2022-03-09 11:21:14 +00:00
Auxilor
ca9c940b2b Updated/fixed crunch 2022-03-09 11:20:54 +00:00
Auxilor
af198d30f7 Updated to 6.27.3 2022-03-08 08:25:45 +00:00
Auxilor
637b239c66 Fixed MiniMessage on 1.18.2 2022-03-08 08:25:25 +00:00
Auxilor
124c059294 Fixed MiniMessage on 1.18.2 2022-03-07 12:32:52 +00:00
Auxilor
f1d0bd901d Updated to 6.28.0 2022-03-07 12:29:02 +00:00
Auxilor
20f4bf4e78 Improved shapeless recipe support 2022-03-07 12:27:21 +00:00
Auxilor
595bc76294 Added shapeless recipe support 2022-03-07 11:32:18 +00:00
Auxilor
9a66e78dcd Merge remote-tracking branch 'origin/master' 2022-03-07 10:29:15 +00:00
Auxilor
0fcf229bfb Fixed missing javadoc jar 2022-03-07 10:28:59 +00:00
Will FP
d2ffc43b17 Update README.md 2022-03-07 10:25:50 +00:00
Auxilor
c50f69b372 Fixed javadoc 2022-03-07 10:16:52 +00:00
Will FP
f5eafafc4c Update README.md 2022-03-06 15:50:39 +00:00
Auxilor
39372c9b1a Updated to 6.27.2 2022-03-06 13:59:51 +00:00
Auxilor
4c64f03aa1 Fixed isCustomItem 2022-03-06 13:59:42 +00:00
118 changed files with 2258 additions and 2022 deletions

View File

@@ -39,7 +39,7 @@ and many more.
# For developers
## Javadoc
The 6.13.0 Javadoc can be found [here](https://javadoc.jitpack.io/com/willfp/eco/6.13.0/javadoc/)
The 6.27.2 Javadoc can be found [here](https://javadoc.jitpack.io/com/willfp/eco/6.27.2/javadoc/)
## Plugin Information
@@ -68,7 +68,7 @@ dependencies {
}
```
Replace `Tag` with a release tag for eco, eg `6.13.0`.
Replace `Tag` with a release tag for eco, eg `6.27.2`.
Maven:
@@ -88,7 +88,7 @@ Maven:
</dependency>
```
Replace `Tag` with a release tag for eco, eg `6.13.0`.
Replace `Tag` with a release tag for eco, eg `6.27.2`.
## Build locally:

View File

@@ -85,21 +85,25 @@ allprojects {
}
dependencies {
compileOnly(kotlin("stdlib", version = "1.6.10"))
// Included in spigot jar, no need to move to implementation
compileOnly("org.jetbrains:annotations:23.0.0")
compileOnly("com.google.guava:guava:31.1-jre")
// Test
testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2")
// Adventure
compileOnly("net.kyori:adventure-api:4.10.0")
compileOnly("net.kyori:adventure-text-serializer-gson:4.10.0")
compileOnly("net.kyori:adventure-text-serializer-legacy:4.10.0")
implementation("net.kyori:adventure-api:4.10.1")
implementation("net.kyori:adventure-text-serializer-gson:4.10.1") {
exclude("com.google.code.gson", "gson") // Prevent shading into the jar
}
implementation("net.kyori:adventure-text-serializer-legacy:4.10.1")
// Other
compileOnly("com.google.guava:guava:31.1-jre")
compileOnly("com.github.ben-manes.caffeine:caffeine:3.0.5")
implementation("com.github.ben-manes.caffeine:caffeine:3.0.6")
implementation("org.apache.maven:maven-artifact:3.8.4")
implementation(kotlin("stdlib", version = "1.6.10"))
}
tasks.withType<JavaCompile> {
@@ -115,10 +119,11 @@ allprojects {
exclude(group = "com.darkblade12", module = "particleeffect")
exclude(group = "com.github.cryptomorin", module = "XSeries")
exclude(group = "net.wesjd", module = "anvilgui")
exclude(group = "org.slf4j", module = "slf4j-api")
}
configurations.testImplementation {
setExtendsFrom(listOf(configurations.compileOnly.get()))
setExtendsFrom(listOf(configurations.compileOnly.get(), configurations.implementation.get()))
}
tasks {
@@ -133,6 +138,28 @@ allprojects {
shadowJar {
relocate("org.bstats", "com.willfp.eco.libs.bstats")
relocate("redempt.crunch", "com.willfp.eco.libs.crunch")
relocate("org.apache.commons.lang3", "com.willfp.eco.libs.lang3")
relocate("org.apache.maven", "com.willfp.eco.libs.maven")
relocate("org.checkerframework", "com.willfp.eco.libs.checkerframework")
relocate("org.intellij", "com.willfp.eco.libs.intellij")
relocate("org.jetbrains.annotations", "com.willfp.eco.libs.jetbrains.annotations")
//relocate("org.jetbrains.exposed", "com.willfp.eco.libs.exposed")
relocate("org.objenesis", "com.willfp.eco.libs.objenesis")
relocate("org.reflections", "com.willfp.eco.libs.reflections")
relocate("javassist", "com.willfp.eco.libs.javassist")
relocate("javax.annotation", "com.willfp.eco.libs.annotation")
relocate("com.google.errorprone", "com.willfp.eco.libs.errorprone")
relocate("com.google.j2objc", "com.willfp.eco.libs.j2objc")
relocate("com.google.thirdparty", "com.willfp.eco.libs.google.thirdparty")
relocate("com.google.protobuf", "com.willfp.eco.libs.google.protobuf") // No I don't know either
relocate("google.protobuf", "com.willfp.eco.libs.protobuf") // Still don't know
relocate("com.zaxxer.hikari", "com.willfp.eco.libs.hikari")
//relocate("com.mysql", "com.willfp.eco.libs.mysql")
/*
Kotlin and caffeine are not shaded so that they can be accessed directly by eco plugins.
Also, not relocating adventure, because it's a pain in the ass, and it doesn't *seem* to be causing loader constraint violations.
*/
}
compileJava {

View File

@@ -4,7 +4,6 @@ dependencies {
// Other
compileOnly 'org.spigotmc:spigot-api:1.17.1-R0.1-SNAPSHOT'
compileOnly 'org.apache.maven:maven-artifact:3.8.1'
compileOnly 'com.comphenix.protocol:ProtocolLib:4.6.1-SNAPSHOT'
compileOnly 'com.google.code.gson:gson:2.8.8'
}
@@ -12,6 +11,10 @@ dependencies {
group 'com.willfp'
version rootProject.version
java {
withJavadocJar()
}
build.dependsOn publishToMavenLocal
publishing {

View File

@@ -353,9 +353,9 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
DefaultArtifactVersion mostRecentVersion = new DefaultArtifactVersion(version);
if (!(currentVersion.compareTo(mostRecentVersion) > 0 || currentVersion.equals(mostRecentVersion))) {
this.outdated = true;
this.getLogger().warning("&c" + this.getName() + " is out of date! (Version " + this.getDescription().getVersion() + ")");
this.getLogger().warning("&cThe newest version is &f" + version);
this.getLogger().warning("&cDownload the new version!");
this.getLogger().warning(this.getName() + " is out of date! (Version " + this.getDescription().getVersion() + ")");
this.getLogger().warning("The newest version is " + version);
this.getLogger().warning("Download the new version!");
}
});
}
@@ -489,9 +489,9 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
public final void reload() {
this.getConfigHandler().updateConfigs();
this.getScheduler().cancelAll();
this.getConfigHandler().callUpdate();
this.getConfigHandler().callUpdate(); // Call twice to fix issues
this.getScheduler().cancelAll();
this.handleReload();
@@ -694,7 +694,7 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
public final FileConfiguration getConfig() {
this.getLogger().warning("Call to default config method in eco plugin!");
return Objects.requireNonNull(this.getConfigYml().getBukkitHandle());
return Objects.requireNonNull(this.getConfigYml().toBukkit());
}
/**

View File

@@ -35,7 +35,7 @@ public class Prerequisite {
*
* @deprecated Use {@link EconomyManager#hasRegistrations()} instead.
*/
@Deprecated
@Deprecated(forRemoval = true)
public static final Prerequisite HAS_VAULT = new Prerequisite(
() -> ClassUtils.exists("net.milkbowl.vault.economy.Economy"),
"Requires server to have vault"
@@ -54,7 +54,7 @@ public class Prerequisite {
*
* @deprecated eco no longer supports versions before 1.17.
*/
@Deprecated(since = "6.25.2")
@Deprecated(since = "6.25.2", forRemoval = true)
public static final Prerequisite HAS_1_17 = new Prerequisite(
() -> ProxyConstants.NMS_VERSION.contains("17") || HAS_1_18.isMet(),
"Requires server to be running 1.17+"

View File

@@ -10,6 +10,7 @@ import java.util.List;
/**
* Interface for all command implementations.
*/
@SuppressWarnings("removal")
public interface CommandBase {
/**
* Get command name.
@@ -81,7 +82,7 @@ public interface CommandBase {
* @see CommandHandler
* @deprecated Use {@link CommandBase#onExecute(CommandSender, List)} instead.
*/
@Deprecated
@Deprecated(forRemoval = true)
CommandHandler getHandler();
/**
@@ -91,7 +92,7 @@ public interface CommandBase {
* @see CommandHandler
* @deprecated Handlers have been deprecated.
*/
@Deprecated
@Deprecated(forRemoval = true)
void setHandler(@NotNull CommandHandler handler);
/**
@@ -101,7 +102,7 @@ public interface CommandBase {
* @see TabCompleteHandler
* @deprecated Use {@link CommandBase#tabComplete(CommandSender, List)} instead.
*/
@Deprecated
@Deprecated(forRemoval = true)
TabCompleteHandler getTabCompleter();
/**
@@ -111,6 +112,6 @@ public interface CommandBase {
* @see TabCompleteHandler
* @deprecated Handlers have been deprecated.
*/
@Deprecated
@Deprecated(forRemoval = true)
void setTabCompleter(@NotNull TabCompleteHandler handler);
}

View File

@@ -16,7 +16,7 @@ import java.util.List;
* update to use the new system: {@link CommandBase#onExecute(CommandSender, List)}.
*/
@FunctionalInterface
@Deprecated(since = "6.17.0")
@Deprecated(since = "6.17.0", forRemoval = true)
public interface CommandHandler {
/**
* The code to be called on execution.

View File

@@ -16,7 +16,7 @@ import java.util.List;
* update to use the new system: {@link CommandBase#tabComplete(CommandSender, List)}
*/
@FunctionalInterface
@Deprecated(since = "6.17.0")
@Deprecated(since = "6.17.0", forRemoval = true)
public interface TabCompleteHandler {
/**
* Handle Tab Completion.

View File

@@ -2,8 +2,6 @@ package com.willfp.eco.core.command.impl;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.command.CommandBase;
import com.willfp.eco.core.command.CommandHandler;
import com.willfp.eco.core.command.TabCompleteHandler;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.util.StringUtil;
@@ -23,7 +21,7 @@ import java.util.stream.Collectors;
* in order to execute the command-specific code. It's essentially an internal
* layer, hence why it's a package-private class.
*/
@SuppressWarnings({"DeprecatedIsStillUsed"})
@SuppressWarnings({"DeprecatedIsStillUsed", "removal"})
abstract class HandledCommand implements CommandBase {
/**
* The plugin.
@@ -54,14 +52,14 @@ abstract class HandledCommand implements CommandBase {
*/
@Deprecated
@Nullable
private CommandHandler handler = null;
private com.willfp.eco.core.command.CommandHandler handler = null;
/**
* The tab completion code to be executed in the command.
*/
@Deprecated
@Nullable
private TabCompleteHandler tabCompleter = null;
private com.willfp.eco.core.command.TabCompleteHandler tabCompleter = null;
/**
* All subcommands for the command.
@@ -256,27 +254,27 @@ abstract class HandledCommand implements CommandBase {
return this.subcommands;
}
@Deprecated
@Deprecated(forRemoval = true)
@Override
public @Nullable CommandHandler getHandler() {
public @Nullable com.willfp.eco.core.command.CommandHandler getHandler() {
return this.handler;
}
@Deprecated
@Deprecated(forRemoval = true)
@Override
public @Nullable TabCompleteHandler getTabCompleter() {
public @Nullable com.willfp.eco.core.command.TabCompleteHandler getTabCompleter() {
return this.tabCompleter;
}
@Deprecated
@Deprecated(forRemoval = true)
@Override
public void setHandler(@Nullable final CommandHandler handler) {
public void setHandler(@Nullable final com.willfp.eco.core.command.CommandHandler handler) {
this.handler = handler;
}
@Deprecated
@Deprecated(forRemoval = true)
@Override
public void setTabCompleter(@Nullable final TabCompleteHandler tabCompleter) {
public void setTabCompleter(@Nullable final com.willfp.eco.core.command.TabCompleteHandler tabCompleter) {
this.tabCompleter = tabCompleter;
}
}

View File

@@ -23,13 +23,31 @@ public abstract class BaseConfig extends LoadableConfigWrapper {
@NotNull final PluginLike plugin,
final boolean removeUnused,
@NotNull final ConfigType type) {
this(configName, plugin, removeUnused, type, true);
}
/**
* Create new Base Config.
*
* @param plugin The plugin or extension.
* @param configName The config name (excluding extension).
* @param removeUnused If unused sections should be removed.
* @param type The config type.
* @param requiresChangeToSave If changes must be applied to save the config.
*/
protected BaseConfig(@NotNull final String configName,
@NotNull final PluginLike plugin,
final boolean removeUnused,
@NotNull final ConfigType type,
final boolean requiresChangeToSave) {
super(Eco.getHandler().getConfigFactory().createUpdatableConfig(
configName,
plugin,
"",
plugin.getClass(),
removeUnused,
type
type,
requiresChangeToSave
));
}
}

View File

@@ -0,0 +1,29 @@
package com.willfp.eco.core.config;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Builder for configs to create them programmatically.
*/
public class BuildableConfig extends TransientConfig {
/**
* Create a new empty config builder.
*/
public BuildableConfig() {
super();
}
/**
* Add to the config builder.
*
* @param path The path.
* @param object The object.
* @return The builder.
*/
public BuildableConfig add(@NotNull final String path,
@Nullable final Object object) {
set(path, object);
return this;
}
}

View File

@@ -1,5 +1,7 @@
package com.willfp.eco.core.config;
import org.jetbrains.annotations.NotNull;
/**
* Config types, classified by file extension.
*/
@@ -7,10 +9,28 @@ public enum ConfigType {
/**
* .json config.
*/
JSON,
JSON("json"),
/**
* .yml config.
*/
YAML
YAML("yml");
/**
* The file extension.
*/
private final String extension;
ConfigType(@NotNull final String extension) {
this.extension = extension;
}
/**
* Get the file extension.
*
* @return The extension.
*/
public String getExtension() {
return extension;
}
}

View File

@@ -38,6 +38,7 @@ public abstract class ExtendableConfig extends LoadableConfigWrapper {
source,
removeUnused,
type,
true,
updateBlacklist
));
}

View File

@@ -26,7 +26,8 @@ public abstract class StaticBaseConfig extends LoadableConfigWrapper {
plugin,
"",
plugin.getClass(),
type
type,
true
));
}
}

View File

@@ -3,12 +3,17 @@ package com.willfp.eco.core.config;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.config.wrapper.ConfigWrapper;
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.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.Map;
/**
@@ -18,10 +23,19 @@ import java.util.Map;
*/
public class TransientConfig extends ConfigWrapper<Config> {
/**
* @param config The ConfigurationSection handle.
*/
public TransientConfig(@NotNull final ConfigurationSection config) {
super(Eco.getHandler().getConfigFactory().createConfig(config));
}
/**
* Exists for backwards compatibility, YamlConfigurations are ConfigurationSections.
*
* @param config The YamlConfiguration handle.
*/
public TransientConfig(@NotNull final YamlConfiguration config) {
super(Eco.getHandler().getConfigFactory().createConfig(config));
this((ConfigurationSection) config);
}
/**
@@ -33,20 +47,50 @@ public class TransientConfig extends ConfigWrapper<Config> {
)) : new TransientConfig());
}
/**
* @param file The File.
* @deprecated Specify the config type to prevent bugs.
*/
@Deprecated(since = "6.30.0", forRemoval = true)
public TransientConfig(@Nullable final File file) {
this(file, ConfigType.YAML);
}
/**
* @param file The file.
* @param type The config type to try read from.
*/
public TransientConfig(@Nullable final File file,
@NotNull final ConfigType type) {
super(file != null ? Eco.getHandler().getConfigFactory().createConfig(readFile(file), type)
: new TransientConfig());
}
/**
* Create a new empty transient config.
*
* @param values The values.
*/
public TransientConfig(@NotNull final Map<String, Object> values) {
super(Eco.getHandler().getConfigFactory().createConfig(values));
super(Eco.getHandler().getConfigFactory().createConfig(values, ConfigType.YAML));
}
/**
* Create a new empty transient config.
*
* @param values The values.
* @param type The type.
*/
public TransientConfig(@NotNull final Map<String, Object> values,
@NotNull final ConfigType type) {
super(Eco.getHandler().getConfigFactory().createConfig(values, type));
}
/**
* Create a new empty transient config.
*/
public TransientConfig() {
super(Eco.getHandler().getConfigFactory().createConfig("", ConfigType.YAML));
this(new HashMap<>(), ConfigType.JSON);
}
/**
@@ -57,4 +101,22 @@ public class TransientConfig extends ConfigWrapper<Config> {
@NotNull final ConfigType type) {
super(Eco.getHandler().getConfigFactory().createConfig(contents, type));
}
/**
* Read a file to a string.
*
* @param file The file.
* @return The string.
*/
private static String readFile(@Nullable final File file) {
if (file == null) {
return "";
}
try {
return Files.readString(file.toPath());
} catch (IOException e) {
return "";
}
}
}

View File

@@ -1,16 +1,25 @@
package com.willfp.eco.core.config.interfaces;
import com.willfp.eco.core.config.BuildableConfig;
import com.willfp.eco.core.config.ConfigType;
import com.willfp.eco.core.config.TransientConfig;
import com.willfp.eco.core.placeholder.PlaceholderInjectable;
import com.willfp.eco.core.placeholder.StaticPlaceholder;
import com.willfp.eco.util.NumberUtils;
import com.willfp.eco.util.StringUtils;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
/**
* All configs implement this interface.
@@ -18,11 +27,16 @@ import java.util.Objects;
* Contains all methods that must exist in yaml and json configurations.
*/
@SuppressWarnings("unused")
public interface Config extends Cloneable {
public interface Config extends Cloneable, PlaceholderInjectable {
/**
* Clears cache.
* <p>
* Configs no longer have caches as they have in previous versions.
*/
void clearCache();
@Deprecated(since = "6.31.1", forRemoval = true)
default void clearCache() {
// Do nothing.
}
/**
* Convert the config into readable text.
@@ -48,6 +62,19 @@ public interface Config extends Cloneable {
@NotNull
List<String> getKeys(boolean deep);
/**
* Recurse config keys.
*
* @param found The found keys.
* @param root The root.
* @return The keys.
*/
@NotNull
default List<String> recurseKeys(@NotNull Set<String> found,
@NotNull String root) {
return new ArrayList<>();
}
/**
* Get an object from config.
* Default implementations call {@link org.bukkit.configuration.file.YamlConfiguration#get(String)}.
@@ -238,35 +265,6 @@ public interface Config extends Cloneable {
return getString(path, false, StringUtils.FormatOption.WITHOUT_PLACEHOLDERS);
}
/**
* Get a string from config.
*
* @param path The key to fetch the value from.
* @param format If the string should be formatted.
* @return The found value, or an empty string if not found.
* @deprecated Since 6.18.0, {@link Config#getString(String)} is not formatted by default.
*/
@Deprecated(since = "6.18.0")
default String getString(@NotNull String path,
boolean format) {
return this.getString(path, format, StringUtils.FormatOption.WITH_PLACEHOLDERS);
}
/**
* Get a string from config.
*
* @param path The key to fetch the value from.
* @param option The format option.
* @return The found value, or an empty string if not found.
* @deprecated Use {@link Config#getFormattedString(String, StringUtils.FormatOption)} instead.
*/
@NotNull
@Deprecated
default String getString(@NotNull String path,
@NotNull final StringUtils.FormatOption option) {
return this.getString(path, true, option);
}
/**
* Get a string from config.
*
@@ -319,36 +317,6 @@ public interface Config extends Cloneable {
return getStringOrNull(path, false, StringUtils.FormatOption.WITH_PLACEHOLDERS);
}
/**
* Get a string from config.
*
* @param path The key to fetch the value from.
* @param format If the string should be formatted.
* @return The found value, or null if not found.
* @deprecated Since 6.18.0, {@link Config#getString(String)} is not formatted by default.
*/
@Nullable
@Deprecated(since = "6.18.0")
default String getStringOrNull(@NotNull String path,
boolean format) {
return this.getStringOrNull(path, format, StringUtils.FormatOption.WITH_PLACEHOLDERS);
}
/**
* Get a string from config.
*
* @param path The key to fetch the value from.
* @param option The format option.
* @return The found value, or null if not found.
* @deprecated Use {@link Config#getFormattedString(String, StringUtils.FormatOption)} instead.
*/
@Nullable
@Deprecated
default String getStringOrNull(@NotNull String path,
@NotNull StringUtils.FormatOption option) {
return this.getStringOrNull(path, true, option);
}
/**
* Get a string from config.
*
@@ -403,36 +371,6 @@ public interface Config extends Cloneable {
return getStrings(path, false, StringUtils.FormatOption.WITH_PLACEHOLDERS);
}
/**
* Get a list of strings from config.
*
* @param path The key to fetch the value from.
* @param format If the strings should be formatted.
* @return The found value, or a blank {@link java.util.ArrayList} if not found.
* @deprecated Since 6.18.0, {@link Config#getString(String)} is not formatted by default.
*/
@NotNull
@Deprecated(since = "6.18.0")
default List<String> getStrings(@NotNull String path,
boolean format) {
return this.getStrings(path, format, StringUtils.FormatOption.WITH_PLACEHOLDERS);
}
/**
* Get a list of strings from config.
*
* @param path The key to fetch the value from.
* @param option The format option.
* @return The found value, or a blank {@link java.util.ArrayList} if not found.
* @deprecated Use {@link Config#getFormattedStrings(String, StringUtils.FormatOption)} instead.
*/
@NotNull
@Deprecated
default List<String> getStrings(@NotNull String path,
@NotNull StringUtils.FormatOption option) {
return getStrings(path, false, option);
}
/**
* Get a list of strings from config.
*
@@ -491,36 +429,6 @@ public interface Config extends Cloneable {
return getStringsOrNull(path, false, StringUtils.FormatOption.WITH_PLACEHOLDERS);
}
/**
* Get a list of strings from config.
*
* @param path The key to fetch the value from.
* @param format If the strings should be formatted.
* @return The found value, or null if not found.
* @deprecated Since 6.18.0, {@link Config#getString(String)} is not formatted by default.
*/
@Nullable
@Deprecated(since = "6.18.0")
default List<String> getStringsOrNull(@NotNull String path,
boolean format) {
return getStringsOrNull(path, format, StringUtils.FormatOption.WITH_PLACEHOLDERS);
}
/**
* Get a list of strings from config.
*
* @param path The key to fetch the value from.
* @param option The format option.
* @return The found value, or null if not found.
* @deprecated Use {@link Config#getFormattedStringsOrNull(String, StringUtils.FormatOption)} instead.
*/
@Nullable
@Deprecated
default List<String> getStringsOrNull(@NotNull String path,
@NotNull StringUtils.FormatOption option) {
return getStringsOrNull(path, false, option);
}
/**
* Get a list of strings from config.
*
@@ -563,7 +471,7 @@ public interface Config extends Cloneable {
*/
default double getDoubleFromExpression(@NotNull String path,
@Nullable Player player) {
return NumberUtils.evaluateExpression(this.getString(path), player);
return NumberUtils.evaluateExpression(this.getString(path), player, this.getInjectedPlaceholders());
}
/**
@@ -629,4 +537,48 @@ public interface Config extends Cloneable {
* @return The clone.
*/
Config clone();
@Override
default void injectPlaceholders(@NotNull Iterable<StaticPlaceholder> placeholders) {
// Do nothing.
}
@Override
default List<StaticPlaceholder> getInjectedPlaceholders() {
return Collections.emptyList();
}
@Override
default void clearInjectedPlaceholders() {
// Do nothing.
}
/**
* Convert the config to a map of values.
*
* @return The values.
*/
default Map<String, Object> toMap() {
return new HashMap<>();
}
/**
* Convert the config to a map of values.
*
* @return The values.
*/
default ConfigurationSection toBukkit() {
YamlConfiguration empty = new YamlConfiguration();
empty.createSection("temp", this.toMap());
return empty.getConfigurationSection("temp");
}
/**
* Create a new config builder.
*
* @return The builder.
*/
static BuildableConfig builder() {
return new BuildableConfig();
}
}

View File

@@ -1,63 +0,0 @@
package com.willfp.eco.core.config.interfaces;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* JSON config.
*
* @deprecated JSON and yml have full parity, use configs without a prefix instead,
* eg {@link com.willfp.eco.core.config.TransientConfig}, {@link com.willfp.eco.core.config.BaseConfig}.
* These configs will be removed eventually.
*/
@Deprecated(since = "6.17.0")
@SuppressWarnings("DeprecatedIsStillUsed")
public interface JSONConfig extends Config {
/**
* Get a list of subsections from config.
*
* @param path The key to fetch the value from.
* @return The found value, or a blank {@link java.util.ArrayList} if not found.
*/
@NotNull
default List<JSONConfig> getSubsections(@NotNull String path) {
return Objects.requireNonNullElse(getSubsectionsOrNull(path), new ArrayList<>());
}
/**
* Get a list of subsections from config.
*
* @param path The key to fetch the value from.
* @return The found value, or null if not found.
*/
@Nullable
List<JSONConfig> getSubsectionsOrNull(@NotNull String path);
/**
* Get subsection from config.
*
* @param path The key to check.
* @return The subsection. Throws NPE if not found.
*/
@Override
@NotNull
JSONConfig getSubsection(@NotNull String path);
/**
* Get subsection from config.
*
* @param path The key to check.
* @return The subsection, or null if not found.
*/
@Override
@Nullable
JSONConfig getSubsectionOrNull(@NotNull String path);
@Override
JSONConfig clone();
}

View File

@@ -1,7 +1,7 @@
package com.willfp.eco.core.config.interfaces;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
@@ -47,14 +47,21 @@ public interface LoadableConfig extends Config {
/**
* Get bukkit {@link YamlConfiguration}.
* <p>
* This method is not recommended unless absolutely required as it
* only returns true if the type of config is {@link com.willfp.eco.core.config.ConfigType#YAML},
* and if the handle is an {@link YamlConfiguration} specifically. This depends on the internals
* and the implementation, and so may cause problems - it exists mostly for parity with
* {@link JavaPlugin#getConfig()}.
* This used to represent the underlying config, but since 6.30.0 configs use
* their own implementations internally, without relying on bukkit.
*
* @return The config, or null if config is not yaml-based.
* @deprecated Use toBukkit() instead.
*/
@Nullable
YamlConfiguration getBukkitHandle();
@Deprecated(since = "6.30.0", forRemoval = true)
default YamlConfiguration getBukkitHandle() {
return this.toBukkit();
}
/**
* Convert the config to a bukkit {@link YamlConfiguration}.
*/
@NotNull
YamlConfiguration toBukkit();
}

View File

@@ -1,21 +0,0 @@
package com.willfp.eco.core.config.interfaces;
import org.bukkit.configuration.file.YamlConfiguration;
/**
* Interface for configs that wrap an {@link YamlConfiguration}.
*
* @deprecated JSON and yml have full parity, use configs without a prefix instead,
* eg {@link com.willfp.eco.core.config.TransientConfig}, {@link com.willfp.eco.core.config.BaseConfig}.
* These configs will be removed eventually.
*/
@Deprecated(since = "6.17.0")
@SuppressWarnings("DeprecatedIsStillUsed")
public interface WrappedYamlConfiguration {
/**
* Get the ConfigurationSection handle.
*
* @return The handle.
*/
YamlConfiguration getBukkitHandle();
}

View File

@@ -1,90 +0,0 @@
package com.willfp.eco.core.config.json;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.PluginLike;
import com.willfp.eco.core.config.ConfigType;
import com.willfp.eco.core.config.interfaces.JSONConfig;
import com.willfp.eco.core.config.json.wrapper.LoadableJSONConfigWrapper;
import org.jetbrains.annotations.NotNull;
/**
* Config implementation for configs present in the plugin's base directory (eg config.json).
* <p>
* Automatically updates.
*
* @deprecated JSON and yml have full parity, use configs without a prefix instead,
* eg {@link com.willfp.eco.core.config.TransientConfig}, {@link com.willfp.eco.core.config.BaseConfig}.
* These configs will be removed eventually.
*/
@Deprecated(since = "6.17.0")
public abstract class JSONBaseConfig extends LoadableJSONConfigWrapper {
/**
* @param configName The name of the config
* @param removeUnused Whether keys not present in the default config should be removed on update.
* @param plugin The plugin.
* @param updateBlacklist Substring of keys to not add/remove keys for.
*/
protected JSONBaseConfig(@NotNull final String configName,
final boolean removeUnused,
@NotNull final PluginLike plugin,
@NotNull final String... updateBlacklist) {
super(
(JSONConfig)
Eco.getHandler().getConfigFactory().createUpdatableConfig(
configName,
plugin,
"",
plugin.getClass(),
removeUnused,
ConfigType.JSON,
updateBlacklist
)
);
}
/**
* @param configName The name of the config
* @param removeUnused Whether keys not present in the default config should be removed on update.
* @param plugin The plugin.
*/
protected JSONBaseConfig(@NotNull final String configName,
final boolean removeUnused,
@NotNull final PluginLike plugin) {
super(
(JSONConfig)
Eco.getHandler().getConfigFactory().createUpdatableConfig(
configName,
plugin,
"",
plugin.getClass(),
removeUnused,
ConfigType.JSON
)
);
}
/**
* @param configName The name of the config
* @param removeUnused Whether keys not present in the default config should be removed on update.
* @param plugin The plugin.
* @param updateBlacklist Substring of keys to not add/remove keys for.
*/
protected JSONBaseConfig(@NotNull final String configName,
final boolean removeUnused,
@NotNull final EcoPlugin plugin,
@NotNull final String... updateBlacklist) {
this(configName, removeUnused, (PluginLike) plugin, updateBlacklist);
}
/**
* @param configName The name of the config
* @param removeUnused Whether keys not present in the default config should be removed on update.
* @param plugin The plugin.
*/
protected JSONBaseConfig(@NotNull final String configName,
final boolean removeUnused,
@NotNull final EcoPlugin plugin) {
this(configName, removeUnused, (PluginLike) plugin);
}
}

View File

@@ -1,70 +0,0 @@
package com.willfp.eco.core.config.json;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.PluginLike;
import com.willfp.eco.core.config.ConfigType;
import com.willfp.eco.core.config.interfaces.JSONConfig;
import com.willfp.eco.core.config.json.wrapper.LoadableJSONConfigWrapper;
import org.jetbrains.annotations.NotNull;
/**
* Config implementation for configs present in one of two places:
* <ul>
* <li>Plugin base directory (eg config.yml, lang.yml)</li>
* <li>Other extension's configs</li>
* </ul>
* <p>
* Automatically updates.
*
* @deprecated JSON and yml have full parity, use configs without a prefix instead,
* eg {@link com.willfp.eco.core.config.TransientConfig}, {@link com.willfp.eco.core.config.BaseConfig}.
* These configs will be removed eventually.
*/
@Deprecated(since = "6.17.0")
public abstract class JSONExtendableConfig extends LoadableJSONConfigWrapper {
/**
* @param configName The name of the config
* @param removeUnused Whether keys not present in the default config should be removed on update.
* @param plugin The plugin.
* @param updateBlacklist Substring of keys to not add/remove keys for.
* @param subDirectoryPath The subdirectory path.
* @param source The class that owns the resource.
*/
protected JSONExtendableConfig(@NotNull final String configName,
final boolean removeUnused,
@NotNull final PluginLike plugin,
@NotNull final Class<?> source,
@NotNull final String subDirectoryPath,
@NotNull final String... updateBlacklist) {
super(
(JSONConfig)
Eco.getHandler().getConfigFactory().createUpdatableConfig(
configName,
plugin,
subDirectoryPath,
source,
removeUnused,
ConfigType.JSON,
updateBlacklist
)
);
}
/**
* @param configName The name of the config
* @param removeUnused Whether keys not present in the default config should be removed on update.
* @param plugin The plugin.
* @param updateBlacklist Substring of keys to not add/remove keys for.
* @param subDirectoryPath The subdirectory path.
* @param source The class that owns the resource.
*/
protected JSONExtendableConfig(@NotNull final String configName,
final boolean removeUnused,
@NotNull final EcoPlugin plugin,
@NotNull final Class<?> source,
@NotNull final String subDirectoryPath,
@NotNull final String... updateBlacklist) {
this(configName, removeUnused, (PluginLike) plugin, source, subDirectoryPath, updateBlacklist);
}
}

View File

@@ -1,45 +0,0 @@
package com.willfp.eco.core.config.json;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.PluginLike;
import com.willfp.eco.core.config.ConfigType;
import com.willfp.eco.core.config.interfaces.JSONConfig;
import com.willfp.eco.core.config.json.wrapper.LoadableJSONConfigWrapper;
import org.jetbrains.annotations.NotNull;
/**
* Non-updatable JSON config that exists within a plugin jar.
*
* @deprecated JSON and yml have full parity, use configs without a prefix instead,
* eg {@link com.willfp.eco.core.config.TransientConfig}, {@link com.willfp.eco.core.config.BaseConfig}.
* These configs will be removed eventually.
*/
@Deprecated(since = "6.17.0")
public abstract class JSONStaticBaseConfig extends LoadableJSONConfigWrapper {
/**
* Config implementation for configs present in the plugin's base directory (eg config.json, lang.json).
* <p>
* Does not automatically update.
*
* @param configName The name of the config
* @param plugin The plugin.
*/
protected JSONStaticBaseConfig(@NotNull final String configName,
@NotNull final PluginLike plugin) {
super((JSONConfig) Eco.getHandler().getConfigFactory().createLoadableConfig(configName, plugin, "", plugin.getClass(), ConfigType.JSON));
}
/**
* Config implementation for configs present in the plugin's base directory (eg config.json, lang.json).
* <p>
* Does not automatically update.
*
* @param configName The name of the config
* @param plugin The plugin.
*/
protected JSONStaticBaseConfig(@NotNull final String configName,
@NotNull final EcoPlugin plugin) {
this(configName, (PluginLike) plugin);
}
}

View File

@@ -1,37 +0,0 @@
package com.willfp.eco.core.config.json;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.config.interfaces.JSONConfig;
import com.willfp.eco.core.config.json.wrapper.JSONConfigWrapper;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
/**
* Raw JSON config with a map of values at its core.
*
* @deprecated JSON and yml have full parity, use configs without a prefix instead,
* eg {@link com.willfp.eco.core.config.TransientConfig}, {@link com.willfp.eco.core.config.BaseConfig}.
* These configs will be removed eventually.
*/
@Deprecated(since = "6.17.0")
public class JSONTransientConfig extends JSONConfigWrapper {
/**
* Config implementation for passing maps.
* <p>
* Does not automatically update.
*
* @param values The map of values.
*/
public JSONTransientConfig(@NotNull final Map<String, Object> values) {
super((JSONConfig) Eco.getHandler().getConfigFactory().createConfig(values));
}
/**
* Empty JSON config.
*/
public JSONTransientConfig() {
super((JSONConfig) Eco.getHandler().getConfigFactory().createConfig(new HashMap<>()));
}
}

View File

@@ -1,53 +0,0 @@
package com.willfp.eco.core.config.json.wrapper;
import com.willfp.eco.core.config.interfaces.JSONConfig;
import com.willfp.eco.core.config.wrapper.ConfigWrapper;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* Wrapper to handle the backend JSON config implementations.
*
* @deprecated JSON and yml have full parity, use configs without a prefix instead,
* eg {@link com.willfp.eco.core.config.TransientConfig}, {@link com.willfp.eco.core.config.BaseConfig}.
* These configs will be removed eventually.
*/
@Deprecated(since = "6.17.0")
public abstract class JSONConfigWrapper extends ConfigWrapper<JSONConfig> implements JSONConfig {
/**
* Create a config wrapper.
*
* @param handle The handle.
*/
protected JSONConfigWrapper(@NotNull final JSONConfig handle) {
super(handle);
}
@Override
public @NotNull List<JSONConfig> getSubsections(@NotNull final String path) {
return this.getHandle().getSubsections(path);
}
@Override
public @Nullable List<JSONConfig> getSubsectionsOrNull(@NotNull final String path) {
return this.getHandle().getSubsectionsOrNull(path);
}
@Override
public @NotNull JSONConfig getSubsection(@NotNull final String path) {
return this.getHandle().getSubsection(path);
}
@Override
public @Nullable JSONConfig getSubsectionOrNull(@NotNull final String path) {
return this.getHandle().getSubsectionOrNull(path);
}
@Override
public JSONConfig clone() {
return this.getHandle().clone();
}
}

View File

@@ -1,63 +0,0 @@
package com.willfp.eco.core.config.json.wrapper;
import com.willfp.eco.core.config.interfaces.JSONConfig;
import com.willfp.eco.core.config.interfaces.LoadableConfig;
import org.apache.commons.lang.Validate;
import org.bukkit.configuration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
/**
* Wrapper to handle the backend loadable JSON config implementations.
*
* @deprecated JSON and yml have full parity, use configs without a prefix instead,
* eg {@link com.willfp.eco.core.config.TransientConfig}, {@link com.willfp.eco.core.config.BaseConfig}.
* These configs will be removed eventually.
*/
@Deprecated(since = "6.17.0")
public abstract class LoadableJSONConfigWrapper extends JSONConfigWrapper implements LoadableConfig {
/**
* Create a config wrapper.
*
* @param handle The handle.
*/
protected LoadableJSONConfigWrapper(@NotNull final JSONConfig handle) {
super(handle);
Validate.isTrue(handle instanceof LoadableConfig, "Wrapped config must be loadable!");
}
@Override
public void createFile() {
((LoadableConfig) this.getHandle()).createFile();
}
@Override
public String getResourcePath() {
return ((LoadableConfig) this.getHandle()).getResourcePath();
}
@Override
public void save() throws IOException {
((LoadableConfig) this.getHandle()).save();
}
@Override
public File getConfigFile() {
return ((LoadableConfig) this.getHandle()).getConfigFile();
}
@Override
public String getName() {
return ((LoadableConfig) this.getHandle()).getName();
}
@Override
public @Nullable YamlConfiguration getBukkitHandle() {
return null;
}
}

View File

@@ -1,5 +1,6 @@
package com.willfp.eco.core.config.updating;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -29,7 +30,8 @@ import java.lang.annotation.Target;
* }</pre>
* <p>
* If using kotlin, you have to annotate the method with {@code @JvmStatic}
* in order to prevent null pointer exceptions.
* in order to prevent null pointer exceptions - this also means that you cannot
* have config updater methods in companion objects.
* <p>
* Config update methods in all classes in a plugin jar will be called
* on reload.
@@ -39,5 +41,6 @@ import java.lang.annotation.Target;
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface ConfigUpdater {
}

View File

@@ -5,7 +5,7 @@ import com.willfp.eco.core.PluginLike;
import com.willfp.eco.core.config.ConfigType;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.config.interfaces.LoadableConfig;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.configuration.ConfigurationSection;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
@@ -20,13 +20,14 @@ public interface ConfigFactory {
/**
* Updatable config.
*
* @param configName The name of the config
* @param plugin The plugin.
* @param subDirectoryPath The subdirectory path.
* @param source The class that owns the resource.
* @param removeUnused Whether keys not present in the default config should be removed on update.
* @param type The config type.
* @param updateBlacklist Substring of keys to not add/remove keys for.
* @param configName The name of the config
* @param plugin The plugin.
* @param subDirectoryPath The subdirectory path.
* @param source The class that owns the resource.
* @param removeUnused Whether keys not present in the default config should be removed on update.
* @param type The config type.
* @param updateBlacklist Substring of keys to not add/remove keys for.
* @param requiresChangesToSave If the config must be changed in order to save the config.
* @return The config implementation.
*/
LoadableConfig createUpdatableConfig(@NotNull String configName,
@@ -35,23 +36,26 @@ public interface ConfigFactory {
@NotNull Class<?> source,
boolean removeUnused,
@NotNull ConfigType type,
boolean requiresChangesToSave,
@NotNull String... updateBlacklist);
/**
* Loadable config.
*
* @param configName The name of the config
* @param plugin The plugin.
* @param subDirectoryPath The subdirectory path.
* @param source The class that owns the resource.
* @param type The config type.
* @param configName The name of the config
* @param plugin The plugin.
* @param subDirectoryPath The subdirectory path.
* @param source The class that owns the resource.
* @param type The config type.
* @param requiresChangesToSave If the config must be changed in order to save the config.
* @return The config implementation.
*/
LoadableConfig createLoadableConfig(@NotNull String configName,
@NotNull PluginLike plugin,
@NotNull String subDirectoryPath,
@NotNull Class<?> source,
@NotNull ConfigType type);
@NotNull ConfigType type,
boolean requiresChangesToSave);
/**
* Create config.
@@ -59,15 +63,17 @@ public interface ConfigFactory {
* @param config The handle.
* @return The config implementation.
*/
Config createConfig(@NotNull YamlConfiguration config);
Config createConfig(@NotNull ConfigurationSection config);
/**
* Create config.
*
* @param values The values.
* @param type The config type.
* @return The config implementation.
*/
Config createConfig(@NotNull Map<String, Object> values);
Config createConfig(@NotNull Map<String, Object> values,
@NotNull ConfigType type);
/**
* Create config.

View File

@@ -2,11 +2,14 @@ package com.willfp.eco.core.config.wrapper;
import com.willfp.eco.core.config.ConfigType;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.placeholder.StaticPlaceholder;
import com.willfp.eco.util.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Configs from eco have an internal implementation,
@@ -16,7 +19,7 @@ import java.util.List;
*
* @param <T> The type of the handle.
*/
@SuppressWarnings("MethodDoesntCallSuperMethod")
@SuppressWarnings({"MethodDoesntCallSuperMethod", "removal"})
public abstract class ConfigWrapper<T extends Config> implements Config {
/**
* Configs from eco have an internal implementation,
@@ -40,6 +43,7 @@ public abstract class ConfigWrapper<T extends Config> implements Config {
}
@Override
@Deprecated(since = "6.31.1", forRemoval = true)
public void clearCache() {
handle.clearCache();
}
@@ -59,6 +63,12 @@ public abstract class ConfigWrapper<T extends Config> implements Config {
return handle.getKeys(deep);
}
@Override
public @NotNull List<String> recurseKeys(@NotNull final Set<String> found,
@NotNull final String root) {
return handle.recurseKeys(found, root);
}
@Override
public @Nullable Object get(@NotNull final String path) {
return handle.get(path);
@@ -134,6 +144,31 @@ public abstract class ConfigWrapper<T extends Config> implements Config {
return handle.getType();
}
@Override
public void injectPlaceholders(@NotNull final StaticPlaceholder... placeholders) {
handle.injectPlaceholders(placeholders);
}
@Override
public void injectPlaceholders(@NotNull final Iterable<StaticPlaceholder> placeholders) {
handle.injectPlaceholders(placeholders);
}
@Override
public List<StaticPlaceholder> getInjectedPlaceholders() {
return handle.getInjectedPlaceholders();
}
@Override
public void clearInjectedPlaceholders() {
handle.clearInjectedPlaceholders();
}
@Override
public Map<String, Object> toMap() {
return this.handle.toMap();
}
/**
* Get the handle.
*

View File

@@ -3,7 +3,6 @@ package com.willfp.eco.core.config.wrapper;
import com.willfp.eco.core.config.interfaces.LoadableConfig;
import org.bukkit.configuration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
@@ -47,7 +46,7 @@ public abstract class LoadableConfigWrapper extends ConfigWrapper<LoadableConfig
}
@Override
public @Nullable YamlConfiguration getBukkitHandle() {
return this.getHandle().getBukkitHandle();
public @NotNull YamlConfiguration toBukkit() {
return this.getHandle().toBukkit();
}
}

View File

@@ -1,87 +0,0 @@
package com.willfp.eco.core.config.yaml;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.PluginLike;
import com.willfp.eco.core.config.ConfigType;
import com.willfp.eco.core.config.yaml.wrapper.LoadableYamlConfigWrapper;
import org.jetbrains.annotations.NotNull;
/**
* Config implementation for configs present in the plugin's base directory (eg config.yml, lang.yml).
* <p>
* Automatically updates.
*
* @deprecated JSON and yml have full parity, use configs without a prefix instead,
* eg {@link com.willfp.eco.core.config.TransientConfig}, {@link com.willfp.eco.core.config.BaseConfig}.
* These configs will be removed eventually.
*/
@Deprecated(since = "6.17.0")
public abstract class YamlBaseConfig extends LoadableYamlConfigWrapper {
/**
* @param configName The name of the config
* @param removeUnused Whether keys not present in the default config should be removed on update.
* @param plugin The plugin.
* @param updateBlacklist Substring of keys to not add/remove keys for.
*/
protected YamlBaseConfig(@NotNull final String configName,
final boolean removeUnused,
@NotNull final PluginLike plugin,
@NotNull final String... updateBlacklist) {
super(
Eco.getHandler().getConfigFactory().createUpdatableConfig(
configName,
plugin,
"",
plugin.getClass(),
removeUnused,
ConfigType.YAML,
updateBlacklist
)
);
}
/**
* @param configName The name of the config
* @param removeUnused Whether keys not present in the default config should be removed on update.
* @param plugin The plugin.
*/
protected YamlBaseConfig(@NotNull final String configName,
final boolean removeUnused,
@NotNull final PluginLike plugin) {
super(
Eco.getHandler().getConfigFactory().createUpdatableConfig(
configName,
plugin,
"",
plugin.getClass(),
removeUnused,
ConfigType.YAML
)
);
}
/**
* @param configName The name of the config
* @param removeUnused Whether keys not present in the default config should be removed on update.
* @param plugin The plugin.
* @param updateBlacklist Substring of keys to not add/remove keys for.
*/
protected YamlBaseConfig(@NotNull final String configName,
final boolean removeUnused,
@NotNull final EcoPlugin plugin,
@NotNull final String... updateBlacklist) {
this(configName, removeUnused, (PluginLike) plugin, updateBlacklist);
}
/**
* @param configName The name of the config
* @param removeUnused Whether keys not present in the default config should be removed on update.
* @param plugin The plugin.
*/
protected YamlBaseConfig(@NotNull final String configName,
final boolean removeUnused,
@NotNull final EcoPlugin plugin) {
this(configName, removeUnused, (PluginLike) plugin);
}
}

View File

@@ -1,68 +0,0 @@
package com.willfp.eco.core.config.yaml;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.PluginLike;
import com.willfp.eco.core.config.ConfigType;
import com.willfp.eco.core.config.yaml.wrapper.LoadableYamlConfigWrapper;
import org.jetbrains.annotations.NotNull;
/**
* Config implementation for configs present in one of two places:
* <ul>
* <li>Plugin base directory (eg config.yml, lang.yml)</li>
* <li>Other extension's configs</li>
* </ul>
* <p>
* Automatically updates.
*
* @deprecated JSON and yml have full parity, use configs without a prefix instead,
* eg {@link com.willfp.eco.core.config.TransientConfig}, {@link com.willfp.eco.core.config.BaseConfig}.
* These configs will be removed eventually.
*/
@Deprecated(since = "6.17.0")
public abstract class YamlExtendableConfig extends LoadableYamlConfigWrapper {
/**
* @param configName The name of the config
* @param removeUnused Whether keys not present in the default config should be removed on update.
* @param plugin The plugin.
* @param updateBlacklist Substring of keys to not add/remove keys for.
* @param subDirectoryPath The subdirectory path.
* @param source The class that owns the resource.
*/
protected YamlExtendableConfig(@NotNull final String configName,
final boolean removeUnused,
@NotNull final PluginLike plugin,
@NotNull final Class<?> source,
@NotNull final String subDirectoryPath,
@NotNull final String... updateBlacklist) {
super(
Eco.getHandler().getConfigFactory().createUpdatableConfig(
configName,
plugin,
subDirectoryPath,
source,
removeUnused,
ConfigType.YAML,
updateBlacklist
)
);
}
/**
* @param configName The name of the config
* @param removeUnused Whether keys not present in the default config should be removed on update.
* @param plugin The plugin.
* @param updateBlacklist Substring of keys to not add/remove keys for.
* @param subDirectoryPath The subdirectory path.
* @param source The class that owns the resource.
*/
protected YamlExtendableConfig(@NotNull final String configName,
final boolean removeUnused,
@NotNull final EcoPlugin plugin,
@NotNull final Class<?> source,
@NotNull final String subDirectoryPath,
@NotNull final String... updateBlacklist) {
this(configName, removeUnused, (PluginLike) plugin, source, subDirectoryPath, updateBlacklist);
}
}

View File

@@ -1,44 +0,0 @@
package com.willfp.eco.core.config.yaml;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.PluginLike;
import com.willfp.eco.core.config.ConfigType;
import com.willfp.eco.core.config.yaml.wrapper.LoadableYamlConfigWrapper;
import org.jetbrains.annotations.NotNull;
/**
* Non-updatable yaml config that exists within a plugin jar.
*
* @deprecated JSON and yml have full parity, use configs without a prefix instead,
* eg {@link com.willfp.eco.core.config.TransientConfig}, {@link com.willfp.eco.core.config.BaseConfig}.
* These configs will be removed eventually.
*/
@Deprecated(since = "6.17.0")
public abstract class YamlStaticBaseConfig extends LoadableYamlConfigWrapper {
/**
* Config implementation for configs present in the plugin's base directory (eg config.yml, lang.yml).
* <p>
* Does not automatically update.
*
* @param configName The name of the config
* @param plugin The plugin.
*/
protected YamlStaticBaseConfig(@NotNull final String configName,
@NotNull final PluginLike plugin) {
super(Eco.getHandler().getConfigFactory().createLoadableConfig(configName, plugin, "", plugin.getClass(), ConfigType.YAML));
}
/**
* Config implementation for configs present in the plugin's base directory (eg config.yml, lang.yml).
* <p>
* Does not automatically update.
*
* @param configName The name of the config
* @param plugin The plugin.
*/
protected YamlStaticBaseConfig(@NotNull final String configName,
@NotNull final EcoPlugin plugin) {
this(configName, (PluginLike) plugin);
}
}

View File

@@ -1,42 +0,0 @@
package com.willfp.eco.core.config.yaml;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.config.ConfigType;
import com.willfp.eco.core.config.yaml.wrapper.YamlConfigWrapper;
import org.bukkit.configuration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull;
import java.io.StringReader;
/**
* Config implementation for passing YamlConfigurations.
* <p>
* Does not automatically update.
*
* @deprecated JSON and yml have full parity, use configs without a prefix instead,
* eg {@link com.willfp.eco.core.config.TransientConfig}, {@link com.willfp.eco.core.config.BaseConfig}.
* These configs will be removed eventually.
*/
@Deprecated(since = "6.17.0")
public class YamlTransientConfig extends YamlConfigWrapper {
/**
* @param config The YamlConfiguration handle.
*/
public YamlTransientConfig(@NotNull final YamlConfiguration config) {
super(Eco.getHandler().getConfigFactory().createConfig(config));
}
/**
* @param contents The contents of the config.
*/
public YamlTransientConfig(@NotNull final String contents) {
super(Eco.getHandler().getConfigFactory().createConfig(contents, ConfigType.YAML));
}
/**
* Create a new empty transient config.
*/
public YamlTransientConfig() {
super(Eco.getHandler().getConfigFactory().createConfig(YamlConfiguration.loadConfiguration(new StringReader(""))));
}
}

View File

@@ -1,63 +0,0 @@
package com.willfp.eco.core.config.yaml.wrapper;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.config.interfaces.LoadableConfig;
import org.apache.commons.lang.Validate;
import org.bukkit.configuration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
/**
* Wrapper to handle the backend loadable yaml config implementations.
*
* @deprecated JSON and yml have full parity, use configs without a prefix instead,
* eg {@link com.willfp.eco.core.config.TransientConfig}, {@link com.willfp.eco.core.config.BaseConfig}.
* These configs will be removed eventually.
*/
@Deprecated(since = "6.17.0")
public abstract class LoadableYamlConfigWrapper extends YamlConfigWrapper implements LoadableConfig {
/**
* Create a config wrapper.
*
* @param handle The handle.
*/
protected LoadableYamlConfigWrapper(@NotNull final Config handle) {
super(handle);
Validate.isTrue(handle instanceof LoadableConfig, "Wrapped config must be loadable!");
}
@Override
public void createFile() {
((LoadableConfig) this.getHandle()).createFile();
}
@Override
public String getResourcePath() {
return ((LoadableConfig) this.getHandle()).getResourcePath();
}
@Override
public void save() throws IOException {
((LoadableConfig) this.getHandle()).save();
}
@Override
public File getConfigFile() {
return ((LoadableConfig) this.getHandle()).getConfigFile();
}
@Override
public String getName() {
return ((LoadableConfig) this.getHandle()).getName();
}
@Override
public @Nullable YamlConfiguration getBukkitHandle() {
return ((LoadableConfig) this.getHandle()).getBukkitHandle();
}
}

View File

@@ -1,31 +0,0 @@
package com.willfp.eco.core.config.yaml.wrapper;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.config.interfaces.WrappedYamlConfiguration;
import com.willfp.eco.core.config.wrapper.ConfigWrapper;
import org.bukkit.configuration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull;
/**
* Wrapper to handle the backend yaml config implementations.
*
* @deprecated JSON and yml have full parity, use configs without a prefix instead,
* eg {@link com.willfp.eco.core.config.TransientConfig}, {@link com.willfp.eco.core.config.BaseConfig}.
* These configs will be removed eventually.
*/
@Deprecated(since = "6.17.0")
public abstract class YamlConfigWrapper extends ConfigWrapper<Config> implements WrappedYamlConfiguration {
/**
* Create a config wrapper.
*
* @param handle The handle.
*/
protected YamlConfigWrapper(@NotNull final Config handle) {
super(handle);
}
@Override
public YamlConfiguration getBukkitHandle() {
return ((WrappedYamlConfiguration) this.getHandle()).getBukkitHandle();
}
}

View File

@@ -5,6 +5,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* All storable data key types.
@@ -79,6 +80,22 @@ public final class PersistentDataKeyType<T> {
this.name = name;
}
@Override
public boolean equals(@Nullable final Object that) {
if (this == that) {
return true;
}
if (!(that instanceof PersistentDataKeyType type)) {
return false;
}
return Objects.equals(this.name, type.name);
}
@Override
public int hashCode() {
return Objects.hash(this.name);
}
/**
* Get all registered {@link PersistentDataKeyType}s.
*

View File

@@ -40,9 +40,9 @@ public abstract class CustomGoal<T extends Mob> implements EntityGoal<T>, Target
/**
* Tick the goal.
* <p>
* Runs ever tick as long as {@link this#canUse()} returns true.
* Runs ever tick as long as canUse returns true.
* <p>
* Runs after {@link this#start()}.
* Runs after start().
*/
public void tick() {
// Override when needed.
@@ -51,7 +51,7 @@ public abstract class CustomGoal<T extends Mob> implements EntityGoal<T>, Target
/**
* Start the goal.
* <p>
* Runs once {@link this#canUse()} returns true.
* Runs once canUse() returns true.
*/
public void start() {
// Override when needed.
@@ -60,7 +60,7 @@ public abstract class CustomGoal<T extends Mob> implements EntityGoal<T>, Target
/**
* Stop the goal.
* <p>
* Runs once {@link this#canUse()} returns false.
* Runs once canUse() returns false.
*/
public void stop() {
// Override when needed.

View File

@@ -24,7 +24,7 @@ public interface FastItemStack {
* @return A map of all enchantments.
* @deprecated Poorly named method. Use getEnchants instead.
*/
@Deprecated(since = "6.24.0")
@Deprecated(since = "6.24.0", forRemoval = true)
default Map<Enchantment, Integer> getEnchantmentsOnItem(boolean checkStored) {
return getEnchants(checkStored);
}

View File

@@ -96,6 +96,13 @@ public interface Menu {
*/
Set<NamespacedKey> getKeys(@NotNull Player player);
/**
* Re-render the menu for a player.
*
* @param player The player.
*/
void refresh(@NotNull Player player);
/**
* Create a builder with a given amount of rows.
*

View File

@@ -34,6 +34,17 @@ public final class CustomItemsManager {
}
}
/**
* Register all the custom items for a specific plugin into eco.
*
* @see com.willfp.eco.core.items.Items
*/
public static void registerProviders() {
for (CustomItemsWrapper customItemsWrapper : REGISTERED) {
customItemsWrapper.registerProvider();
}
}
private CustomItemsManager() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}

View File

@@ -11,5 +11,14 @@ public interface CustomItemsWrapper extends Integration {
*
* @see com.willfp.eco.core.items.Items
*/
void registerAllItems();
default void registerAllItems() {
// Override when needed.
}
/**
* Register {@link com.willfp.eco.core.items.provider.ItemProvider}s.
*/
default void registerProvider() {
// Override when needed.
}
}

View File

@@ -1,6 +1,10 @@
package com.willfp.eco.core.integrations.placeholder;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.placeholder.Placeholder;
import com.willfp.eco.core.placeholder.PlayerPlaceholder;
import com.willfp.eco.core.placeholder.PlayerlessPlaceholder;
import org.apache.commons.lang.Validate;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
@@ -10,10 +14,13 @@ import java.util.Objects;
import java.util.function.Function;
/**
* A {@link PlaceholderEntry} is a placeholder in and of itself.
* A placeholder entry is a placeholder in and of itself.
* <p>
* It should be fairly straightforward.
*
* @deprecated Confusing functionality with inconsistent nullability and poor naming.
*/
@Deprecated(since = "6.28.0", forRemoval = true)
public class PlaceholderEntry {
/**
* The name of the placeholder, used in lookups.
@@ -140,7 +147,28 @@ public class PlaceholderEntry {
* Register the placeholder.
*/
public void register() {
PlaceholderManager.registerPlaceholder(this);
PlaceholderManager.registerPlaceholder(this.toModernPlaceholder());
}
/**
* Convert the placeholder to a modern placeholder.
*
* @return The placeholder.
*/
Placeholder toModernPlaceholder() {
if (this.requiresPlayer) {
return new PlayerPlaceholder(
Objects.requireNonNullElse(plugin, Eco.getHandler().getEcoPlugin()),
identifier,
function
);
} else {
return new PlayerlessPlaceholder(
Objects.requireNonNullElse(plugin, Eco.getHandler().getEcoPlugin()),
identifier,
() -> function.apply(null)
);
}
}
@Override

View File

@@ -4,11 +4,16 @@ import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.placeholder.Placeholder;
import com.willfp.eco.core.placeholder.PlayerPlaceholder;
import com.willfp.eco.core.placeholder.PlayerlessPlaceholder;
import com.willfp.eco.core.placeholder.StaticPlaceholder;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -19,11 +24,12 @@ import java.util.concurrent.TimeUnit;
/**
* Class to handle placeholder integrations.
*/
@SuppressWarnings("removal")
public final class PlaceholderManager {
/**
* All registered placeholders.
*/
private static final Map<EcoPlugin, Map<String, PlaceholderEntry>> REGISTERED_PLACEHOLDERS = new HashMap<>();
private static final Map<EcoPlugin, Map<String, Placeholder>> REGISTERED_PLACEHOLDERS = new HashMap<>();
/**
* All registered placeholder integrations.
@@ -35,7 +41,7 @@ public final class PlaceholderManager {
*/
private static final LoadingCache<EntryWithPlayer, String> PLACEHOLDER_CACHE = Caffeine.newBuilder()
.expireAfterWrite(50, TimeUnit.MILLISECONDS)
.build(key -> key.entry.getResult(key.player));
.build(key -> key.entry.getValue(key.player));
/**
* Register a new placeholder integration.
@@ -50,16 +56,31 @@ public final class PlaceholderManager {
/**
* Register a placeholder.
*
* @param expansion The {@link com.willfp.eco.core.integrations.placeholder.PlaceholderEntry} to register.
* @param placeholder The placeholder to register.
*/
public static void registerPlaceholder(@NotNull final PlaceholderEntry expansion) {
EcoPlugin plugin = expansion.getPlugin() == null ? Eco.getHandler().getEcoPlugin() : expansion.getPlugin();
Map<String, PlaceholderEntry> pluginPlaceholders = REGISTERED_PLACEHOLDERS
public static void registerPlaceholder(@NotNull final Placeholder placeholder) {
if (placeholder instanceof StaticPlaceholder) {
throw new IllegalArgumentException("Static placeholders cannot be registered!");
}
EcoPlugin plugin = placeholder.getPlugin() == null ? Eco.getHandler().getEcoPlugin() : placeholder.getPlugin();
Map<String, Placeholder> pluginPlaceholders = REGISTERED_PLACEHOLDERS
.getOrDefault(plugin, new HashMap<>());
pluginPlaceholders.put(expansion.getIdentifier(), expansion);
pluginPlaceholders.put(placeholder.getIdentifier(), placeholder);
REGISTERED_PLACEHOLDERS.put(plugin, pluginPlaceholders);
}
/**
* Register a placeholder.
*
* @param placeholder The placeholder to register.
* @deprecated Uses old placeholder system.
*/
@Deprecated(since = "6.28.0", forRemoval = true)
public static void registerPlaceholder(@NotNull final PlaceholderEntry placeholder) {
registerPlaceholder(placeholder.toModernPlaceholder());
}
/**
* Get the result of a placeholder with respect to a player.
*
@@ -82,29 +103,36 @@ public final class PlaceholderManager {
* @param plugin The plugin for the placeholder.
* @return The value of the placeholder.
*/
@NotNull
public static String getResult(@Nullable final Player player,
@NotNull final String identifier,
@Nullable final EcoPlugin plugin) {
EcoPlugin owner = plugin == null ? Eco.getHandler().getEcoPlugin() : plugin;
PlaceholderEntry entry = REGISTERED_PLACEHOLDERS.getOrDefault(owner, new HashMap<>()).get(identifier.toLowerCase());
Placeholder placeholder = REGISTERED_PLACEHOLDERS.getOrDefault(owner, new HashMap<>()).get(identifier.toLowerCase());
if (entry == null && plugin != null) {
PlaceholderEntry alternate = REGISTERED_PLACEHOLDERS.getOrDefault(Eco.getHandler().getEcoPlugin(), new HashMap<>())
if (placeholder == null && plugin != null) {
Placeholder alternate = REGISTERED_PLACEHOLDERS.getOrDefault(Eco.getHandler().getEcoPlugin(), new HashMap<>())
.get(identifier.toLowerCase());
if (alternate != null) {
entry = alternate;
placeholder = alternate;
}
}
if (entry == null) {
if (placeholder == null) {
return "";
}
if (player == null && entry.requiresPlayer()) {
if (placeholder instanceof PlayerPlaceholder playerPlaceholder) {
if (player == null) {
return "";
} else {
return PLACEHOLDER_CACHE.get(new EntryWithPlayer(playerPlaceholder, player));
}
} else if (placeholder instanceof PlayerlessPlaceholder playerlessPlaceholder) {
return playerlessPlaceholder.getValue();
} else {
return "";
}
return PLACEHOLDER_CACHE.get(new EntryWithPlayer(entry, player));
}
/**
@@ -116,10 +144,28 @@ public final class PlaceholderManager {
*/
public static String translatePlaceholders(@NotNull final String text,
@Nullable final Player player) {
return translatePlaceholders(text, player, Collections.emptyList());
}
/**
* Translate all placeholders with respect to a player.
*
* @param text The text that may contain placeholders to translate.
* @param player The player to translate the placeholders with respect to.
* @param statics Extra static placeholders.
* @return The text, translated.
*/
public static String translatePlaceholders(@NotNull final String text,
@Nullable final Player player,
@NotNull final List<StaticPlaceholder> statics) {
String processed = text;
for (PlaceholderIntegration integration : REGISTERED_INTEGRATIONS) {
processed = integration.translate(processed, player);
}
for (StaticPlaceholder placeholder : statics) {
// Do I know this is a bad way of doing this? Yes.
processed = processed.replace("%" + placeholder.getIdentifier() + "%", placeholder.getValue());
}
return processed;
}
@@ -138,8 +184,8 @@ public final class PlaceholderManager {
return found;
}
private static record EntryWithPlayer(@NotNull PlaceholderEntry entry,
@Nullable Player player) {
private record EntryWithPlayer(@NotNull PlayerPlaceholder entry,
@NotNull Player player) {
}

View File

@@ -8,10 +8,12 @@ import com.willfp.eco.core.recipe.parts.EmptyTestableItem;
import com.willfp.eco.core.recipe.parts.MaterialTestableItem;
import com.willfp.eco.core.recipe.parts.ModifiedTestableItem;
import com.willfp.eco.core.recipe.parts.TestableStack;
import com.willfp.eco.core.recipe.parts.UnrestrictedMaterialTestableItem;
import com.willfp.eco.util.NamespacedKeyUtils;
import com.willfp.eco.util.NumberUtils;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
@@ -114,6 +116,48 @@ public final class Items {
REGISTRY.remove(key);
}
/**
* Turn an ItemStack back into a lookup string.
*
* @param itemStack The ItemStack.
* @return The lookup string.
*/
@NotNull
public static String toLookupString(@Nullable final ItemStack itemStack) {
if (itemStack == null) {
return "";
}
StringBuilder builder = new StringBuilder();
CustomItem customItem = getCustomItem(itemStack);
if (customItem != null) {
builder.append(customItem.getKey());
} else {
builder.append(itemStack.getType().name().toLowerCase());
}
if (itemStack.getAmount() > 1) {
builder.append(" ")
.append(itemStack.getAmount());
}
ItemMeta meta = itemStack.getItemMeta();
if (meta != null) {
for (LookupArgParser parser : ARG_PARSERS) {
String parsed = parser.serializeBack(meta);
if (parsed != null) {
builder.append(" ")
.append(parsed);
}
}
}
return builder.toString();
}
/**
* This is the backbone of the entire eco item system.
* <p>
@@ -155,11 +199,16 @@ public final class Items {
String[] split = args[0].toLowerCase().split(":");
if (split.length == 1) {
Material material = Material.getMaterial(args[0].toUpperCase());
String itemType = args[0];
boolean isWildcard = itemType.startsWith("*");
if (isWildcard) {
itemType = itemType.substring(1);
}
Material material = Material.getMaterial(itemType.toUpperCase());
if (material == null || material == Material.AIR) {
return new EmptyTestableItem();
}
item = new MaterialTestableItem(material);
item = isWildcard ? new UnrestrictedMaterialTestableItem(material) : new MaterialTestableItem(material);
}
if (split.length == 2) {
@@ -183,11 +232,16 @@ public final class Items {
This has been superseded by id amount
*/
if (part == null) {
Material material = Material.getMaterial(split[0].toUpperCase());
String itemType = split[0];
boolean isWildcard = itemType.startsWith("*");
if (isWildcard) {
itemType = itemType.substring(1);
}
Material material = Material.getMaterial(itemType.toUpperCase());
if (material == null || material == Material.AIR) {
return new EmptyTestableItem();
}
item = new MaterialTestableItem(material);
item = isWildcard ? new UnrestrictedMaterialTestableItem(material) : new MaterialTestableItem(material);
stackAmount = Integer.parseInt(split[1]);
} else {
item = part;
@@ -298,7 +352,7 @@ public final class Items {
* @param itemStack The itemStack to check.
* @return If is recipe.
*/
public static boolean isCustomItem(@NotNull final ItemStack itemStack) {
public static boolean isCustomItem(@Nullable final ItemStack itemStack) {
return getCustomItem(itemStack) != null;
}
@@ -309,7 +363,11 @@ public final class Items {
* @return The custom item, or null if not exists.
*/
@Nullable
public static CustomItem getCustomItem(@NotNull final ItemStack itemStack) {
public static CustomItem getCustomItem(@Nullable final ItemStack itemStack) {
if (itemStack == null) {
return null;
}
return CACHE.get(HashedItem.of(itemStack)).map(Items::getOrWrap).orElse(null);
}
@@ -373,6 +431,64 @@ public final class Items {
return items;
}
/**
* Merge ItemStack onto another ItemStack.
*
* @param from The ItemStack to merge from.
* @param to The ItemStack to merge onto.
* @return The ItemStack, merged (same instance as to).
*/
@NotNull
public static ItemStack mergeFrom(@NotNull final ItemStack from,
@NotNull final ItemStack to) {
ItemMeta fromMeta = from.getItemMeta();
ItemMeta toMeta = to.getItemMeta();
if (fromMeta == null || toMeta == null) {
return to;
}
ItemMeta newMeta = mergeFrom(fromMeta, toMeta);
to.setItemMeta(newMeta);
to.setType(from.getType());
to.setAmount(from.getAmount());
return to;
}
/**
* Merge ItemMeta onto other ItemMeta.
*
* @param from The ItemMeta to merge from.
* @param to The ItemMeta to merge onto.
* @return The ItemMeta, merged (same instance as to).
*/
@NotNull
public static ItemMeta mergeFrom(@NotNull final ItemMeta from,
@NotNull final ItemMeta to) {
if (from.hasDisplayName()) {
to.setDisplayName(from.getDisplayName());
}
to.setLore(from.getLore());
for (Enchantment enchant : to.getEnchants().keySet()) {
to.removeEnchant(enchant);
}
for (Map.Entry<Enchantment, Integer> entry : from.getEnchants().entrySet()) {
to.addEnchant(entry.getKey(), entry.getValue(), true);
}
if (from.hasCustomModelData()) {
to.setCustomModelData(from.getCustomModelData());
} else {
to.setCustomModelData(null);
}
return to;
}
private Items() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}

View File

@@ -22,4 +22,14 @@ public interface LookupArgParser {
*/
@Nullable Predicate<ItemStack> parseArguments(@NotNull String[] args,
@NotNull ItemMeta meta);
/**
* Serialize the item back to a string.
*
* @param meta The ItemMeta.
* @return The string, or null if not required.
*/
default @Nullable String serializeBack(@NotNull final ItemMeta meta) {
return null;
}
}

View File

@@ -59,6 +59,10 @@ public abstract class AbstractItemStackBuilder<T extends ItemMeta, U extends Abs
* @param base The ItemStack to start with.
*/
protected AbstractItemStackBuilder(@NotNull final ItemStack base) {
if (base.getType() == Material.AIR) {
base.setType(Material.STONE); // Prevents NPEs.
}
this.base = base;
this.meta = (T) base.getItemMeta();
assert meta != null;

View File

@@ -16,7 +16,7 @@ public interface LookupHandler<T extends Testable<?>> {
* <p>
* You shouldn't override this method unless you're doing something
* technically interesting or weird. This is the entry point for all
* lookup parsers, {@link this#parse(String[])} is to specify implementation-specific
* lookup parsers, parse() is to specify implementation-specific
* parsing.
*
* @param key The key.
@@ -56,7 +56,7 @@ public interface LookupHandler<T extends Testable<?>> {
/**
* Get the failsafe object.
* <p>
* A failsafe object should never pass {@link this#validate(Testable)}, as this will
* A failsafe object should never pass validate(), as this will
* cause issues with segment parsers. See {@link com.willfp.eco.core.items.ItemsLookupHandler} and
* {@link com.willfp.eco.core.recipe.parts.EmptyTestableItem} for examples.
*

View File

@@ -0,0 +1,22 @@
package com.willfp.eco.core.placeholder;
import com.willfp.eco.core.EcoPlugin;
/**
* A placeholder represents a string that can hold a value.
*/
public sealed interface Placeholder permits PlayerPlaceholder, PlayerlessPlaceholder, StaticPlaceholder {
/**
* Get the plugin that holds the placeholder.
*
* @return The plugin.
*/
EcoPlugin getPlugin();
/**
* Get the identifier for the placeholder.
*
* @return The identifier.
*/
String getIdentifier();
}

View File

@@ -0,0 +1,38 @@
package com.willfp.eco.core.placeholder;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* Represents a class that can have placeholders injected into it.
*/
public interface PlaceholderInjectable {
/**
* Inject placeholder.
*
* @param placeholders The placeholders.
*/
default void injectPlaceholders(@NotNull StaticPlaceholder... placeholders) {
this.injectPlaceholders(List.of(placeholders));
}
/**
* Inject placeholder.
*
* @param placeholders The placeholders.
*/
void injectPlaceholders(@NotNull Iterable<StaticPlaceholder> placeholders);
/**
* Clear injected placeholders.
*/
void clearInjectedPlaceholders();
/**
* Get injected placeholders.
*
* @return Injected placeholders.
*/
List<StaticPlaceholder> getInjectedPlaceholders();
}

View File

@@ -0,0 +1,92 @@
package com.willfp.eco.core.placeholder;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
import java.util.function.Function;
/**
* A placeholder that requires a player.
*/
public final class PlayerPlaceholder implements Placeholder {
/**
* The name of the placeholder.
*/
private final String identifier;
/**
* The function to retrieve the output of the placeholder given a player.
*/
private final Function<Player, String> function;
/**
* The plugin for the placeholder.
*/
private final EcoPlugin plugin;
/**
* Create a new player placeholder.
*
* @param plugin The plugin.
* @param identifier The identifier.
* @param function The function to retrieve the value.
*/
public PlayerPlaceholder(@NotNull final EcoPlugin plugin,
@NotNull final String identifier,
@NotNull final Function<Player, String> function) {
this.plugin = plugin;
this.identifier = identifier;
this.function = function;
}
/**
* Get the value of the placeholder for a given player.
*
* @param player The player.
* @return The value.
*/
public String getValue(@NotNull final Player player) {
return function.apply(player);
}
/**
* Register the placeholder.
*
* @return The placeholder.
*/
public PlayerPlaceholder register() {
PlaceholderManager.registerPlaceholder(this);
return this;
}
@Override
public EcoPlugin getPlugin() {
return this.plugin;
}
@Override
public String getIdentifier() {
return this.identifier;
}
@Override
public boolean equals(@Nullable final Object o) {
if (this == o) {
return true;
}
if (!(o instanceof StaticPlaceholder that)) {
return false;
}
return Objects.equals(this.getIdentifier(), that.getIdentifier())
&& Objects.equals(this.getPlugin(), that.getPlugin());
}
@Override
public int hashCode() {
return Objects.hash(this.getIdentifier(), this.getPlugin());
}
}

View File

@@ -0,0 +1,90 @@
package com.willfp.eco.core.placeholder;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
import java.util.function.Supplier;
/**
* A placeholder that does not require a player.
*/
public final class PlayerlessPlaceholder implements Placeholder {
/**
* The name of the placeholder.
*/
private final String identifier;
/**
* The function to retrieve the output of the placeholder.
*/
private final Supplier<String> function;
/**
* The plugin for the placeholder.
*/
private final EcoPlugin plugin;
/**
* Create a new player placeholder.
*
* @param plugin The plugin.
* @param identifier The identifier.
* @param function The function to retrieve the value.
*/
public PlayerlessPlaceholder(@NotNull final EcoPlugin plugin,
@NotNull final String identifier,
@NotNull final Supplier<String> function) {
this.plugin = plugin;
this.identifier = identifier;
this.function = function;
}
/**
* Get the value of the placeholder.
*
* @return The value.
*/
public String getValue() {
return function.get();
}
/**
* Register the placeholder.
*
* @return The placeholder.
*/
public PlayerlessPlaceholder register() {
PlaceholderManager.registerPlaceholder(this);
return this;
}
@Override
public EcoPlugin getPlugin() {
return this.plugin;
}
@Override
public String getIdentifier() {
return this.identifier;
}
@Override
public boolean equals(@Nullable final Object o) {
if (this == o) {
return true;
}
if (!(o instanceof StaticPlaceholder that)) {
return false;
}
return Objects.equals(this.getIdentifier(), that.getIdentifier())
&& Objects.equals(this.getPlugin(), that.getPlugin());
}
@Override
public int hashCode() {
return Objects.hash(this.getIdentifier(), this.getPlugin());
}
}

View File

@@ -0,0 +1,71 @@
package com.willfp.eco.core.placeholder;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.EcoPlugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
import java.util.function.Supplier;
/**
* A placeholder that cannot be registered, and exists purely in injection.
*/
public final class StaticPlaceholder implements Placeholder {
/**
* The name of the placeholder.
*/
private final String identifier;
/**
* The function to retrieve the output of the placeholder.
*/
private final Supplier<String> function;
/**
* Create a new player placeholder.
*
* @param identifier The identifier.
* @param function The function to retrieve the value.
*/
public StaticPlaceholder(@NotNull final String identifier,
@NotNull final Supplier<String> function) {
this.identifier = identifier;
this.function = function;
}
/**
* Get the value of the placeholder.
*
* @return The value.
*/
public String getValue() {
return function.get();
}
@Override
public EcoPlugin getPlugin() {
return Eco.getHandler().getEcoPlugin();
}
@Override
public String getIdentifier() {
return this.identifier;
}
@Override
public boolean equals(@Nullable final Object o) {
if (this == o) {
return true;
}
if (!(o instanceof StaticPlaceholder that)) {
return false;
}
return Objects.equals(this.getIdentifier(), that.getIdentifier());
}
@Override
public int hashCode() {
return Objects.hash(this.getIdentifier());
}
}

View File

@@ -23,7 +23,7 @@ public class ProxyError extends Error {
* @param message The message to send.
* @deprecated Proxy Errors should include a cause.
*/
@Deprecated
@Deprecated(forRemoval = true)
public ProxyError(@NotNull final String message) {
super(message);
}

View File

@@ -8,6 +8,7 @@ import org.jetbrains.annotations.NotNull;
*
* @deprecated Poorly named, exception when it's actually an error, contains doubly nested errors.
*/
@SuppressWarnings("removal")
@Deprecated(since = "6.24.0", forRemoval = true)
public class UnsupportedVersionException extends ProxyError {
/**
@@ -16,7 +17,7 @@ public class UnsupportedVersionException extends ProxyError {
* @param message The message to send.
* @deprecated Use the default constructor.
*/
@Deprecated(since = "6.24.0")
@Deprecated(since = "6.24.0", forRemoval = true)
public UnsupportedVersionException(@NotNull final String message) {
super(message);
}

View File

@@ -0,0 +1,32 @@
package com.willfp.eco.core.recipe.parts;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Same as material testable items, but doesn't filter out custom items.
*/
public class UnrestrictedMaterialTestableItem extends MaterialTestableItem {
/**
* Create a new simple recipe part.
*
* @param material The material.
*/
public UnrestrictedMaterialTestableItem(@NotNull final Material material) {
super(material);
}
/**
* If the item matches the material.
*
* @param itemStack The item to test.
* @return If the item is of the specified material.
*/
@Override
public boolean matches(@Nullable final ItemStack itemStack) {
return itemStack != null && itemStack.getType() == this.getMaterial();
}
}

View File

@@ -3,7 +3,6 @@ package com.willfp.eco.core.recipe.recipes;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.PluginDependent;
import com.willfp.eco.core.Prerequisite;
import com.willfp.eco.core.items.TestableItem;
import com.willfp.eco.core.recipe.Recipes;
import com.willfp.eco.core.recipe.parts.EmptyTestableItem;
@@ -139,12 +138,6 @@ public final class ShapedCraftingRecipe extends PluginDependent<EcoPlugin> imple
displayedRecipe.setIngredient(character, new RecipeChoice.ExactChoice(displayedItems));
}
if (Prerequisite.HAS_1_18.isMet() && !Prerequisite.HAS_PAPER.isMet()) {
if (Bukkit.getServer().getRecipe(this.getKey()) != null) {
return;
}
}
Bukkit.getServer().addRecipe(shapedRecipe);
Bukkit.getServer().addRecipe(displayedRecipe);
}

View File

@@ -0,0 +1,343 @@
package com.willfp.eco.core.recipe.recipes;
import com.google.common.annotations.Beta;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.PluginDependent;
import com.willfp.eco.core.items.TestableItem;
import com.willfp.eco.core.recipe.Recipes;
import com.willfp.eco.core.recipe.parts.EmptyTestableItem;
import com.willfp.eco.core.recipe.parts.GroupedTestableItems;
import com.willfp.eco.core.recipe.parts.TestableStack;
import org.bukkit.Bukkit;
import org.bukkit.NamespacedKey;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.RecipeChoice;
import org.bukkit.inventory.ShapelessRecipe;
import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
/**
* Shapeless crafting recipe.
*/
@Beta
public final class ShapelessCraftingRecipe extends PluginDependent<EcoPlugin> implements CraftingRecipe {
/**
* Recipe parts.
*/
private final List<TestableItem> parts;
/**
* The key of the recipe.
*/
private final NamespacedKey key;
/**
* The key of the displayed recipe.
*/
private final NamespacedKey displayedKey;
/**
* The recipe's output.
*/
private final ItemStack output;
/**
* The permission.
*/
private final String permission;
private ShapelessCraftingRecipe(@NotNull final EcoPlugin plugin,
@NotNull final String key,
@NotNull final List<TestableItem> parts,
@NotNull final ItemStack output,
@Nullable final String permission) {
super(plugin);
this.parts = parts;
this.key = plugin.getNamespacedKeyFactory().create(key);
this.displayedKey = plugin.getNamespacedKeyFactory().create(key + "_displayed");
this.output = output;
this.permission = permission;
}
/**
* Make a new test.
*
* @return The test.
*/
@NotNull
public RecipeTest newTest() {
return new RecipeTest(this);
}
@Override
public boolean test(@NotNull final ItemStack[] matrix) {
RecipeTest test = newTest();
for (ItemStack stack : matrix) {
if (test.matchAndRemove(stack) == null) {
return false;
}
}
return true;
}
@Override
public void register() {
Recipes.register(this);
Bukkit.getServer().removeRecipe(this.getKey());
Bukkit.getServer().removeRecipe(this.getDisplayedKey());
ShapelessRecipe shapelessRecipe = new ShapelessRecipe(this.getKey(), this.getOutput());
for (TestableItem part : parts) {
shapelessRecipe.addIngredient(part.getItem().getType());
}
ShapelessRecipe displayedRecipe = new ShapelessRecipe(this.getDisplayedKey(), this.getOutput());
for (TestableItem part : parts) {
List<TestableItem> items = new ArrayList<>();
if (part instanceof GroupedTestableItems group) {
items.addAll(group.getChildren());
} else {
items.add(part);
}
List<ItemStack> displayedItems = new ArrayList<>();
for (TestableItem testableItem : items) {
if (testableItem instanceof TestableStack) {
ItemStack item = testableItem.getItem().clone();
ItemMeta meta = item.getItemMeta();
assert meta != null;
List<String> lore = meta.hasLore() ? meta.getLore() : new ArrayList<>();
assert lore != null;
lore.add("");
String add = Eco.getHandler().getEcoPlugin().getLangYml().getFormattedString("multiple-in-craft");
add = add.replace("%amount%", String.valueOf(item.getAmount()));
lore.add(add);
meta.setLore(lore);
item.setItemMeta(meta);
displayedItems.add(item);
} else {
displayedItems.add(testableItem.getItem());
}
}
displayedRecipe.addIngredient(new RecipeChoice.ExactChoice(displayedItems));
}
Bukkit.getServer().addRecipe(shapelessRecipe);
Bukkit.getServer().addRecipe(displayedRecipe);
}
/**
* Create a new recipe builder.
*
* @param plugin The plugin that owns the recipe.
* @param key The recipe key.
* @return A new builder.
*/
public static Builder builder(@NotNull final EcoPlugin plugin,
@NotNull final String key) {
return new Builder(plugin, key);
}
/**
* Get the parts.
*
* @return The parts.
*/
@NotNull
@Override
public List<TestableItem> getParts() {
return this.parts;
}
/**
* Get the key.
*
* @return The key.
*/
@NotNull
@Override
public NamespacedKey getKey() {
return this.key;
}
/**
* Get the displayed key.
*
* @return The displayed key.
*/
@NotNull
@Override
public NamespacedKey getDisplayedKey() {
return this.displayedKey;
}
/**
* Get the output.
*
* @return The output.
*/
@NotNull
@Override
public ItemStack getOutput() {
return this.output;
}
/**
* Get the permission.
*
* @return The permission.
*/
@Nullable
@Override
public String getPermission() {
return permission;
}
/**
* Builder for recipes.
*/
public static final class Builder {
/**
* The recipe parts.
*/
private final List<TestableItem> recipeParts = new ArrayList<>();
/**
* The output of the recipe.
*/
private ItemStack output = null;
/**
* The permission for the recipe.
*/
private String permission = null;
/**
* The key of the recipe.
*/
private final String key;
/**
* The plugin that created the recipe.
*/
private final EcoPlugin plugin;
/**
* Create a new recipe builder.
*
* @param plugin The plugin that owns the recipe.
* @param key The recipe key.
*/
private Builder(@NotNull final EcoPlugin plugin,
@NotNull final String key) {
this.key = key;
this.plugin = plugin;
}
/**
* Add a recipe part.
*
* @param part The part of the recipe.
* @return The builder.
*/
public Builder addRecipePart(@NotNull final TestableItem part) {
recipeParts.add(part);
return this;
}
/**
* Set the output of the recipe.
*
* @param output The output.
* @return The builder.
*/
public Builder setOutput(@NotNull final ItemStack output) {
this.output = output;
return this;
}
/**
* Set the permission required to craft the recipe.
*
* @param permission The permission.
* @return The builder.
*/
public Builder setPermission(@Nullable final String permission) {
this.permission = permission;
return this;
}
/**
* Check if recipe parts are all air.
*
* @return If recipe parts are all air.
*/
public boolean isAir() {
for (TestableItem recipePart : this.recipeParts) {
if (recipePart != null && !(recipePart instanceof EmptyTestableItem)) {
return false;
}
}
return true;
}
/**
* Build the recipe.
*
* @return The built recipe.
*/
public ShapelessCraftingRecipe build() {
return new ShapelessCraftingRecipe(plugin, key.toLowerCase(), recipeParts, output, permission);
}
}
/**
* Test for shapeless recipes.
*/
public static final class RecipeTest {
/**
* The remaining items left to be found.
*/
private final List<TestableItem> remaining;
private RecipeTest(@NotNull final ShapelessCraftingRecipe recipe) {
this.remaining = new ArrayList<>(recipe.getParts());
}
/**
* If the item is in the recipe, remove it from the remaining items to test and
* return the matching item.
*
* @param itemStack The item.
* @return The matching item, or null if no match was found.
*/
@Nullable
public TestableItem matchAndRemove(@NotNull final ItemStack itemStack) {
if (remaining.isEmpty() && !(new EmptyTestableItem().matches(itemStack))) {
return null;
}
Optional<TestableItem> match = remaining.stream()
.filter(item -> item.matches(itemStack))
.findFirst();
match.ifPresent(remaining::remove);
return match.orElse(null);
}
}
}

View File

@@ -28,7 +28,7 @@ public final class DurabilityUtils {
* @param slot The slot in the inventory of the item.
* @deprecated The slot is not required.
*/
@Deprecated(since = "6.24.0")
@Deprecated(since = "6.24.0", forRemoval = true)
public static void damageItem(@NotNull final Player player,
@NotNull final ItemStack item,
final int damage,

View File

@@ -1,5 +1,6 @@
package com.willfp.eco.util;
import com.willfp.eco.core.placeholder.StaticPlaceholder;
import org.apache.commons.lang.Validate;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.ApiStatus;
@@ -7,10 +8,10 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.text.DecimalFormat;
import java.util.Collections;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.BiFunction;
/**
* Utilities / API methods for numbers.
@@ -24,7 +25,7 @@ public final class NumberUtils {
/**
* Crunch handler.
*/
private static BiFunction<String, Player, Double> crunch = null;
private static CrunchHandler crunch = null;
/**
* Set of roman numerals to look up.
@@ -109,7 +110,7 @@ public final class NumberUtils {
* @return The new value.
* @deprecated Pointless method.
*/
@Deprecated(since = "6.19.0")
@Deprecated(since = "6.19.0", forRemoval = true)
public static double equalIfOver(final double toChange,
final double limit) {
return Math.min(toChange, limit);
@@ -251,7 +252,21 @@ public final class NumberUtils {
*/
public static double evaluateExpression(@NotNull final String expression,
@Nullable final Player player) {
return crunch.apply(expression, player);
return evaluateExpression(expression, player, Collections.emptyList());
}
/**
* Evaluate an expression with respect to a player (for placeholders).
*
* @param expression The expression.
* @param player The player.
* @param statics The static placeholders.
* @return The value of the expression, or zero if invalid.
*/
public static double evaluateExpression(@NotNull final String expression,
@Nullable final Player player,
@NotNull final Iterable<StaticPlaceholder> statics) {
return crunch.evaluate(expression, player, statics);
}
/**
@@ -260,11 +275,29 @@ public final class NumberUtils {
* @param handler The handler.
*/
@ApiStatus.Internal
public static void initCrunch(@NotNull final BiFunction<String, Player, Double> handler) {
public static void initCrunch(@NotNull final CrunchHandler handler) {
Validate.isTrue(crunch == null, "Already initialized!");
crunch = handler;
}
/**
* Bridge component for crunch.
*/
@ApiStatus.Internal
public interface CrunchHandler {
/**
* Evaluate an expression.
*
* @param expression The expression.
* @param player The player.
* @param statics The statics.
* @return The value of the expression, or zero if invalid.
*/
double evaluate(@NotNull String expression,
@Nullable Player player,
@NotNull Iterable<StaticPlaceholder> statics);
}
private NumberUtils() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}

View File

@@ -71,7 +71,7 @@ public final class TeamUtils {
* @deprecated Stupid method.
*/
@NotNull
@Deprecated(since = "6.24.0")
@Deprecated(since = "6.24.0", forRemoval = true)
public static Team getMaterialColorTeam(@NotNull final Material material) {
return fromChatColor(MATERIAL_COLORS.getOrDefault(material, ChatColor.WHITE));
}

View File

@@ -0,0 +1,24 @@
@file:JvmName("ItemsExtensions")
package com.willfp.eco.core.items
import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.ItemMeta
/**
* @see Items.toLookupString
*/
fun ItemStack?.toLookupString(): String =
Items.toLookupString(this)
/**
* @see Items.mergeFrom
*/
fun ItemStack.mergeFrom(other: ItemStack): ItemStack =
Items.mergeFrom(other, this)
/**
* @see Items.mergeFrom
*/
fun ItemMeta.mergeFrom(other: ItemMeta): ItemMeta =
Items.mergeFrom(other, this)

View File

@@ -2,10 +2,12 @@ group 'com.willfp'
version rootProject.version
dependencies {
// Libraries
implementation 'org.reflections:reflections:0.9.12'
implementation 'org.objenesis:objenesis:3.2'
compileOnly 'org.spigotmc:spigot-api:1.17.1-R0.1-SNAPSHOT'
compileOnly 'me.clip:placeholderapi:2.10.10'
compileOnly 'org.reflections:reflections:0.9.12'
compileOnly 'net.kyori:adventure-text-minimessage:4.10.0'
compileOnly 'net.kyori:adventure-platform-bukkit:4.1.0'
compileOnly 'org.objenesis:objenesis:3.2'
}

View File

@@ -0,0 +1,125 @@
@file:Suppress("UNCHECKED_CAST")
package com.willfp.eco.internal.config
import com.willfp.eco.core.config.ConfigType
import org.bukkit.configuration.file.YamlConstructor
import org.yaml.snakeyaml.DumperOptions
import org.yaml.snakeyaml.LoaderOptions
import org.yaml.snakeyaml.Yaml
import java.io.BufferedReader
import java.io.Reader
fun ConfigType.toMap(input: String?): Map<String, Any?> =
this.handler.toMap(input)
fun ConfigType.toString(map: Map<String, Any?>): String =
this.handler.toString(map)
fun Any?.constrainConfigTypes(type: ConfigType): Any? = when (this) {
is Map<*, *> -> EcoConfigSection(type, this.normalizeToConfig(type))
is Iterable<*> -> {
if (this.firstOrNull() == null) {
mutableListOf<Any>()
} else if (this.firstOrNull() is Map<*, *>) {
this as Iterable<Map<*, *>>
this.map { map -> EcoConfigSection(type, map.normalizeToConfig(type)) }
} else {
this.toMutableList()
}
}
else -> this
}
fun Map<*, *>.normalizeToConfig(type: ConfigType): Map<String, Any?> {
val building = mutableMapOf<String, Any?>()
for ((unprocessedKey, value) in this.entries) {
if (unprocessedKey == null || value == null) {
continue
}
val key = unprocessedKey.toString()
val constrained = value.constrainConfigTypes(type)
building[key] = constrained
}
return building
}
fun Reader.readToString(): String {
val input = this as? BufferedReader ?: BufferedReader(this)
val builder = StringBuilder()
var line: String?
input.use {
while (it.readLine().also { read -> line = read } != null) {
builder.append(line)
builder.append('\n')
}
}
return builder.toString()
}
private val ConfigType.handler: ConfigTypeHandler
get() = if (this == ConfigType.JSON) JSONConfigTypeHandler else YamlConfigTypeHandler
private abstract class ConfigTypeHandler(
val type: ConfigType
) {
fun toMap(input: String?): Map<String, Any?> {
if (input == null || input.isBlank()) {
return emptyMap()
}
return parseToMap(input).normalizeToConfig(type)
}
protected abstract fun parseToMap(input: String): Map<*, *>
abstract fun toString(map: Map<String, Any?>): String
}
private object YamlConfigTypeHandler : ConfigTypeHandler(ConfigType.YAML) {
private fun newYaml(): Yaml {
val yamlOptions = DumperOptions()
val loaderOptions = LoaderOptions()
val representer = EcoRepresenter()
loaderOptions.maxAliasesForCollections = Int.MAX_VALUE
loaderOptions.isAllowDuplicateKeys = false
yamlOptions.indent = 2
yamlOptions.defaultFlowStyle = DumperOptions.FlowStyle.BLOCK
representer.defaultFlowStyle = DumperOptions.FlowStyle.BLOCK
return Yaml(
YamlConstructor(),
representer,
yamlOptions,
loaderOptions,
)
}
override fun parseToMap(input: String): Map<*, *> {
return newYaml().load(input) ?: emptyMap<Any, Any>()
}
override fun toString(map: Map<String, Any?>): String {
return newYaml().dump(map)
}
}
private object JSONConfigTypeHandler : ConfigTypeHandler(ConfigType.JSON) {
override fun parseToMap(input: String): Map<*, *> {
return EcoGsonSerializer.gson.fromJson(input, Map::class.java)
}
override fun toString(map: Map<String, Any?>): String {
return EcoGsonSerializer.gson.toJson(map)
}
}

View File

@@ -0,0 +1,195 @@
package com.willfp.eco.internal.config
import com.willfp.eco.core.config.ConfigType
import com.willfp.eco.core.config.interfaces.Config
import com.willfp.eco.core.placeholder.StaticPlaceholder
import com.willfp.eco.util.StringUtils
import org.bukkit.configuration.file.YamlConfiguration
import java.util.concurrent.ConcurrentHashMap
@Suppress("UNCHECKED_CAST")
open class EcoConfig(
private val configType: ConfigType
) : Config {
private val values = ConcurrentHashMap<String, Any?>()
@Transient
var injections = mutableListOf<StaticPlaceholder>()
fun init(values: Map<String, Any?>) {
this.values.clear()
this.values.putAll(values.normalizeToConfig(this.type))
}
override fun toPlaintext(): String {
return configType.toString(this.values)
}
override fun has(path: String): Boolean {
return get(path) != null
}
override fun getKeys(deep: Boolean): List<String> {
return if (deep) {
recurseKeys(mutableSetOf(), "")
} else {
values.keys.toList()
}
}
override fun recurseKeys(current: MutableSet<String>, root: String): List<String> {
val list = mutableSetOf<String>()
for (key in getKeys(false)) {
list.add("$root$key")
val found = get(key)
if (found is Config) {
list.addAll(found.recurseKeys(current, "$root$key."))
}
}
return list.toList()
}
override fun get(path: String): Any? {
val nearestPath = path.split(".")[0]
if (path.contains(".")) {
val remainingPath = path.removePrefix("${nearestPath}.")
if (remainingPath.isEmpty()) {
return null
}
val first = get(nearestPath)
return if (first is Config) {
first.get(remainingPath)
} else {
null
}
}
return values[nearestPath]
}
override fun set(
path: String,
obj: Any?
) {
val nearestPath = path.split(".")[0]
if (path.contains(".")) {
val remainingPath = path.removePrefix("${nearestPath}.")
if (remainingPath.isEmpty()) {
return
}
val section = getSubsection(nearestPath) // Creates a section if null, therefore it can be set.
section.set(remainingPath, obj)
values[nearestPath] = section // Set the value
return
}
if (obj == null) {
values.remove(nearestPath)
} else {
values[nearestPath] = obj.constrainConfigTypes(type)
}
}
override fun getSubsection(path: String): Config {
return getSubsectionOrNull(path) ?: EcoConfigSection(type, injections = injections)
}
override fun getSubsectionOrNull(path: String): Config? {
return get(path) as? Config
}
override fun getSubsectionsOrNull(path: String): List<Config>? {
return (get(path) as? Iterable<Config>)?.toList()
}
override fun getType(): ConfigType {
return configType
}
override fun getIntOrNull(path: String): Int? {
return (get(path) as? Number)?.toInt()
}
override fun getIntsOrNull(path: String): List<Int>? {
return (get(path) as? Iterable<Number>)?.map { it.toInt() }
}
override fun getBoolOrNull(path: String): Boolean? {
return get(path) as? Boolean
}
override fun getBoolsOrNull(path: String): List<Boolean>? {
return (get(path) as? Iterable<Boolean>)?.toList()
}
override fun getStringOrNull(
path: String,
format: Boolean,
option: StringUtils.FormatOption
): String? {
val string = get(path)?.toString() ?: return null
return if (format) StringUtils.format(string, option) else string
}
override fun getStringsOrNull(
path: String,
format: Boolean,
option: StringUtils.FormatOption
): List<String>? {
val strings = (get(path) as? Iterable<*>)?.map { it.toString() } ?: return null
return if (format) StringUtils.formatList(strings, option) else strings
}
override fun getDoubleOrNull(path: String): Double? {
return (get(path) as? Number)?.toDouble()
}
override fun getDoublesOrNull(path: String): List<Double>? {
return (get(path) as? Iterable<Number>)?.map { it.toDouble() }
}
override fun injectPlaceholders(placeholders: Iterable<StaticPlaceholder>) {
injections.removeIf { placeholders.any { placeholder -> it.identifier == placeholder.identifier } }
injections.addAll(placeholders)
}
override fun getInjectedPlaceholders(): List<StaticPlaceholder> {
return injections.toList()
}
override fun clearInjectedPlaceholders() {
injections.clear()
}
override fun toMap(): MutableMap<String, Any?> {
return values.toMutableMap()
}
override fun toBukkit(): YamlConfiguration {
val temp = YamlConfiguration()
temp.createSection("temp", this.values.toMap())
val section = temp.getConfigurationSection("temp")!!
val bukkit = YamlConfiguration()
for (key in section.getKeys(true)) {
bukkit.set(key, section.get(key))
}
return bukkit
}
override fun clone(): Config {
return EcoConfigSection(type, this.values.toMutableMap(), injections)
}
override fun toString(): String {
return this.toPlaintext()
}
}

View File

@@ -5,61 +5,39 @@ import com.willfp.eco.core.config.ConfigType
import com.willfp.eco.core.config.interfaces.Config
import com.willfp.eco.core.config.interfaces.LoadableConfig
import com.willfp.eco.core.config.wrapper.ConfigFactory
import com.willfp.eco.internal.config.json.EcoJSONConfigSection
import com.willfp.eco.internal.config.json.EcoJSONConfigWrapper
import com.willfp.eco.internal.config.json.EcoLoadableJSONConfig
import com.willfp.eco.internal.config.json.EcoUpdatableJSONConfig
import com.willfp.eco.internal.config.yaml.EcoLoadableYamlConfig
import com.willfp.eco.internal.config.yaml.EcoUpdatableYamlConfig
import com.willfp.eco.internal.config.yaml.EcoYamlConfigSection
import org.bukkit.configuration.file.YamlConfiguration
import java.io.StringReader
import org.bukkit.configuration.ConfigurationSection
object EcoConfigFactory : ConfigFactory {
override fun createConfig(config: YamlConfiguration): Config {
return EcoYamlConfigSection(config)
}
override fun createConfig(values: MutableMap<String, Any>): Config {
return EcoJSONConfigSection(values)
}
override fun createConfig(contents: String, type: ConfigType): Config {
return if (type == ConfigType.JSON) {
@Suppress("UNCHECKED_CAST")
EcoJSONConfigSection(
EcoJSONConfigWrapper.gson.fromJson(
StringReader(contents), Map::class.java
) as MutableMap<String, Any>
)
} else {
EcoYamlConfigSection(YamlConfiguration.loadConfiguration(StringReader(contents)))
override fun createConfig(bukkit: ConfigurationSection): Config {
val config = createConfig(emptyMap(), ConfigType.YAML)
for (key in bukkit.getKeys(true)) {
config.set(key, bukkit.get(key))
}
return config
}
override fun createConfig(values: Map<String, Any>, type: ConfigType): Config =
EcoConfigSection(type, values)
override fun createConfig(contents: String, type: ConfigType): Config =
EcoConfigSection(type, type.toMap(contents))
override fun createLoadableConfig(
configName: String,
plugin: PluginLike,
subDirectoryPath: String,
source: Class<*>,
type: ConfigType
): LoadableConfig {
return if (type == ConfigType.JSON) {
EcoLoadableJSONConfig(
configName,
plugin,
subDirectoryPath,
source
)
} else {
EcoLoadableYamlConfig(
configName,
plugin,
subDirectoryPath,
source
)
}
}
type: ConfigType,
requiresChangesToSave: Boolean
): LoadableConfig = EcoLoadableConfig(
type,
configName,
plugin,
subDirectoryPath,
source,
requiresChangesToSave
)
override fun createUpdatableConfig(
configName: String,
@@ -68,26 +46,16 @@ object EcoConfigFactory : ConfigFactory {
source: Class<*>,
removeUnused: Boolean,
type: ConfigType,
requiresChangesToSave: Boolean,
vararg updateBlacklist: String
): LoadableConfig {
return if (type == ConfigType.JSON) {
EcoUpdatableJSONConfig(
configName,
plugin,
subDirectoryPath,
source,
removeUnused,
*updateBlacklist
)
} else {
EcoUpdatableYamlConfig(
configName,
plugin,
subDirectoryPath,
source,
removeUnused,
*updateBlacklist
)
}
}
}
): LoadableConfig = EcoUpdatableConfig(
type,
configName,
plugin,
subDirectoryPath,
source,
removeUnused,
requiresChangesToSave,
*updateBlacklist
)
}

View File

@@ -1,14 +1,9 @@
package com.willfp.eco.internal.config.updating
package com.willfp.eco.internal.config
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.config.interfaces.LoadableConfig
import com.willfp.eco.core.config.updating.ConfigHandler
import com.willfp.eco.core.config.updating.ConfigUpdater
import com.willfp.eco.internal.config.json.EcoLoadableJSONConfig
import com.willfp.eco.internal.config.json.EcoUpdatableJSONConfig
import com.willfp.eco.internal.config.updating.exceptions.InvalidUpdateMethodException
import com.willfp.eco.internal.config.yaml.EcoLoadableYamlConfig
import com.willfp.eco.internal.config.yaml.EcoUpdatableYamlConfig
import org.reflections.Reflections
import org.reflections.scanners.MethodAnnotationsScanner
@@ -24,7 +19,7 @@ class EcoConfigHandler(
override fun callUpdate() {
for (method in reflections.getMethodsAnnotatedWith(ConfigUpdater::class.java)) {
kotlin.runCatching {
runCatching {
when (method.parameterCount) {
0 -> method.invoke(null)
1 -> method.invoke(null, this.plugin)
@@ -50,11 +45,11 @@ class EcoConfigHandler(
override fun updateConfigs() {
for (config in configs) {
when (config) {
is EcoUpdatableYamlConfig -> config.update()
is EcoUpdatableJSONConfig -> config.update()
is EcoLoadableYamlConfig -> config.reloadFromFile()
is EcoLoadableJSONConfig -> config.reloadFromFile()
is EcoUpdatableConfig -> config.update()
is EcoLoadableConfig -> config.reloadFromFile()
}
}
}
}
}
class InvalidUpdateMethodException(message: String) : RuntimeException(message)

View File

@@ -0,0 +1,16 @@
package com.willfp.eco.internal.config
import com.willfp.eco.core.config.ConfigType
import com.willfp.eco.core.placeholder.StaticPlaceholder
@Suppress("UNCHECKED_CAST")
class EcoConfigSection(
type: ConfigType,
values: Map<String, Any?> = emptyMap(),
injections: Collection<StaticPlaceholder> = emptyList()
) : EcoConfig(type) {
init {
this.init(values)
this.injections = injections.toMutableList()
}
}

View File

@@ -0,0 +1,20 @@
package com.willfp.eco.internal.config
import com.google.gson.GsonBuilder
import com.google.gson.JsonElement
import com.google.gson.JsonSerializationContext
import com.google.gson.JsonSerializer
import com.willfp.eco.core.config.interfaces.Config
import java.lang.reflect.Type
object EcoGsonSerializer : JsonSerializer<Config> {
val gson = GsonBuilder()
.setPrettyPrinting()
.disableHtmlEscaping()
.registerTypeAdapter(Config::class.java, this)
.create()
override fun serialize(src: Config, typeOfSrc: Type, context: JsonSerializationContext): JsonElement {
return gson.toJsonTree(src.toMap())
}
}

View File

@@ -1,27 +1,31 @@
package com.willfp.eco.internal.config.json
package com.willfp.eco.internal.config
import com.willfp.eco.core.PluginLike
import com.willfp.eco.core.config.ConfigType
import com.willfp.eco.core.config.interfaces.LoadableConfig
import org.bukkit.configuration.file.YamlConfiguration
import java.io.File
import java.io.FileNotFoundException
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.FileReader
import java.io.IOException
import java.io.InputStreamReader
import java.io.OutputStream
import java.io.Reader
import java.nio.file.Files
import java.nio.file.StandardOpenOption
@Suppress("UNCHECKED_CAST")
open class EcoLoadableJSONConfig(
open class EcoLoadableConfig(
type: ConfigType,
configName: String,
private val plugin: PluginLike,
private val subDirectoryPath: String,
val source: Class<*>
) : EcoJSONConfigWrapper(), LoadableConfig {
val source: Class<*>,
private val requiresChangesToSave: Boolean
) : EcoConfig(type), LoadableConfig {
private val configFile: File
private val name: String = "$configName.json"
private val name: String = "$configName.${type.extension}"
private var hasChanged = false
private val header = mutableListOf<String>()
fun reloadFromFile() {
runCatching { init(configFile) }.onFailure { it.printStackTrace() }
@@ -54,19 +58,68 @@ open class EcoLoadableJSONConfig(
@Throws(IOException::class)
override fun save() {
if (requiresChangesToSave) {
if (!hasChanged) { // In order to preserve comments
return
}
}
if (configFile.delete()) {
Files.write(
configFile.toPath(),
toPlaintext().toByteArray(),
this.toPlaintext().toByteArray(),
StandardOpenOption.CREATE,
StandardOpenOption.WRITE
)
}
}
@Throws(FileNotFoundException::class)
private fun makeHeader(contents: String) {
header.clear()
if (this.type == ConfigType.YAML) {
for (line in contents.lines()) {
if (!line.startsWith("#")) {
break
}
header.add(line)
}
}
}
protected fun init(reader: Reader) {
val string = reader.readToString()
makeHeader(string)
super.init(type.toMap(string))
}
fun init(file: File) {
super.init(gson.fromJson(FileReader(file), Map::class.java) as MutableMap<String, Any>)
init(InputStreamReader(FileInputStream(file), Charsets.UTF_8))
}
override fun toPlaintext(): String {
val contents = StringBuilder()
if (this.type == ConfigType.YAML) {
for (s in header) {
contents.append(s + "\n")
}
if (header.isNotEmpty()) {
contents.append("\n")
}
}
for (line in super.toPlaintext().lines()) {
if (line.startsWith("#")) {
continue
}
contents.append(line + "\n")
}
return contents.toString()
}
override fun getName(): String {
@@ -77,8 +130,9 @@ open class EcoLoadableJSONConfig(
return configFile
}
override fun getBukkitHandle(): YamlConfiguration? {
return null
override fun set(path: String, obj: Any?) {
hasChanged = true
super.set(path, obj)
}
init {
@@ -93,4 +147,4 @@ open class EcoLoadableJSONConfig(
init(configFile)
plugin.configHandler.addConfig(this)
}
}
}

View File

@@ -0,0 +1,21 @@
package com.willfp.eco.internal.config
import com.willfp.eco.core.config.interfaces.Config
import org.yaml.snakeyaml.nodes.Node
import org.yaml.snakeyaml.representer.Represent
import org.yaml.snakeyaml.representer.Representer
class EcoRepresenter : Representer() {
init {
multiRepresenters[Config::class.java] = RepresentConfig(multiRepresenters[Map::class.java]!!)
}
private class RepresentConfig(
val handle: Represent
) : Represent {
override fun representData(data: Any): Node {
data as Config
return handle.representData(data.toMap())
}
}
}

View File

@@ -1,24 +1,25 @@
package com.willfp.eco.internal.config.json
package com.willfp.eco.internal.config
import com.willfp.eco.core.PluginLike
import org.bukkit.configuration.file.YamlConfiguration
import com.willfp.eco.core.config.ConfigType
import com.willfp.eco.core.config.interfaces.Config
import java.io.BufferedReader
import java.io.InputStreamReader
import java.nio.charset.StandardCharsets
open class EcoUpdatableJSONConfig(
open class EcoUpdatableConfig(
type: ConfigType,
configName: String,
plugin: PluginLike,
subDirectoryPath: String,
source: Class<*>,
private val removeUnused: Boolean,
requiresChangesToSave: Boolean,
vararg updateBlacklist: String
) : EcoLoadableJSONConfig(configName, plugin, subDirectoryPath, source) {
private val updateBlacklist: MutableList<String> = mutableListOf(*updateBlacklist)
) : EcoLoadableConfig(type, configName, plugin, subDirectoryPath, source, requiresChangesToSave) {
private val updateBlacklist = mutableListOf(*updateBlacklist)
fun update() {
super.clearCache()
this.init(configFile)
val newConfig = configInJar ?: return
if (newConfig.getKeys(true) == this.getKeys(true)) {
@@ -43,13 +44,14 @@ open class EcoUpdatableJSONConfig(
this.save()
}
private val configInJar: YamlConfiguration?
private val configInJar: Config?
get() {
val newIn = this.source.getResourceAsStream(resourcePath) ?: return null
val reader = BufferedReader(InputStreamReader(newIn, StandardCharsets.UTF_8))
val newConfig = YamlConfiguration()
newConfig.load(reader)
return newConfig
val config = EcoConfigSection(type, emptyMap())
config.init(type.toMap(reader.readToString()))
return config
}
init {
@@ -57,4 +59,4 @@ open class EcoUpdatableJSONConfig(
plugin.configHandler.addConfig(this)
update()
}
}
}

View File

@@ -1,8 +0,0 @@
package com.willfp.eco.internal.config.json
@Suppress("UNCHECKED_CAST")
class EcoJSONConfigSection(values: Map<String, Any?>) : EcoJSONConfigWrapper() {
init {
init(values)
}
}

View File

@@ -1,216 +0,0 @@
@file:Suppress("DEPRECATION")
package com.willfp.eco.internal.config.json
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.willfp.eco.core.config.ConfigType
import com.willfp.eco.core.config.interfaces.JSONConfig
import com.willfp.eco.util.StringUtils
import java.util.Objects
import java.util.concurrent.ConcurrentHashMap
@Suppress("UNCHECKED_CAST")
open class EcoJSONConfigWrapper : JSONConfig {
companion object {
val gson: Gson = GsonBuilder()
.setPrettyPrinting()
.disableHtmlEscaping()
.create()
}
val values = ConcurrentHashMap<String, Any?>()
private val cache = ConcurrentHashMap<String, Any>()
fun init(values: Map<String, Any?>) {
this.values.clear()
this.values.putAll(values)
}
override fun clearCache() {
cache.clear()
}
override fun toPlaintext(): String {
return gson.toJson(this.values)
}
override fun has(path: String): Boolean {
return getOfKnownType(path, Any::class.java) != null
}
private fun <T : Any?> getOfKnownType(
path: String,
clazz: Class<T>
): T? {
return getOfKnownType(path, clazz, true)
}
protected fun <T> getOfKnownType(
path: String,
clazz: Class<T>,
isBase: Boolean
): T? {
var closestPath = path
if (cache.containsKey(path) && isBase) {
return cache[path] as T?
}
if (path.contains(".")) {
val split = path.split("\\.".toRegex()).toTypedArray()
closestPath = split[0]
}
return if (values[closestPath] is Map<*, *> && path != closestPath) {
val section =
EcoJSONConfigSection((values[closestPath] as Map<String, Any?>?)!!)
section.getOfKnownType(path.substring(closestPath.length + 1), clazz, false)
} else {
if (values.containsKey(closestPath)) {
values[closestPath] as T?
} else {
null
}
}
}
override fun getKeys(deep: Boolean): List<String> {
return if (deep) {
ArrayList(getDeepKeys(HashSet(), ""))
} else {
ArrayList(values.keys)
}
}
protected fun getDeepKeys(
list: MutableSet<String>,
root: String
): Set<String> {
for (key in values.keys) {
list.add(root + key)
if (values[key] is Map<*, *>) {
val section = EcoJSONConfigSection((values[key] as Map<String, Any?>?)!!)
list.addAll(section.getDeepKeys(list, "$root$key."))
}
}
return list
}
override fun get(path: String): Any? {
return getOfKnownType(path, Any::class.java)
}
override fun set(
path: String,
`object`: Any?
) {
setRecursively(path, `object`)
clearCache()
}
protected fun setRecursively(
path: String,
obj: Any?
) {
var closestPath = path
if (path.contains(".")) {
val split = path.split("\\.".toRegex()).toTypedArray()
closestPath = split[0]
}
if (values[closestPath] is Map<*, *> && path != closestPath) {
val section = EcoJSONConfigSection((values[closestPath] as Map<String, Any?>?)!!)
section.setRecursively(path.substring(closestPath.length + 1), obj)
values[closestPath] = section.values
} else {
var ob = obj
if (ob is JSONConfig) {
ob = (obj as EcoJSONConfigWrapper).values
}
values[path] = ob
}
}
override fun getSubsection(path: String): JSONConfig {
return getSubsectionOrNull(path) ?: EcoJSONConfigSection(mutableMapOf())
}
override fun getSubsectionOrNull(path: String): JSONConfig? {
return if (values.containsKey(path)) {
val subsection = values[path] as Map<String, Any>
EcoJSONConfigSection(subsection)
} else {
null
}
}
override fun getSubsectionsOrNull(path: String): List<JSONConfig>? {
val maps = getOfKnownType(path, Any::class.java) as List<Map<String, Any>>?
?: return null
val configs = mutableListOf<JSONConfig>()
for (map in maps) {
configs.add(EcoJSONConfigSection(map))
}
return configs.toMutableList()
}
override fun getIntOrNull(path: String): Int? {
return getOfKnownType(path, Double::class.java)?.toInt()
}
override fun getIntsOrNull(path: String): MutableList<Int>? {
return (getOfKnownType(path, Any::class.java) as Collection<Int>?)?.toMutableList()
}
override fun getBoolOrNull(path: String): Boolean? {
return getOfKnownType(path, Boolean::class.java)
}
override fun getBoolsOrNull(path: String): MutableList<Boolean>? {
return (getOfKnownType(path, Any::class.java) as Collection<Boolean>?)?.toMutableList()
}
override fun getStringOrNull(
path: String,
format: Boolean,
option: StringUtils.FormatOption
): String? {
return if (has(path)) {
val string = getOfKnownType(path, String::class.java) ?: ""
return if (format) StringUtils.format(string, option) else string
} else {
null
}
}
override fun getStringsOrNull(
path: String,
format: Boolean,
option: StringUtils.FormatOption
): MutableList<String>? {
return if (has(path)) {
val strings =
(Objects.requireNonNullElse(
getOfKnownType(path, Any::class.java),
emptyList<String>()
) as List<String>).toMutableList()
return if (format) StringUtils.formatList(strings, option) else strings
} else {
null
}
}
override fun getDoubleOrNull(path: String): Double? {
return getOfKnownType(path, Double::class.java)
}
override fun getDoublesOrNull(path: String): MutableList<Double>? {
return (getOfKnownType(path, Any::class.java) as Collection<Double>?)?.toMutableList()
}
override fun getType(): ConfigType {
return ConfigType.JSON
}
override fun clone(): JSONConfig {
return EcoJSONConfigSection(this.values.toMutableMap())
}
}

View File

@@ -1,3 +0,0 @@
package com.willfp.eco.internal.config.updating.exceptions
class InvalidUpdateMethodException(message: String) : RuntimeException(message)

View File

@@ -1,82 +0,0 @@
@file:Suppress("DEPRECATION")
package com.willfp.eco.internal.config.yaml
import com.willfp.eco.core.PluginLike
import com.willfp.eco.core.config.interfaces.LoadableConfig
import com.willfp.eco.core.config.interfaces.WrappedYamlConfiguration
import org.bukkit.configuration.file.YamlConfiguration
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.OutputStream
open class EcoLoadableYamlConfig(
configName: String,
private val plugin: PluginLike,
private val subDirectoryPath: String,
val source: Class<*>
) : EcoYamlConfigWrapper<YamlConfiguration>(), WrappedYamlConfiguration, LoadableConfig {
private val configFile: File
private val name: String = "$configName.yml"
fun reloadFromFile() {
handle.load(getConfigFile())
}
final override fun createFile() {
val inputStream = source.getResourceAsStream(resourcePath)!!
val outFile = File(this.plugin.dataFolder, resourcePath)
val lastIndex = resourcePath.lastIndexOf('/')
val outDir = File(this.plugin.dataFolder, resourcePath.substring(0, lastIndex.coerceAtLeast(0)))
if (!outDir.exists()) {
outDir.mkdirs()
}
if (!outFile.exists()) {
val out: OutputStream = FileOutputStream(outFile)
inputStream.copyTo(out)
out.close()
inputStream.close()
}
}
override fun getResourcePath(): String {
val resourcePath: String = if (subDirectoryPath.isEmpty()) {
name
} else {
subDirectoryPath + name
}
return "/$resourcePath"
}
@Throws(IOException::class)
override fun save() {
handle.save(getConfigFile())
}
override fun getName(): String {
return name
}
override fun getConfigFile(): File {
return configFile
}
override fun getBukkitHandle(): YamlConfiguration? {
return handle
}
init {
val directory = File(this.plugin.dataFolder, subDirectoryPath)
if (!directory.exists()) {
directory.mkdirs()
}
if (!File(directory, name).exists()) {
createFile()
}
configFile = File(directory, name)
this.plugin.configHandler.addConfig(this)
init(YamlConfiguration.loadConfiguration(configFile))
}
}

View File

@@ -1,60 +0,0 @@
package com.willfp.eco.internal.config.yaml
import com.willfp.eco.core.PluginLike
import org.bukkit.configuration.file.YamlConfiguration
import java.io.BufferedReader
import java.io.InputStreamReader
import java.nio.charset.StandardCharsets
class EcoUpdatableYamlConfig(
configName: String,
plugin: PluginLike,
subDirectoryPath: String,
source: Class<*>,
private val removeUnused: Boolean,
vararg updateBlacklist: String
) : EcoLoadableYamlConfig(configName, plugin, subDirectoryPath, source) {
private val updateBlacklist: MutableList<String> = mutableListOf(*updateBlacklist)
fun update() {
super.clearCache()
this.handle.load(configFile)
val newConfig = configInJar ?: return
if (newConfig.getKeys(true) == this.handle.getKeys(true)) {
return
}
newConfig.getKeys(true).forEach { key ->
if (!this.handle.getKeys(true).contains(key)) {
if (updateBlacklist.stream().noneMatch { key.contains(it) }) {
this.handle.set(key, newConfig[key])
}
}
}
if (removeUnused) {
this.handle.getKeys(true).forEach { s ->
if (!newConfig.getKeys(true).contains(s)) {
if (updateBlacklist.stream().noneMatch(s::contains)) {
this.handle.set(s, null)
}
}
}
}
this.handle.save(configFile)
}
private val configInJar: YamlConfiguration?
get() {
val newIn = source.getResourceAsStream(resourcePath) ?: return null
val reader = BufferedReader(InputStreamReader(newIn, StandardCharsets.UTF_8))
val newConfig = YamlConfiguration()
newConfig.load(reader)
return newConfig
}
init {
this.updateBlacklist.removeIf { it.isEmpty() }
plugin.configHandler.addConfig(this)
update()
}
}

View File

@@ -1,9 +0,0 @@
package com.willfp.eco.internal.config.yaml
import org.bukkit.configuration.ConfigurationSection
class EcoYamlConfigSection(section: ConfigurationSection) : EcoYamlConfigWrapper<ConfigurationSection>() {
init {
init(section)
}
}

View File

@@ -1,241 +0,0 @@
package com.willfp.eco.internal.config.yaml
import com.willfp.eco.core.config.ConfigType
import com.willfp.eco.core.config.interfaces.Config
import com.willfp.eco.util.StringUtils
import org.bukkit.configuration.ConfigurationSection
import org.bukkit.configuration.file.YamlConfiguration
import java.io.StringReader
@Suppress("UNCHECKED_CAST")
open class EcoYamlConfigWrapper<T : ConfigurationSection> : Config {
lateinit var handle: T
private val cache = mutableMapOf<String, Any?>()
protected fun init(config: T): Config {
handle = config
return this
}
override fun toPlaintext(): String {
val temp = YamlConfiguration()
for (key in handle.getKeys(true)) {
temp[key] = handle[key]
}
return temp.saveToString()
}
override fun clearCache() {
cache.clear()
}
override fun has(path: String): Boolean {
return handle.contains(path)
}
override fun getKeys(deep: Boolean): List<String> {
return ArrayList(handle.getKeys(deep))
}
override fun get(path: String): Any? {
return handle[path]
}
override fun set(
path: String,
obj: Any?
) {
cache.remove(path)
handle[path] = obj
}
override fun getSubsectionOrNull(path: String): Config? {
return if (cache.containsKey(path)) {
cache[path] as Config?
} else {
val raw = handle.getConfigurationSection(path)
if (raw == null) {
cache[path] = null
} else {
cache[path] = EcoYamlConfigSection(raw)
}
getSubsectionOrNull(path)
}
}
override fun getIntOrNull(path: String): Int? {
return if (cache.containsKey(path)) {
(cache[path] as Number).toInt()
} else {
if (has(path)) {
cache[path] = handle.getDouble(path).toInt()
} else {
return null
}
getIntOrNull(path)
}
}
override fun getIntsOrNull(path: String): MutableList<Int>? {
return if (cache.containsKey(path)) {
(cache[path] as Collection<Int>).toMutableList()
} else {
if (has(path)) {
cache[path] = handle.getIntegerList(path).toMutableList()
} else {
return null
}
getIntsOrNull(path)
}
}
override fun getBoolOrNull(path: String): Boolean? {
return if (cache.containsKey(path)) {
cache[path] as Boolean
} else {
if (has(path)) {
cache[path] = handle.getBoolean(path)
} else {
return null
}
getBoolOrNull(path)
}
}
override fun getBoolsOrNull(path: String): MutableList<Boolean>? {
return if (cache.containsKey(path)) {
(cache[path] as Collection<Boolean>).toMutableList()
} else {
if (has(path)) {
cache[path] = handle.getBooleanList(path).toMutableList()
} else {
return null
}
getBoolsOrNull(path)
}
}
override fun getStringOrNull(
path: String,
format: Boolean,
option: StringUtils.FormatOption
): String? {
return if (has(path)) {
if (format && option == StringUtils.FormatOption.WITHOUT_PLACEHOLDERS) {
return if (cache.containsKey("$path\$FMT")) {
cache["$path\$FMT"] as String
} else {
val string: String = handle.getString(path, "")!!
cache["$path\$FMT"] = StringUtils.format(string, option)
getString(path, format, option)
}
} else {
val string = if (cache.containsKey(path)) {
cache[path] as String
} else {
cache[path] = handle.getString(path, "")!!
getString(path, format, option)
}
return if (format) StringUtils.format(string) else string
}
} else {
null
}
}
override fun getStringsOrNull(
path: String,
format: Boolean,
option: StringUtils.FormatOption
): MutableList<String>? {
return if (has(path)) {
if (format && option == StringUtils.FormatOption.WITHOUT_PLACEHOLDERS) {
return if (cache.containsKey("$path\$FMT")) {
(cache["$path\$FMT"] as MutableList<String>).toMutableList()
} else {
val list = if (has(path)) handle.getStringList(path) else mutableListOf<String>()
cache["$path\$FMT"] = StringUtils.formatList(list, option)
getStrings(path, true, option)
}
} else {
val strings = if (cache.containsKey(path)) {
(cache[path] as MutableList<String>).toMutableList()
} else {
cache[path] =
if (has(path)) ArrayList(handle.getStringList(path)) else mutableListOf<String>()
getStrings(path, false, option)
}
return if (format) {
StringUtils.formatList(strings, StringUtils.FormatOption.WITH_PLACEHOLDERS)
} else {
strings
}
}
} else {
null
}
}
override fun getDoubleOrNull(path: String): Double? {
return if (cache.containsKey(path)) {
(cache[path] as Number).toDouble()
} else {
if (has(path)) {
cache[path] = handle.getDouble(path)
} else {
return null
}
getDoubleOrNull(path)
}
}
override fun getDoublesOrNull(path: String): MutableList<Double>? {
return if (cache.containsKey(path)) {
(cache[path] as Collection<Double>).toMutableList()
} else {
if (has(path)) {
cache[path] = handle.getDoubleList(path).toMutableList()
} else {
return null
}
getDoublesOrNull(path)
}
}
override fun getSubsectionsOrNull(path: String): MutableList<out Config>? {
return if (has(path)) {
return if (cache.containsKey(path)) {
(cache[path] as Collection<Config>).toMutableList()
} else {
val mapList = ArrayList(handle.getMapList(path)) as List<Map<String, Any?>>
val configList = mutableListOf<Config>()
for (map in mapList) {
val temp = YamlConfiguration.loadConfiguration(StringReader(""))
temp.createSection("a", map)
configList.add(EcoYamlConfigSection(temp.getConfigurationSection("a")!!))
}
cache[path] = if (has(path)) configList else emptyList()
getSubsections(path)
}
} else {
null
}
}
override fun getType(): ConfigType {
return ConfigType.JSON
}
override fun clone(): Config {
return EcoYamlConfigSection(
YamlConfiguration.loadConfiguration(
StringReader(
toPlaintext()
)
)
)
}
}

View File

@@ -4,7 +4,6 @@ import com.willfp.eco.core.gui.menu.CloseHandler
import com.willfp.eco.core.gui.menu.Menu
import com.willfp.eco.core.gui.slot.Slot
import com.willfp.eco.internal.gui.slot.EcoSlot
import com.willfp.eco.util.StringUtils
import org.bukkit.Bukkit
import org.bukkit.NamespacedKey
import org.bukkit.entity.Player
@@ -19,7 +18,7 @@ class EcoMenu(
val slots: List<MutableList<EcoSlot>>,
private val title: String,
private val onClose: CloseHandler
): Menu {
) : Menu {
override fun getSlot(row: Int, column: Int): Slot {
if (row < 1 || row > this.rows) {
return slots[0][0]
@@ -37,33 +36,23 @@ class EcoMenu(
var i = 0
for (row in slots) {
for (item in row) {
for (slot in row) {
if (i == rows * 9) {
break
}
val slotItem = item.getItemStack(player, this)
val meta = slotItem.itemMeta
if (meta != null) {
val lore = meta.lore
if (lore != null) {
lore.replaceAll{ s -> StringUtils.format(s, player) }
meta.lore = lore
}
slotItem.itemMeta = meta
}
inventory.setItem(i, slotItem)
inventory.setItem(i, slot.getItemStack(player, this))
i++
}
}
player.openInventory(inventory)
MenuHandler.registerMenu(inventory, this)
MenuHandler.registerInventory(inventory, this, player)
return inventory
}
fun handleClose(event: InventoryCloseEvent) {
onClose.handle(event, this)
MenuHandler.unregisterMenu(event.inventory)
MenuHandler.unregisterInventory(event.inventory)
}
override fun getRows(): Int {
@@ -74,9 +63,8 @@ class EcoMenu(
return title
}
override fun getCaptiveItems(player: Player): MutableList<ItemStack> {
val inventory = MenuHandler.getExtendedInventory(player.openInventory.topInventory)
inventory ?: return mutableListOf()
override fun getCaptiveItems(player: Player): List<ItemStack> {
val inventory = player.openInventory.topInventory.asRenderedInventory() ?: return emptyList()
return inventory.captiveItems
}
@@ -86,21 +74,22 @@ class EcoMenu(
type: PersistentDataType<T, Z>,
value: Z
) {
val inventory = MenuHandler.getExtendedInventory(player.openInventory.topInventory)
inventory ?: return
val inventory = player.openInventory.topInventory.asRenderedInventory() ?: return
inventory.data[key] = value
inventory.refresh(player)
inventory.render()
}
override fun <T : Any, Z : Any> readData(player: Player, key: NamespacedKey, type: PersistentDataType<T, Z>): T? {
val inventory = MenuHandler.getExtendedInventory(player.openInventory.topInventory)
inventory ?: return null
return inventory.data[key] as T?
val inventory = player.openInventory.topInventory.asRenderedInventory() ?: return null
return inventory.data[key] as? T?
}
override fun getKeys(player: Player): MutableSet<NamespacedKey> {
val inventory = MenuHandler.getExtendedInventory(player.openInventory.topInventory)
inventory ?: return HashSet()
override fun getKeys(player: Player): Set<NamespacedKey> {
val inventory = player.openInventory.topInventory.asRenderedInventory() ?: return emptySet()
return inventory.data.keys
}
}
override fun refresh(player: Player) {
player.openInventory.topInventory.asRenderedInventory()?.render()
}
}

View File

@@ -1,32 +1,29 @@
package com.willfp.eco.internal.gui.menu
import com.willfp.eco.core.gui.menu.Menu
import org.bukkit.entity.Player
import org.bukkit.inventory.Inventory
import java.util.WeakHashMap
object MenuHandler {
private val menus = WeakHashMap<ExtendedInventory, EcoMenu>()
private val inventories = WeakHashMap<Inventory, ExtendedInventory>()
private val inventories = WeakHashMap<Inventory, MenuRenderedInventory>()
fun registerMenu(
object MenuHandler {
fun registerInventory(
inventory: Inventory,
menu: EcoMenu
menu: EcoMenu,
player: Player
) {
val extendedInventory = ExtendedInventory(inventory, menu)
inventories[inventory] = extendedInventory
menus[extendedInventory] = menu
val rendered = MenuRenderedInventory(menu, inventory, player)
inventories[inventory] = rendered
}
fun unregisterMenu(inventory: Inventory) {
menus.remove(inventories[inventory])
fun unregisterInventory(inventory: Inventory) {
inventories.remove(inventory)
}
}
fun getMenu(inventory: Inventory): Menu? {
return menus[inventories[inventory]]
}
fun Inventory.asRenderedInventory(): MenuRenderedInventory? =
inventories[this]
fun getExtendedInventory(inventory: Inventory): ExtendedInventory? {
return inventories[inventory]
}
}
fun Inventory.getMenu(): Menu? =
this.asRenderedInventory()?.menu

View File

@@ -1,35 +1,21 @@
package com.willfp.eco.internal.gui.menu
import com.willfp.eco.util.MenuUtils
import com.willfp.eco.util.StringUtils
import org.bukkit.Material
import org.bukkit.NamespacedKey
import org.bukkit.entity.Player
import org.bukkit.inventory.Inventory
import org.bukkit.inventory.ItemStack
class ExtendedInventory(
class MenuRenderedInventory(
val menu: EcoMenu,
val inventory: Inventory,
private val menu: EcoMenu
val player: Player
) {
val captiveItems = mutableListOf<ItemStack>()
val data = mutableMapOf<NamespacedKey, Any>()
fun refresh(player: Player) {
captiveItems.clear()
for (i in 0 until inventory.size) {
val (row, column) = MenuUtils.convertSlotToRowColumn(i)
val slot = menu.getSlot(row, column)
if (slot.isCaptive) {
val defaultItem = slot.getItemStack(player)
val item = inventory.getItem(i) ?: continue
if (item == defaultItem && item.type == Material.AIR) {
continue
}
captiveItems.add(item)
}
}
fun render() {
generateCaptive()
var i = 0
for (row in menu.slots) {
@@ -37,21 +23,31 @@ class ExtendedInventory(
if (i == menu.rows * 9) {
break
}
val slotItem = slot.getItemStack(player, menu)
val meta = slotItem.itemMeta
if (meta != null) {
val lore = meta.lore
if (lore != null) {
lore.replaceAll{ s -> StringUtils.format(s, player) }
meta.lore = lore
}
slotItem.itemMeta = meta
}
if (!slot.isCaptive) {
inventory.setItem(i, slotItem)
inventory.setItem(i, slot.getItemStack(player, menu))
}
i++
}
}
}
}
private fun generateCaptive() {
captiveItems.clear()
for (i in 0 until inventory.size) {
val (row, column) = MenuUtils.convertSlotToRowColumn(i)
val slot = menu.getSlot(row, column)
if (slot.isCaptive) {
val renderedItem = slot.getItemStack(player)
val itemStack = inventory.getItem(i) ?: continue
if (itemStack == renderedItem || itemStack.type.isAir || itemStack.amount == 0) {
continue
}
captiveItems.add(itemStack)
}
}
}
}

View File

@@ -5,7 +5,8 @@ import com.willfp.eco.core.gui.slot.Slot
import com.willfp.eco.core.gui.slot.functional.SlotHandler
import com.willfp.eco.core.gui.slot.functional.SlotProvider
import com.willfp.eco.core.gui.slot.functional.SlotUpdater
import com.willfp.eco.internal.gui.menu.MenuHandler
import com.willfp.eco.internal.gui.menu.getMenu
import org.bukkit.Material
import org.bukkit.entity.Player
import org.bukkit.event.inventory.ClickType
import org.bukkit.event.inventory.InventoryClickEvent
@@ -20,11 +21,12 @@ open class EcoSlot(
private val onMiddleClick: SlotHandler,
private val updater: SlotUpdater
) : Slot {
fun handleInventoryClick(
event: InventoryClickEvent,
menu: Menu
) {
event.isCancelled = true
when (event.click) {
ClickType.LEFT -> this.onLeftClick.handle(event, this, menu)
ClickType.RIGHT -> this.onRightClick.handle(event, this, menu)
@@ -36,9 +38,9 @@ open class EcoSlot(
}
override fun getItemStack(player: Player): ItemStack {
val menu = MenuHandler.getMenu(player.openInventory.topInventory)!!
val menu = player.openInventory.topInventory.getMenu()!!
val prev = provider.provide(player, menu)
return updater.update(player, menu, prev)
return updater.update(player, menu, prev) ?: ItemStack(Material.AIR)
}
fun getItemStack(
@@ -46,7 +48,8 @@ open class EcoSlot(
menu: Menu
): ItemStack {
val prev = provider.provide(player, menu)
return updater.update(player, menu, prev)
val updated = updater.update(player, menu, prev)
return updated ?: ItemStack(Material.AIR)
}
override fun isCaptive(): Boolean {

View File

@@ -41,4 +41,12 @@ class ArgParserColor : LookupArgParser {
)
}
}
override fun serializeBack(meta: ItemMeta): String? {
if (meta !is LeatherArmorMeta) {
return null
}
return "color:#${Integer.toHexString(meta.color.asRGB())}"
}
}

View File

@@ -30,4 +30,12 @@ class ArgParserCustomModelData : LookupArgParser {
testMeta.customModelData == modelData
}
}
}
override fun serializeBack(meta: ItemMeta): String? {
if (!meta.hasCustomModelData()) {
return null
}
return "custom-model-data:${meta.customModelData}"
}
}

View File

@@ -52,4 +52,26 @@ class ArgParserEnchantment : LookupArgParser {
true
}
}
override fun serializeBack(meta: ItemMeta): String? {
val enchants = mutableMapOf<Enchantment, Int>()
if (meta is EnchantmentStorageMeta) {
enchants.putAll(meta.storedEnchants)
} else {
enchants.putAll(meta.enchants)
}
if (enchants.isEmpty()) {
return null
}
val builder = StringBuilder()
for ((enchant, level) in enchants) {
builder.append("${enchant.key.key}:$level ")
}
return builder.toString().trimEnd()
}
}

View File

@@ -11,7 +11,7 @@ class ArgParserFlag : LookupArgParser {
val flags = mutableSetOf<ItemFlag>()
for (arg in args) {
val flag = kotlin.runCatching { ItemFlag.valueOf(arg.uppercase()) }.getOrNull() ?: continue
val flag = runCatching { ItemFlag.valueOf(arg.uppercase()) }.getOrNull() ?: continue
flags.add(flag)
}
@@ -27,4 +27,14 @@ class ArgParserFlag : LookupArgParser {
testMeta.itemFlags.containsAll(flags)
}
}
}
override fun serializeBack(meta: ItemMeta): String? {
val flags = meta.itemFlags
if (flags.isEmpty()) {
return null
}
return flags.joinToString(" ") { it.name.lowercase() }
}
}

View File

@@ -30,4 +30,12 @@ class ArgParserName : LookupArgParser {
testMeta.displayName == formatted
}
}
override fun serializeBack(meta: ItemMeta): String? {
if (!meta.hasDisplayName()) {
return null
}
return "name:\"${meta.displayName}\""
}
}

View File

@@ -36,4 +36,12 @@ class ArgParserTexture : LookupArgParser {
texture == SkullUtils.getSkullTexture(testMeta)
}
}
override fun serializeBack(meta: ItemMeta): String? {
if (meta !is SkullMeta) {
return null
}
return "texture:${SkullUtils.getSkullTexture(meta)}"
}
}

View File

@@ -27,4 +27,12 @@ class ArgParserUnbreakable : LookupArgParser {
testMeta.isUnbreakable
}
}
override fun serializeBack(meta: ItemMeta): String? {
if (!meta.isUnbreakable) {
return null
}
return "unbreakable"
}
}

View File

@@ -2,24 +2,17 @@ package com.willfp.eco.internal.logging
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.util.StringUtils
import org.bukkit.Bukkit
import java.util.logging.Level
import java.util.logging.Logger
class EcoLogger(plugin: EcoPlugin) : Logger(plugin.name, null as String?) {
class EcoLogger(private val plugin: EcoPlugin) : Logger(plugin.name, null as String?) {
override fun info(msg: String) {
super.info(StringUtils.format(msg))
}
override fun warning(msg: String) {
super.warning(StringUtils.format(msg))
}
override fun severe(msg: String) {
super.severe(StringUtils.format(msg))
Bukkit.getConsoleSender().sendMessage("[${plugin.name}] ${StringUtils.format(msg)}")
}
init {
parent = plugin.server.logger
this.level = Level.ALL
}
}
}

View File

@@ -62,7 +62,8 @@ class ChatComponent : ChatComponentProxy {
}
val newShowItem = showItem.nbt(
BinaryTagHolder.binaryTagHolder(
@Suppress("UnstableApiUsage", "DEPRECATION")
BinaryTagHolder.of(
CraftItemStack.asNMSCopy(
Display.display(
CraftItemStack.asBukkitCopy(

View File

@@ -62,7 +62,8 @@ class ChatComponent : ChatComponentProxy {
}
val newShowItem = showItem.nbt(
BinaryTagHolder.binaryTagHolder(
@Suppress("UnstableApiUsage", "DEPRECATION")
BinaryTagHolder.of(
CraftItemStack.asNMSCopy(
Display.display(
CraftItemStack.asBukkitCopy(

View File

@@ -22,8 +22,10 @@ class MiniMessageTranslator : MiniMessageTranslatorProxy {
).toLegacy()
}.getOrNull() ?: mut
if (startsWithPrefix) {
mut = Display.PREFIX + miniMessage
mut = if (startsWithPrefix) {
Display.PREFIX + miniMessage
} else {
miniMessage
}
return mut

View File

@@ -2,13 +2,23 @@ group 'com.willfp'
version rootProject.version
dependencies {
implementation 'com.github.Redempt:Crunch:1.0'
compileOnly 'net.kyori:adventure-platform-bukkit:4.1.0'
compileOnly 'org.apache.maven:maven-artifact:3.8.1'
compileOnly 'com.google.code.gson:gson:2.8.8'
compileOnly 'org.spigotmc:spigot-api:1.17.1-R0.1-SNAPSHOT'
compileOnly project(":eco-core:core-proxy")
compileOnly project(":eco-core:core-backend")
// Libraries
implementation 'com.github.Redempt:Crunch:1.1.2'
implementation 'mysql:mysql-connector-java:8.0.25'
implementation 'org.jetbrains.exposed:exposed-core:0.37.3'
implementation 'org.jetbrains.exposed:exposed-dao:0.37.3'
implementation 'org.jetbrains.exposed:exposed-jdbc:0.37.3'
implementation 'com.zaxxer:HikariCP:5.0.0'
implementation 'net.kyori:adventure-platform-bukkit:4.1.0'
// Included in spigot jar
compileOnly 'com.google.code.gson:gson:2.8.8'
compileOnly 'org.spigotmc:spigot-api:1.17.1-R0.1-SNAPSHOT'
// Plugin dependencies
compileOnly 'com.comphenix.protocol:ProtocolLib:4.6.1-SNAPSHOT'
compileOnly 'com.sk89q.worldguard:worldguard-bukkit:7.0.7-SNAPSHOT'
compileOnly 'com.github.TechFortress:GriefPrevention:16.17.1'
@@ -26,11 +36,6 @@ dependencies {
compileOnly 'com.github.brcdev-minecraft:shopgui-api:2.2.0'
compileOnly 'com.github.LoneDev6:API-ItemsAdder:2.4.7'
compileOnly 'com.arcaniax:HeadDatabase-API:1.3.0'
compileOnly 'org.jetbrains.exposed:exposed-core:0.37.3'
compileOnly 'org.jetbrains.exposed:exposed-dao:0.37.3'
compileOnly 'org.jetbrains.exposed:exposed-jdbc:0.37.3'
compileOnly 'mysql:mysql-connector-java:8.0.25'
compileOnly 'com.zaxxer:HikariCP:5.0.0'
compileOnly 'com.gmail.filoghost.holographicdisplays:holographicdisplays-api:2.4.0'
compileOnly 'com.github.EssentialsX:Essentials:2.18.2'
compileOnly 'com.bgsoftware:SuperiorSkyblockAPI:1.8.3'
@@ -40,7 +45,10 @@ dependencies {
compileOnly 'com.github.WhipDevelopment:CrashClaim:f9cd7d92eb'
compileOnly 'com.wolfyscript.wolfyutilities:wolfyutilities:3.16.0.0'
compileOnly 'com.github.decentsoftware-eu:decentholograms:2.1.2'
compileOnly 'io.lumine.xikage:MythicMobs:4.9.1'
// MythicMobs
compileOnly 'io.lumine:Mythic:5.0.1'
compileOnly 'io.lumine:LumineUtils:1.16.1-SNAPSHOT'
// CombatLogX V10 + NewbieHelper Expansion
compileOnly 'com.SirBlobman.combatlogx:CombatLogX-API:10.0.0.0-SNAPSHOT'
@@ -53,6 +61,7 @@ dependencies {
// LibsDisguises
compileOnly 'LibsDisguises:LibsDisguises:10.0.26'
// All other libs
compileOnly fileTree(dir: '../../lib', include: ['*.jar'])
}

View File

@@ -10,7 +10,7 @@ import com.willfp.eco.internal.EcoCleaner
import com.willfp.eco.internal.EcoPropsParser
import com.willfp.eco.internal.Plugins
import com.willfp.eco.internal.config.EcoConfigFactory
import com.willfp.eco.internal.config.updating.EcoConfigHandler
import com.willfp.eco.internal.config.EcoConfigHandler
import com.willfp.eco.internal.drops.EcoDropQueueFactory
import com.willfp.eco.internal.events.EcoEventManager
import com.willfp.eco.internal.extensions.EcoExtensionLoader

View File

@@ -110,10 +110,12 @@ import com.willfp.eco.internal.spigot.math.evaluateExpression
import com.willfp.eco.internal.spigot.proxy.FastItemStackFactoryProxy
import com.willfp.eco.internal.spigot.proxy.SkullProxy
import com.willfp.eco.internal.spigot.proxy.TPSProxy
import com.willfp.eco.internal.spigot.recipes.ShapedRecipeListener
import com.willfp.eco.internal.spigot.recipes.CraftingRecipeListener
import com.willfp.eco.internal.spigot.recipes.StackedRecipeListener
import com.willfp.eco.internal.spigot.recipes.listeners.ComplexInComplex
import com.willfp.eco.internal.spigot.recipes.listeners.ComplexInVanilla
import com.willfp.eco.internal.spigot.recipes.stackhandlers.ShapedCraftingRecipeStackHandler
import com.willfp.eco.internal.spigot.recipes.stackhandlers.ShapelessCraftingRecipeStackHandler
import com.willfp.eco.util.NumberUtils
import com.willfp.eco.util.ServerUtils
import com.willfp.eco.util.SkullUtils
@@ -156,8 +158,11 @@ abstract class EcoSpigotPlugin : EcoPlugin() {
Entities.registerArgParser(EntityArgParserSilent())
Entities.registerArgParser(EntityArgParserEquipment())
ShapedRecipeListener.registerListener(ComplexInComplex())
ShapedRecipeListener.registerListener(ComplexInVanilla())
CraftingRecipeListener.registerListener(ComplexInComplex())
CraftingRecipeListener.registerListener(ComplexInVanilla())
StackedRecipeListener.registerHandler(ShapedCraftingRecipeStackHandler())
StackedRecipeListener.registerHandler(ShapelessCraftingRecipeStackHandler())
SegmentParserGroup().register()
SegmentParserUseIfPresent().register()
@@ -171,7 +176,9 @@ abstract class EcoSpigotPlugin : EcoPlugin() {
val tpsProxy = getProxy(TPSProxy::class.java)
ServerUtils.initialize { tpsProxy.getTPS() }
NumberUtils.initCrunch { exp, player -> evaluateExpression(exp, player) }
NumberUtils.initCrunch { expression, player, statics -> evaluateExpression(expression, player, statics) }
CustomItemsManager.registerProviders()
postInit()
}
@@ -263,9 +270,8 @@ abstract class EcoSpigotPlugin : EcoPlugin() {
IntegrationLoader("HeadDatabase") { CustomItemsManager.register(CustomItemsHeadDatabase(this)) },
IntegrationLoader("ExecutableItems") { CustomItemsManager.register(CustomItemsExecutableItems()) },
IntegrationLoader("CustomCrafting") {
CustomItemsManager.register(CustomItemsCustomCrafting()); ShapedRecipeListener.registerValidator(
CustomRecipeCustomCrafting()
)
CustomItemsManager.register(CustomItemsCustomCrafting())
CraftingRecipeListener.registerValidator(CustomRecipeCustomCrafting())
},
IntegrationLoader("MythicMobs") { CustomItemsManager.register(CustomItemsMythicMobs(this)) },
@@ -317,7 +323,7 @@ abstract class EcoSpigotPlugin : EcoPlugin() {
NaturalExpGainListeners(),
ArmorListener(),
EntityDeathByEntityListeners(this),
ShapedRecipeListener(),
CraftingRecipeListener(),
StackedRecipeListener(this),
GUIListener(this),
ArrowDataListener(this),

View File

@@ -10,5 +10,6 @@ class DataYml(
"data",
plugin,
false,
ConfigType.YAML
ConfigType.YAML,
false
)

View File

@@ -27,7 +27,7 @@ abstract class EcoProfile(
return this.data[key] as T
}
this.data[key] = handler.read(uuid, key.key) ?: key.defaultValue
this.data[key] = handler.read(uuid, key) ?: key.defaultValue
return read(key)
}
@@ -36,11 +36,11 @@ abstract class EcoProfile(
return false
}
return this.data == other.data && this.uuid == other.uuid
return this.uuid == other.uuid
}
override fun hashCode(): Int {
return this.data.hashCode()
return this.uuid.hashCode()
}
companion object {

View File

@@ -23,5 +23,5 @@ interface DataHandler {
fun <T> write(uuid: UUID, key: NamespacedKey, value: T)
fun saveKeysFor(uuid: UUID, keys: Set<PersistentDataKey<*>>)
fun <T> read(uuid: UUID, key: NamespacedKey): T?
fun <T> read(uuid: UUID, key: PersistentDataKey<T>): T?
}

View File

@@ -45,6 +45,7 @@ class MySQLDataHandler(
private val serverHandler: ImplementedMySQLHandler
init {
val config = HikariConfig()
config.driverClassName = "com.mysql.cj.jdbc.Driver"
config.username = plugin.configYml.getString("mysql.user")
@@ -102,9 +103,9 @@ class MySQLDataHandler(
}
}
override fun <T> read(uuid: UUID, key: NamespacedKey): T? {
override fun <T> read(uuid: UUID, key: PersistentDataKey<T>): T? {
return applyFor(uuid) {
it.read(uuid, key)
it.read(uuid, key.key)
}
}

Some files were not shown because too many files have changed in this diff Show More