Compare commits

..

82 Commits

Author SHA1 Message Date
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
91 changed files with 1867 additions and 1929 deletions

View File

@@ -39,7 +39,7 @@ and many more.
# For developers # For developers
## Javadoc ## 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 ## 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: Maven:
@@ -88,7 +88,7 @@ Maven:
</dependency> </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: ## Build locally:

View File

@@ -12,6 +12,10 @@ dependencies {
group 'com.willfp' group 'com.willfp'
version rootProject.version version rootProject.version
java {
withJavadocJar()
}
build.dependsOn publishToMavenLocal build.dependsOn publishToMavenLocal
publishing { publishing {

View File

@@ -694,7 +694,7 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
public final FileConfiguration getConfig() { public final FileConfiguration getConfig() {
this.getLogger().warning("Call to default config method in eco plugin!"); 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 Use {@link EconomyManager#hasRegistrations()} instead.
*/ */
@Deprecated @Deprecated(forRemoval = true)
public static final Prerequisite HAS_VAULT = new Prerequisite( public static final Prerequisite HAS_VAULT = new Prerequisite(
() -> ClassUtils.exists("net.milkbowl.vault.economy.Economy"), () -> ClassUtils.exists("net.milkbowl.vault.economy.Economy"),
"Requires server to have vault" "Requires server to have vault"
@@ -54,7 +54,7 @@ public class Prerequisite {
* *
* @deprecated eco no longer supports versions before 1.17. * @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( public static final Prerequisite HAS_1_17 = new Prerequisite(
() -> ProxyConstants.NMS_VERSION.contains("17") || HAS_1_18.isMet(), () -> ProxyConstants.NMS_VERSION.contains("17") || HAS_1_18.isMet(),
"Requires server to be running 1.17+" "Requires server to be running 1.17+"

View File

@@ -10,6 +10,7 @@ import java.util.List;
/** /**
* Interface for all command implementations. * Interface for all command implementations.
*/ */
@SuppressWarnings("removal")
public interface CommandBase { public interface CommandBase {
/** /**
* Get command name. * Get command name.
@@ -81,7 +82,7 @@ public interface CommandBase {
* @see CommandHandler * @see CommandHandler
* @deprecated Use {@link CommandBase#onExecute(CommandSender, List)} instead. * @deprecated Use {@link CommandBase#onExecute(CommandSender, List)} instead.
*/ */
@Deprecated @Deprecated(forRemoval = true)
CommandHandler getHandler(); CommandHandler getHandler();
/** /**
@@ -91,7 +92,7 @@ public interface CommandBase {
* @see CommandHandler * @see CommandHandler
* @deprecated Handlers have been deprecated. * @deprecated Handlers have been deprecated.
*/ */
@Deprecated @Deprecated(forRemoval = true)
void setHandler(@NotNull CommandHandler handler); void setHandler(@NotNull CommandHandler handler);
/** /**
@@ -101,7 +102,7 @@ public interface CommandBase {
* @see TabCompleteHandler * @see TabCompleteHandler
* @deprecated Use {@link CommandBase#tabComplete(CommandSender, List)} instead. * @deprecated Use {@link CommandBase#tabComplete(CommandSender, List)} instead.
*/ */
@Deprecated @Deprecated(forRemoval = true)
TabCompleteHandler getTabCompleter(); TabCompleteHandler getTabCompleter();
/** /**
@@ -111,6 +112,6 @@ public interface CommandBase {
* @see TabCompleteHandler * @see TabCompleteHandler
* @deprecated Handlers have been deprecated. * @deprecated Handlers have been deprecated.
*/ */
@Deprecated @Deprecated(forRemoval = true)
void setTabCompleter(@NotNull TabCompleteHandler handler); 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)}. * update to use the new system: {@link CommandBase#onExecute(CommandSender, List)}.
*/ */
@FunctionalInterface @FunctionalInterface
@Deprecated(since = "6.17.0") @Deprecated(since = "6.17.0", forRemoval = true)
public interface CommandHandler { public interface CommandHandler {
/** /**
* The code to be called on execution. * 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)} * update to use the new system: {@link CommandBase#tabComplete(CommandSender, List)}
*/ */
@FunctionalInterface @FunctionalInterface
@Deprecated(since = "6.17.0") @Deprecated(since = "6.17.0", forRemoval = true)
public interface TabCompleteHandler { public interface TabCompleteHandler {
/** /**
* Handle Tab Completion. * 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.EcoPlugin;
import com.willfp.eco.core.command.CommandBase; 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.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.util.StringUtil; 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 * in order to execute the command-specific code. It's essentially an internal
* layer, hence why it's a package-private class. * layer, hence why it's a package-private class.
*/ */
@SuppressWarnings({"DeprecatedIsStillUsed"}) @SuppressWarnings({"DeprecatedIsStillUsed", "removal"})
abstract class HandledCommand implements CommandBase { abstract class HandledCommand implements CommandBase {
/** /**
* The plugin. * The plugin.
@@ -54,14 +52,14 @@ abstract class HandledCommand implements CommandBase {
*/ */
@Deprecated @Deprecated
@Nullable @Nullable
private CommandHandler handler = null; private com.willfp.eco.core.command.CommandHandler handler = null;
/** /**
* The tab completion code to be executed in the command. * The tab completion code to be executed in the command.
*/ */
@Deprecated @Deprecated
@Nullable @Nullable
private TabCompleteHandler tabCompleter = null; private com.willfp.eco.core.command.TabCompleteHandler tabCompleter = null;
/** /**
* All subcommands for the command. * All subcommands for the command.
@@ -256,27 +254,27 @@ abstract class HandledCommand implements CommandBase {
return this.subcommands; return this.subcommands;
} }
@Deprecated @Deprecated(forRemoval = true)
@Override @Override
public @Nullable CommandHandler getHandler() { public @Nullable com.willfp.eco.core.command.CommandHandler getHandler() {
return this.handler; return this.handler;
} }
@Deprecated @Deprecated(forRemoval = true)
@Override @Override
public @Nullable TabCompleteHandler getTabCompleter() { public @Nullable com.willfp.eco.core.command.TabCompleteHandler getTabCompleter() {
return this.tabCompleter; return this.tabCompleter;
} }
@Deprecated @Deprecated(forRemoval = true)
@Override @Override
public void setHandler(@Nullable final CommandHandler handler) { public void setHandler(@Nullable final com.willfp.eco.core.command.CommandHandler handler) {
this.handler = handler; this.handler = handler;
} }
@Deprecated @Deprecated(forRemoval = true)
@Override @Override
public void setTabCompleter(@Nullable final TabCompleteHandler tabCompleter) { public void setTabCompleter(@Nullable final com.willfp.eco.core.command.TabCompleteHandler tabCompleter) {
this.tabCompleter = tabCompleter; this.tabCompleter = tabCompleter;
} }
} }

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; package com.willfp.eco.core.config;
import org.jetbrains.annotations.NotNull;
/** /**
* Config types, classified by file extension. * Config types, classified by file extension.
*/ */
@@ -7,10 +9,28 @@ public enum ConfigType {
/** /**
* .json config. * .json config.
*/ */
JSON, JSON("json"),
/** /**
* .yml config. * .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

@@ -3,12 +3,17 @@ package com.willfp.eco.core.config;
import com.willfp.eco.core.Eco; import com.willfp.eco.core.Eco;
import com.willfp.eco.core.config.interfaces.Config; import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.config.wrapper.ConfigWrapper; import com.willfp.eco.core.config.wrapper.ConfigWrapper;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
/** /**
@@ -18,10 +23,19 @@ import java.util.Map;
*/ */
public class TransientConfig extends ConfigWrapper<Config> { 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. * @param config The YamlConfiguration handle.
*/ */
public TransientConfig(@NotNull final YamlConfiguration config) { 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()); )) : 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. * Create a new empty transient config.
* *
* @param values The values. * @param values The values.
*/ */
public TransientConfig(@NotNull final Map<String, Object> 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. * Create a new empty transient config.
*/ */
public TransientConfig() { 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) { @NotNull final ConfigType type) {
super(Eco.getHandler().getConfigFactory().createConfig(contents, 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; 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.ConfigType;
import com.willfp.eco.core.config.TransientConfig; 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.NumberUtils;
import com.willfp.eco.util.StringUtils; import com.willfp.eco.util.StringUtils;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
/** /**
* All configs implement this interface. * All configs implement this interface.
@@ -18,7 +27,7 @@ import java.util.Objects;
* Contains all methods that must exist in yaml and json configurations. * Contains all methods that must exist in yaml and json configurations.
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
public interface Config extends Cloneable { public interface Config extends Cloneable, PlaceholderInjectable {
/** /**
* Clears cache. * Clears cache.
*/ */
@@ -48,6 +57,19 @@ public interface Config extends Cloneable {
@NotNull @NotNull
List<String> getKeys(boolean deep); 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. * Get an object from config.
* Default implementations call {@link org.bukkit.configuration.file.YamlConfiguration#get(String)}. * Default implementations call {@link org.bukkit.configuration.file.YamlConfiguration#get(String)}.
@@ -238,35 +260,6 @@ public interface Config extends Cloneable {
return getString(path, false, StringUtils.FormatOption.WITHOUT_PLACEHOLDERS); 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. * Get a string from config.
* *
@@ -319,36 +312,6 @@ public interface Config extends Cloneable {
return getStringOrNull(path, false, StringUtils.FormatOption.WITH_PLACEHOLDERS); 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. * Get a string from config.
* *
@@ -403,36 +366,6 @@ public interface Config extends Cloneable {
return getStrings(path, false, StringUtils.FormatOption.WITH_PLACEHOLDERS); 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. * Get a list of strings from config.
* *
@@ -491,36 +424,6 @@ public interface Config extends Cloneable {
return getStringsOrNull(path, false, StringUtils.FormatOption.WITH_PLACEHOLDERS); 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. * Get a list of strings from config.
* *
@@ -563,7 +466,7 @@ public interface Config extends Cloneable {
*/ */
default double getDoubleFromExpression(@NotNull String path, default double getDoubleFromExpression(@NotNull String path,
@Nullable Player player) { @Nullable Player player) {
return NumberUtils.evaluateExpression(this.getString(path), player); return NumberUtils.evaluateExpression(this.getString(path), player, this.getInjectedPlaceholders());
} }
/** /**
@@ -629,4 +532,48 @@ public interface Config extends Cloneable {
* @return The clone. * @return The clone.
*/ */
Config 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; package com.willfp.eco.core.config.interfaces;
import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.io.File; import java.io.File;
@@ -47,14 +47,21 @@ public interface LoadableConfig extends Config {
/** /**
* Get bukkit {@link YamlConfiguration}. * Get bukkit {@link YamlConfiguration}.
* <p> * <p>
* This method is not recommended unless absolutely required as it * This used to represent the underlying config, but since 6.30.0 configs use
* only returns true if the type of config is {@link com.willfp.eco.core.config.ConfigType#YAML}, * their own implementations internally, without relying on bukkit.
* 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()}.
* *
* @return The config, or null if config is not yaml-based. * @return The config, or null if config is not yaml-based.
* @deprecated Use toBukkit() instead.
*/ */
@Nullable @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; package com.willfp.eco.core.config.updating;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
@@ -29,7 +30,8 @@ import java.lang.annotation.Target;
* }</pre> * }</pre>
* <p> * <p>
* If using kotlin, you have to annotate the method with {@code @JvmStatic} * 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> * <p>
* Config update methods in all classes in a plugin jar will be called * Config update methods in all classes in a plugin jar will be called
* on reload. * on reload.
@@ -39,5 +41,6 @@ import java.lang.annotation.Target;
*/ */
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD) @Target(ElementType.METHOD)
@Documented
public @interface ConfigUpdater { 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.ConfigType;
import com.willfp.eco.core.config.interfaces.Config; import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.config.interfaces.LoadableConfig; 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.ApiStatus;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -59,15 +59,17 @@ public interface ConfigFactory {
* @param config The handle. * @param config The handle.
* @return The config implementation. * @return The config implementation.
*/ */
Config createConfig(@NotNull YamlConfiguration config); Config createConfig(@NotNull ConfigurationSection config);
/** /**
* Create config. * Create config.
* *
* @param values The values. * @param values The values.
* @param type The config type.
* @return The config implementation. * @return The config implementation.
*/ */
Config createConfig(@NotNull Map<String, Object> values); Config createConfig(@NotNull Map<String, Object> values,
@NotNull ConfigType type);
/** /**
* Create config. * 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.ConfigType;
import com.willfp.eco.core.config.interfaces.Config; import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.placeholder.StaticPlaceholder;
import com.willfp.eco.util.StringUtils; import com.willfp.eco.util.StringUtils;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set;
/** /**
* Configs from eco have an internal implementation, * Configs from eco have an internal implementation,
@@ -59,6 +62,12 @@ public abstract class ConfigWrapper<T extends Config> implements Config {
return handle.getKeys(deep); 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 @Override
public @Nullable Object get(@NotNull final String path) { public @Nullable Object get(@NotNull final String path) {
return handle.get(path); return handle.get(path);
@@ -134,6 +143,31 @@ public abstract class ConfigWrapper<T extends Config> implements Config {
return handle.getType(); 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. * 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 com.willfp.eco.core.config.interfaces.LoadableConfig;
import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@@ -47,7 +46,7 @@ public abstract class LoadableConfigWrapper extends ConfigWrapper<LoadableConfig
} }
@Override @Override
public @Nullable YamlConfiguration getBukkitHandle() { public @NotNull YamlConfiguration toBukkit() {
return this.getHandle().getBukkitHandle(); 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

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

View File

@@ -24,7 +24,7 @@ public interface FastItemStack {
* @return A map of all enchantments. * @return A map of all enchantments.
* @deprecated Poorly named method. Use getEnchants instead. * @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) { default Map<Enchantment, Integer> getEnchantmentsOnItem(boolean checkStored) {
return getEnchants(checkStored); return getEnchants(checkStored);
} }

View File

@@ -96,6 +96,13 @@ public interface Menu {
*/ */
Set<NamespacedKey> getKeys(@NotNull Player player); 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. * Create a builder with a given amount of rows.
* *

View File

@@ -1,6 +1,10 @@
package com.willfp.eco.core.integrations.placeholder; package com.willfp.eco.core.integrations.placeholder;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.EcoPlugin; 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.apache.commons.lang.Validate;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -10,10 +14,13 @@ import java.util.Objects;
import java.util.function.Function; 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> * <p>
* It should be fairly straightforward. * It should be fairly straightforward.
*
* @deprecated Confusing functionality with inconsistent nullability and poor naming.
*/ */
@Deprecated(since = "6.28.0", forRemoval = true)
public class PlaceholderEntry { public class PlaceholderEntry {
/** /**
* The name of the placeholder, used in lookups. * The name of the placeholder, used in lookups.
@@ -140,7 +147,28 @@ public class PlaceholderEntry {
* Register the placeholder. * Register the placeholder.
*/ */
public void register() { 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 @Override

View File

@@ -4,11 +4,16 @@ import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache; import com.github.benmanes.caffeine.cache.LoadingCache;
import com.willfp.eco.core.Eco; import com.willfp.eco.core.Eco;
import com.willfp.eco.core.EcoPlugin; 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.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@@ -19,11 +24,12 @@ import java.util.concurrent.TimeUnit;
/** /**
* Class to handle placeholder integrations. * Class to handle placeholder integrations.
*/ */
@SuppressWarnings("removal")
public final class PlaceholderManager { public final class PlaceholderManager {
/** /**
* All registered placeholders. * 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. * All registered placeholder integrations.
@@ -35,7 +41,7 @@ public final class PlaceholderManager {
*/ */
private static final LoadingCache<EntryWithPlayer, String> PLACEHOLDER_CACHE = Caffeine.newBuilder() private static final LoadingCache<EntryWithPlayer, String> PLACEHOLDER_CACHE = Caffeine.newBuilder()
.expireAfterWrite(50, TimeUnit.MILLISECONDS) .expireAfterWrite(50, TimeUnit.MILLISECONDS)
.build(key -> key.entry.getResult(key.player)); .build(key -> key.entry.getValue(key.player));
/** /**
* Register a new placeholder integration. * Register a new placeholder integration.
@@ -50,16 +56,31 @@ public final class PlaceholderManager {
/** /**
* Register a placeholder. * 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) { public static void registerPlaceholder(@NotNull final Placeholder placeholder) {
EcoPlugin plugin = expansion.getPlugin() == null ? Eco.getHandler().getEcoPlugin() : expansion.getPlugin(); if (placeholder instanceof StaticPlaceholder) {
Map<String, PlaceholderEntry> pluginPlaceholders = REGISTERED_PLACEHOLDERS 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<>()); .getOrDefault(plugin, new HashMap<>());
pluginPlaceholders.put(expansion.getIdentifier(), expansion); pluginPlaceholders.put(placeholder.getIdentifier(), placeholder);
REGISTERED_PLACEHOLDERS.put(plugin, pluginPlaceholders); 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. * 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. * @param plugin The plugin for the placeholder.
* @return The value of the placeholder. * @return The value of the placeholder.
*/ */
@NotNull
public static String getResult(@Nullable final Player player, public static String getResult(@Nullable final Player player,
@NotNull final String identifier, @NotNull final String identifier,
@Nullable final EcoPlugin plugin) { @Nullable final EcoPlugin plugin) {
EcoPlugin owner = plugin == null ? Eco.getHandler().getEcoPlugin() : 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) { if (placeholder == null && plugin != null) {
PlaceholderEntry alternate = REGISTERED_PLACEHOLDERS.getOrDefault(Eco.getHandler().getEcoPlugin(), new HashMap<>()) Placeholder alternate = REGISTERED_PLACEHOLDERS.getOrDefault(Eco.getHandler().getEcoPlugin(), new HashMap<>())
.get(identifier.toLowerCase()); .get(identifier.toLowerCase());
if (alternate != null) { if (alternate != null) {
entry = alternate; placeholder = alternate;
} }
} }
if (entry == null) { if (placeholder == null) {
return ""; 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 "";
} }
return PLACEHOLDER_CACHE.get(new EntryWithPlayer(entry, player));
} }
/** /**
@@ -116,10 +144,28 @@ public final class PlaceholderManager {
*/ */
public static String translatePlaceholders(@NotNull final String text, public static String translatePlaceholders(@NotNull final String text,
@Nullable final Player player) { @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; String processed = text;
for (PlaceholderIntegration integration : REGISTERED_INTEGRATIONS) { for (PlaceholderIntegration integration : REGISTERED_INTEGRATIONS) {
processed = integration.translate(processed, player); 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; return processed;
} }
@@ -138,8 +184,8 @@ public final class PlaceholderManager {
return found; return found;
} }
private static record EntryWithPlayer(@NotNull PlaceholderEntry entry, private record EntryWithPlayer(@NotNull PlayerPlaceholder entry,
@Nullable Player player) { @NotNull Player player) {
} }

View File

@@ -8,6 +8,7 @@ import com.willfp.eco.core.recipe.parts.EmptyTestableItem;
import com.willfp.eco.core.recipe.parts.MaterialTestableItem; import com.willfp.eco.core.recipe.parts.MaterialTestableItem;
import com.willfp.eco.core.recipe.parts.ModifiedTestableItem; import com.willfp.eco.core.recipe.parts.ModifiedTestableItem;
import com.willfp.eco.core.recipe.parts.TestableStack; 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.NamespacedKeyUtils;
import com.willfp.eco.util.NumberUtils; import com.willfp.eco.util.NumberUtils;
import org.bukkit.Material; import org.bukkit.Material;
@@ -155,11 +156,16 @@ public final class Items {
String[] split = args[0].toLowerCase().split(":"); String[] split = args[0].toLowerCase().split(":");
if (split.length == 1) { 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) { if (material == null || material == Material.AIR) {
return new EmptyTestableItem(); return new EmptyTestableItem();
} }
item = new MaterialTestableItem(material); item = isWildcard ? new UnrestrictedMaterialTestableItem(material) : new MaterialTestableItem(material);
} }
if (split.length == 2) { if (split.length == 2) {
@@ -183,11 +189,16 @@ public final class Items {
This has been superseded by id amount This has been superseded by id amount
*/ */
if (part == null) { 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) { if (material == null || material == Material.AIR) {
return new EmptyTestableItem(); return new EmptyTestableItem();
} }
item = new MaterialTestableItem(material); item = isWildcard ? new UnrestrictedMaterialTestableItem(material) : new MaterialTestableItem(material);
stackAmount = Integer.parseInt(split[1]); stackAmount = Integer.parseInt(split[1]);
} else { } else {
item = part; item = part;
@@ -298,7 +309,7 @@ public final class Items {
* @param itemStack The itemStack to check. * @param itemStack The itemStack to check.
* @return If is recipe. * @return If is recipe.
*/ */
public static boolean isCustomItem(@NotNull final ItemStack itemStack) { public static boolean isCustomItem(@Nullable final ItemStack itemStack) {
return getCustomItem(itemStack) != null; return getCustomItem(itemStack) != null;
} }
@@ -309,7 +320,11 @@ public final class Items {
* @return The custom item, or null if not exists. * @return The custom item, or null if not exists.
*/ */
@Nullable @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); return CACHE.get(HashedItem.of(itemStack)).map(Items::getOrWrap).orElse(null);
} }

View File

@@ -16,7 +16,7 @@ public interface LookupHandler<T extends Testable<?>> {
* <p> * <p>
* You shouldn't override this method unless you're doing something * You shouldn't override this method unless you're doing something
* technically interesting or weird. This is the entry point for all * 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. * parsing.
* *
* @param key The key. * @param key The key.
@@ -56,7 +56,7 @@ public interface LookupHandler<T extends Testable<?>> {
/** /**
* Get the failsafe object. * Get the failsafe object.
* <p> * <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 * cause issues with segment parsers. See {@link com.willfp.eco.core.items.ItemsLookupHandler} and
* {@link com.willfp.eco.core.recipe.parts.EmptyTestableItem} for examples. * {@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. * @param message The message to send.
* @deprecated Proxy Errors should include a cause. * @deprecated Proxy Errors should include a cause.
*/ */
@Deprecated @Deprecated(forRemoval = true)
public ProxyError(@NotNull final String message) { public ProxyError(@NotNull final String message) {
super(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. * @deprecated Poorly named, exception when it's actually an error, contains doubly nested errors.
*/ */
@SuppressWarnings("removal")
@Deprecated(since = "6.24.0", forRemoval = true) @Deprecated(since = "6.24.0", forRemoval = true)
public class UnsupportedVersionException extends ProxyError { public class UnsupportedVersionException extends ProxyError {
/** /**
@@ -16,7 +17,7 @@ public class UnsupportedVersionException extends ProxyError {
* @param message The message to send. * @param message The message to send.
* @deprecated Use the default constructor. * @deprecated Use the default constructor.
*/ */
@Deprecated(since = "6.24.0") @Deprecated(since = "6.24.0", forRemoval = true)
public UnsupportedVersionException(@NotNull final String message) { public UnsupportedVersionException(@NotNull final String message) {
super(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.Eco;
import com.willfp.eco.core.EcoPlugin; import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.PluginDependent; import com.willfp.eco.core.PluginDependent;
import com.willfp.eco.core.Prerequisite;
import com.willfp.eco.core.items.TestableItem; import com.willfp.eco.core.items.TestableItem;
import com.willfp.eco.core.recipe.Recipes; import com.willfp.eco.core.recipe.Recipes;
import com.willfp.eco.core.recipe.parts.EmptyTestableItem; 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)); 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(shapedRecipe);
Bukkit.getServer().addRecipe(displayedRecipe); 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. * @param slot The slot in the inventory of the item.
* @deprecated The slot is not required. * @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, public static void damageItem(@NotNull final Player player,
@NotNull final ItemStack item, @NotNull final ItemStack item,
final int damage, final int damage,

View File

@@ -1,5 +1,6 @@
package com.willfp.eco.util; package com.willfp.eco.util;
import com.willfp.eco.core.placeholder.StaticPlaceholder;
import org.apache.commons.lang.Validate; import org.apache.commons.lang.Validate;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.ApiStatus;
@@ -7,10 +8,10 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.util.Collections;
import java.util.Map; import java.util.Map;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
import java.util.function.BiFunction;
/** /**
* Utilities / API methods for numbers. * Utilities / API methods for numbers.
@@ -24,7 +25,7 @@ public final class NumberUtils {
/** /**
* Crunch handler. * Crunch handler.
*/ */
private static BiFunction<String, Player, Double> crunch = null; private static CrunchHandler crunch = null;
/** /**
* Set of roman numerals to look up. * Set of roman numerals to look up.
@@ -109,7 +110,7 @@ public final class NumberUtils {
* @return The new value. * @return The new value.
* @deprecated Pointless method. * @deprecated Pointless method.
*/ */
@Deprecated(since = "6.19.0") @Deprecated(since = "6.19.0", forRemoval = true)
public static double equalIfOver(final double toChange, public static double equalIfOver(final double toChange,
final double limit) { final double limit) {
return Math.min(toChange, limit); return Math.min(toChange, limit);
@@ -251,7 +252,21 @@ public final class NumberUtils {
*/ */
public static double evaluateExpression(@NotNull final String expression, public static double evaluateExpression(@NotNull final String expression,
@Nullable final Player player) { @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. * @param handler The handler.
*/ */
@ApiStatus.Internal @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!"); Validate.isTrue(crunch == null, "Already initialized!");
crunch = handler; 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() { private NumberUtils() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); 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. * @deprecated Stupid method.
*/ */
@NotNull @NotNull
@Deprecated(since = "6.24.0") @Deprecated(since = "6.24.0", forRemoval = true)
public static Team getMaterialColorTeam(@NotNull final Material material) { public static Team getMaterialColorTeam(@NotNull final Material material) {
return fromChatColor(MATERIAL_COLORS.getOrDefault(material, ChatColor.WHITE)); return fromChatColor(MATERIAL_COLORS.getOrDefault(material, ChatColor.WHITE));
} }

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,206 @@
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 clearCache() {
// No cache
}
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?
) {
this.clearCache()
val nearestPath = path.split(".")[0]
if (path.contains(".")) {
val remainingPath = path.removePrefix("${nearestPath}.")
if (remainingPath.isEmpty()) {
return
}
val section = get(nearestPath)
if (section == null) {
values[nearestPath] = EcoConfigSection(this.type)
return set(path, obj)
} else if (section is Config) {
section.set(remainingPath, obj)
return
}
}
if (obj == null) {
values.remove(nearestPath)
} else {
values[nearestPath] = obj.constrainConfigTypes(type)
}
}
override fun getSubsection(path: String): Config {
return getSubsectionOrNull(path) ?: EcoConfigSection(type, mutableMapOf(), 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)
this.clearCache()
}
override fun getInjectedPlaceholders(): List<StaticPlaceholder> {
return injections.toList()
}
override fun clearInjectedPlaceholders() {
injections.clear()
this.clearCache()
}
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,37 @@ import com.willfp.eco.core.config.ConfigType
import com.willfp.eco.core.config.interfaces.Config import com.willfp.eco.core.config.interfaces.Config
import com.willfp.eco.core.config.interfaces.LoadableConfig import com.willfp.eco.core.config.interfaces.LoadableConfig
import com.willfp.eco.core.config.wrapper.ConfigFactory import com.willfp.eco.core.config.wrapper.ConfigFactory
import com.willfp.eco.internal.config.json.EcoJSONConfigSection import org.bukkit.configuration.ConfigurationSection
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
object EcoConfigFactory : ConfigFactory { object EcoConfigFactory : ConfigFactory {
override fun createConfig(config: YamlConfiguration): Config { override fun createConfig(bukkit: ConfigurationSection): Config {
return EcoYamlConfigSection(config) val config = createConfig(emptyMap(), ConfigType.YAML)
} for (key in bukkit.getKeys(true)) {
config.set(key, bukkit.get(key))
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)))
} }
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( override fun createLoadableConfig(
configName: String, configName: String,
plugin: PluginLike, plugin: PluginLike,
subDirectoryPath: String, subDirectoryPath: String,
source: Class<*>, source: Class<*>,
type: ConfigType type: ConfigType
): LoadableConfig { ): LoadableConfig = EcoLoadableConfig(
return if (type == ConfigType.JSON) { type,
EcoLoadableJSONConfig( configName,
configName, plugin,
plugin, subDirectoryPath,
subDirectoryPath, source
source )
)
} else {
EcoLoadableYamlConfig(
configName,
plugin,
subDirectoryPath,
source
)
}
}
override fun createUpdatableConfig( override fun createUpdatableConfig(
configName: String, configName: String,
@@ -69,25 +45,13 @@ object EcoConfigFactory : ConfigFactory {
removeUnused: Boolean, removeUnused: Boolean,
type: ConfigType, type: ConfigType,
vararg updateBlacklist: String vararg updateBlacklist: String
): LoadableConfig { ): LoadableConfig = EcoUpdatableConfig(
return if (type == ConfigType.JSON) { type,
EcoUpdatableJSONConfig( configName,
configName, plugin,
plugin, subDirectoryPath,
subDirectoryPath, source,
source, removeUnused,
removeUnused, *updateBlacklist
*updateBlacklist )
) }
} else {
EcoUpdatableYamlConfig(
configName,
plugin,
subDirectoryPath,
source,
removeUnused,
*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.EcoPlugin
import com.willfp.eco.core.config.interfaces.LoadableConfig import com.willfp.eco.core.config.interfaces.LoadableConfig
import com.willfp.eco.core.config.updating.ConfigHandler import com.willfp.eco.core.config.updating.ConfigHandler
import com.willfp.eco.core.config.updating.ConfigUpdater 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.Reflections
import org.reflections.scanners.MethodAnnotationsScanner import org.reflections.scanners.MethodAnnotationsScanner
@@ -24,7 +19,7 @@ class EcoConfigHandler(
override fun callUpdate() { override fun callUpdate() {
for (method in reflections.getMethodsAnnotatedWith(ConfigUpdater::class.java)) { for (method in reflections.getMethodsAnnotatedWith(ConfigUpdater::class.java)) {
kotlin.runCatching { runCatching {
when (method.parameterCount) { when (method.parameterCount) {
0 -> method.invoke(null) 0 -> method.invoke(null)
1 -> method.invoke(null, this.plugin) 1 -> method.invoke(null, this.plugin)
@@ -50,11 +45,11 @@ class EcoConfigHandler(
override fun updateConfigs() { override fun updateConfigs() {
for (config in configs) { for (config in configs) {
when (config) { when (config) {
is EcoUpdatableYamlConfig -> config.update() is EcoUpdatableConfig -> config.update()
is EcoUpdatableJSONConfig -> config.update() is EcoLoadableConfig -> config.reloadFromFile()
is EcoLoadableYamlConfig -> config.reloadFromFile()
is EcoLoadableJSONConfig -> 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,30 @@
package com.willfp.eco.internal.config.json package com.willfp.eco.internal.config
import com.willfp.eco.core.PluginLike import com.willfp.eco.core.PluginLike
import com.willfp.eco.core.config.ConfigType
import com.willfp.eco.core.config.interfaces.LoadableConfig import com.willfp.eco.core.config.interfaces.LoadableConfig
import org.bukkit.configuration.file.YamlConfiguration
import java.io.File import java.io.File
import java.io.FileNotFoundException import java.io.FileInputStream
import java.io.FileOutputStream import java.io.FileOutputStream
import java.io.FileReader
import java.io.IOException import java.io.IOException
import java.io.InputStreamReader
import java.io.OutputStream import java.io.OutputStream
import java.io.Reader
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.StandardOpenOption import java.nio.file.StandardOpenOption
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
open class EcoLoadableJSONConfig( open class EcoLoadableConfig(
type: ConfigType,
configName: String, configName: String,
private val plugin: PluginLike, private val plugin: PluginLike,
private val subDirectoryPath: String, private val subDirectoryPath: String,
val source: Class<*> val source: Class<*>
) : EcoJSONConfigWrapper(), LoadableConfig { ) : EcoConfig(type), LoadableConfig {
private val configFile: File 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() { fun reloadFromFile() {
runCatching { init(configFile) }.onFailure { it.printStackTrace() } runCatching { init(configFile) }.onFailure { it.printStackTrace() }
@@ -54,19 +57,64 @@ open class EcoLoadableJSONConfig(
@Throws(IOException::class) @Throws(IOException::class)
override fun save() { override fun save() {
if (!hasChanged) { // In order to preserve comments
return
}
if (configFile.delete()) { if (configFile.delete()) {
Files.write( Files.write(
configFile.toPath(), configFile.toPath(),
toPlaintext().toByteArray(), this.toPlaintext().toByteArray(),
StandardOpenOption.CREATE, StandardOpenOption.CREATE,
StandardOpenOption.WRITE StandardOpenOption.WRITE
) )
} }
} }
@Throws(FileNotFoundException::class) private fun makeHeader(contents: String) {
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) { 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 { override fun getName(): String {
@@ -77,8 +125,9 @@ open class EcoLoadableJSONConfig(
return configFile return configFile
} }
override fun getBukkitHandle(): YamlConfiguration? { override fun set(path: String, obj: Any?) {
return null hasChanged = true
super.set(path, obj)
} }
init { init {
@@ -93,4 +142,4 @@ open class EcoLoadableJSONConfig(
init(configFile) init(configFile)
plugin.configHandler.addConfig(this) 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,21 +1,22 @@
package com.willfp.eco.internal.config.json package com.willfp.eco.internal.config
import com.willfp.eco.core.PluginLike 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.BufferedReader
import java.io.InputStreamReader import java.io.InputStreamReader
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
open class EcoUpdatableJSONConfig( open class EcoUpdatableConfig(
type: ConfigType,
configName: String, configName: String,
plugin: PluginLike, plugin: PluginLike,
subDirectoryPath: String, subDirectoryPath: String,
source: Class<*>, source: Class<*>,
private val removeUnused: Boolean, private val removeUnused: Boolean,
vararg updateBlacklist: String vararg updateBlacklist: String
) : EcoLoadableJSONConfig(configName, plugin, subDirectoryPath, source) { ) : EcoLoadableConfig(type, configName, plugin, subDirectoryPath, source) {
private val updateBlacklist = mutableListOf(*updateBlacklist)
private val updateBlacklist: MutableList<String> = mutableListOf(*updateBlacklist)
fun update() { fun update() {
super.clearCache() super.clearCache()
@@ -43,13 +44,14 @@ open class EcoUpdatableJSONConfig(
this.save() this.save()
} }
private val configInJar: YamlConfiguration? private val configInJar: Config?
get() { get() {
val newIn = this.source.getResourceAsStream(resourcePath) ?: return null val newIn = this.source.getResourceAsStream(resourcePath) ?: return null
val reader = BufferedReader(InputStreamReader(newIn, StandardCharsets.UTF_8)) val reader = BufferedReader(InputStreamReader(newIn, StandardCharsets.UTF_8))
val newConfig = YamlConfiguration()
newConfig.load(reader) val config = EcoConfigSection(type, emptyMap())
return newConfig config.init(type.toMap(reader.readToString()))
return config
} }
init { init {
@@ -57,4 +59,4 @@ open class EcoUpdatableJSONConfig(
plugin.configHandler.addConfig(this) plugin.configHandler.addConfig(this)
update() 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.menu.Menu
import com.willfp.eco.core.gui.slot.Slot import com.willfp.eco.core.gui.slot.Slot
import com.willfp.eco.internal.gui.slot.EcoSlot import com.willfp.eco.internal.gui.slot.EcoSlot
import com.willfp.eco.util.StringUtils
import org.bukkit.Bukkit import org.bukkit.Bukkit
import org.bukkit.NamespacedKey import org.bukkit.NamespacedKey
import org.bukkit.entity.Player import org.bukkit.entity.Player
@@ -19,7 +18,7 @@ class EcoMenu(
val slots: List<MutableList<EcoSlot>>, val slots: List<MutableList<EcoSlot>>,
private val title: String, private val title: String,
private val onClose: CloseHandler private val onClose: CloseHandler
): Menu { ) : Menu {
override fun getSlot(row: Int, column: Int): Slot { override fun getSlot(row: Int, column: Int): Slot {
if (row < 1 || row > this.rows) { if (row < 1 || row > this.rows) {
return slots[0][0] return slots[0][0]
@@ -37,33 +36,23 @@ class EcoMenu(
var i = 0 var i = 0
for (row in slots) { for (row in slots) {
for (item in row) { for (slot in row) {
if (i == rows * 9) { if (i == rows * 9) {
break break
} }
val slotItem = item.getItemStack(player, this) inventory.setItem(i, slot.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)
i++ i++
} }
} }
player.openInventory(inventory) player.openInventory(inventory)
MenuHandler.registerMenu(inventory, this) MenuHandler.registerInventory(inventory, this, player)
return inventory return inventory
} }
fun handleClose(event: InventoryCloseEvent) { fun handleClose(event: InventoryCloseEvent) {
onClose.handle(event, this) onClose.handle(event, this)
MenuHandler.unregisterMenu(event.inventory) MenuHandler.unregisterInventory(event.inventory)
} }
override fun getRows(): Int { override fun getRows(): Int {
@@ -74,9 +63,8 @@ class EcoMenu(
return title return title
} }
override fun getCaptiveItems(player: Player): MutableList<ItemStack> { override fun getCaptiveItems(player: Player): List<ItemStack> {
val inventory = MenuHandler.getExtendedInventory(player.openInventory.topInventory) val inventory = player.openInventory.topInventory.asRenderedInventory() ?: return emptyList()
inventory ?: return mutableListOf()
return inventory.captiveItems return inventory.captiveItems
} }
@@ -86,21 +74,22 @@ class EcoMenu(
type: PersistentDataType<T, Z>, type: PersistentDataType<T, Z>,
value: Z value: Z
) { ) {
val inventory = MenuHandler.getExtendedInventory(player.openInventory.topInventory) val inventory = player.openInventory.topInventory.asRenderedInventory() ?: return
inventory ?: return
inventory.data[key] = value 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? { override fun <T : Any, Z : Any> readData(player: Player, key: NamespacedKey, type: PersistentDataType<T, Z>): T? {
val inventory = MenuHandler.getExtendedInventory(player.openInventory.topInventory) val inventory = player.openInventory.topInventory.asRenderedInventory() ?: return null
inventory ?: return null return inventory.data[key] as? T?
return inventory.data[key] as T?
} }
override fun getKeys(player: Player): MutableSet<NamespacedKey> { override fun getKeys(player: Player): Set<NamespacedKey> {
val inventory = MenuHandler.getExtendedInventory(player.openInventory.topInventory) val inventory = player.openInventory.topInventory.asRenderedInventory() ?: return emptySet()
inventory ?: return HashSet()
return inventory.data.keys 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 package com.willfp.eco.internal.gui.menu
import com.willfp.eco.core.gui.menu.Menu import com.willfp.eco.core.gui.menu.Menu
import org.bukkit.entity.Player
import org.bukkit.inventory.Inventory import org.bukkit.inventory.Inventory
import java.util.WeakHashMap import java.util.WeakHashMap
object MenuHandler { private val inventories = WeakHashMap<Inventory, MenuRenderedInventory>()
private val menus = WeakHashMap<ExtendedInventory, EcoMenu>()
private val inventories = WeakHashMap<Inventory, ExtendedInventory>()
fun registerMenu( object MenuHandler {
fun registerInventory(
inventory: Inventory, inventory: Inventory,
menu: EcoMenu menu: EcoMenu,
player: Player
) { ) {
val extendedInventory = ExtendedInventory(inventory, menu) val rendered = MenuRenderedInventory(menu, inventory, player)
inventories[inventory] = extendedInventory inventories[inventory] = rendered
menus[extendedInventory] = menu
} }
fun unregisterMenu(inventory: Inventory) { fun unregisterInventory(inventory: Inventory) {
menus.remove(inventories[inventory])
inventories.remove(inventory) inventories.remove(inventory)
} }
}
fun getMenu(inventory: Inventory): Menu? { fun Inventory.asRenderedInventory(): MenuRenderedInventory? =
return menus[inventories[inventory]] inventories[this]
}
fun getExtendedInventory(inventory: Inventory): ExtendedInventory? { fun Inventory.getMenu(): Menu? =
return inventories[inventory] this.asRenderedInventory()?.menu
}
}

View File

@@ -1,35 +1,21 @@
package com.willfp.eco.internal.gui.menu package com.willfp.eco.internal.gui.menu
import com.willfp.eco.util.MenuUtils import com.willfp.eco.util.MenuUtils
import com.willfp.eco.util.StringUtils
import org.bukkit.Material
import org.bukkit.NamespacedKey import org.bukkit.NamespacedKey
import org.bukkit.entity.Player import org.bukkit.entity.Player
import org.bukkit.inventory.Inventory import org.bukkit.inventory.Inventory
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
class ExtendedInventory( class MenuRenderedInventory(
val menu: EcoMenu,
val inventory: Inventory, val inventory: Inventory,
private val menu: EcoMenu val player: Player
) { ) {
val captiveItems = mutableListOf<ItemStack>() val captiveItems = mutableListOf<ItemStack>()
val data = mutableMapOf<NamespacedKey, Any>() val data = mutableMapOf<NamespacedKey, Any>()
fun refresh(player: Player) { fun render() {
captiveItems.clear() generateCaptive()
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)
}
}
var i = 0 var i = 0
for (row in menu.slots) { for (row in menu.slots) {
@@ -37,21 +23,31 @@ class ExtendedInventory(
if (i == menu.rows * 9) { if (i == menu.rows * 9) {
break 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) { if (!slot.isCaptive) {
inventory.setItem(i, slotItem) inventory.setItem(i, slot.getItemStack(player, menu))
} }
i++ 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.SlotHandler
import com.willfp.eco.core.gui.slot.functional.SlotProvider import com.willfp.eco.core.gui.slot.functional.SlotProvider
import com.willfp.eco.core.gui.slot.functional.SlotUpdater 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.entity.Player
import org.bukkit.event.inventory.ClickType import org.bukkit.event.inventory.ClickType
import org.bukkit.event.inventory.InventoryClickEvent import org.bukkit.event.inventory.InventoryClickEvent
@@ -20,11 +21,12 @@ open class EcoSlot(
private val onMiddleClick: SlotHandler, private val onMiddleClick: SlotHandler,
private val updater: SlotUpdater private val updater: SlotUpdater
) : Slot { ) : Slot {
fun handleInventoryClick( fun handleInventoryClick(
event: InventoryClickEvent, event: InventoryClickEvent,
menu: Menu menu: Menu
) { ) {
event.isCancelled = true
when (event.click) { when (event.click) {
ClickType.LEFT -> this.onLeftClick.handle(event, this, menu) ClickType.LEFT -> this.onLeftClick.handle(event, this, menu)
ClickType.RIGHT -> this.onRightClick.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 { 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) val prev = provider.provide(player, menu)
return updater.update(player, menu, prev) return updater.update(player, menu, prev) ?: ItemStack(Material.AIR)
} }
fun getItemStack( fun getItemStack(
@@ -46,7 +48,8 @@ open class EcoSlot(
menu: Menu menu: Menu
): ItemStack { ): ItemStack {
val prev = provider.provide(player, menu) 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 { override fun isCaptive(): Boolean {

View File

@@ -11,7 +11,7 @@ class ArgParserFlag : LookupArgParser {
val flags = mutableSetOf<ItemFlag>() val flags = mutableSetOf<ItemFlag>()
for (arg in args) { 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) flags.add(flag)
} }

View File

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

View File

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

View File

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

View File

@@ -2,7 +2,7 @@ group 'com.willfp'
version rootProject.version version rootProject.version
dependencies { dependencies {
implementation 'com.github.Redempt:Crunch:1.0' implementation 'com.github.Redempt:Crunch:1.1.2'
compileOnly 'net.kyori:adventure-platform-bukkit:4.1.0' compileOnly 'net.kyori:adventure-platform-bukkit:4.1.0'
compileOnly 'org.apache.maven:maven-artifact:3.8.1' compileOnly 'org.apache.maven:maven-artifact:3.8.1'
compileOnly 'com.google.code.gson:gson:2.8.8' compileOnly 'com.google.code.gson:gson:2.8.8'
@@ -40,7 +40,8 @@ dependencies {
compileOnly 'com.github.WhipDevelopment:CrashClaim:f9cd7d92eb' compileOnly 'com.github.WhipDevelopment:CrashClaim:f9cd7d92eb'
compileOnly 'com.wolfyscript.wolfyutilities:wolfyutilities:3.16.0.0' compileOnly 'com.wolfyscript.wolfyutilities:wolfyutilities:3.16.0.0'
compileOnly 'com.github.decentsoftware-eu:decentholograms:2.1.2' compileOnly 'com.github.decentsoftware-eu:decentholograms:2.1.2'
compileOnly 'io.lumine.xikage:MythicMobs:4.9.1' compileOnly 'io.lumine:Mythic:5.0.1'
compileOnly 'io.lumine:LumineUtils:1.16.1-SNAPSHOT'
// CombatLogX V10 + NewbieHelper Expansion // CombatLogX V10 + NewbieHelper Expansion
compileOnly 'com.SirBlobman.combatlogx:CombatLogX-API:10.0.0.0-SNAPSHOT' compileOnly 'com.SirBlobman.combatlogx:CombatLogX-API:10.0.0.0-SNAPSHOT'

View File

@@ -10,7 +10,7 @@ import com.willfp.eco.internal.EcoCleaner
import com.willfp.eco.internal.EcoPropsParser import com.willfp.eco.internal.EcoPropsParser
import com.willfp.eco.internal.Plugins import com.willfp.eco.internal.Plugins
import com.willfp.eco.internal.config.EcoConfigFactory 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.drops.EcoDropQueueFactory
import com.willfp.eco.internal.events.EcoEventManager import com.willfp.eco.internal.events.EcoEventManager
import com.willfp.eco.internal.extensions.EcoExtensionLoader 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.FastItemStackFactoryProxy
import com.willfp.eco.internal.spigot.proxy.SkullProxy import com.willfp.eco.internal.spigot.proxy.SkullProxy
import com.willfp.eco.internal.spigot.proxy.TPSProxy 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.StackedRecipeListener
import com.willfp.eco.internal.spigot.recipes.listeners.ComplexInComplex 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.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.NumberUtils
import com.willfp.eco.util.ServerUtils import com.willfp.eco.util.ServerUtils
import com.willfp.eco.util.SkullUtils import com.willfp.eco.util.SkullUtils
@@ -156,8 +158,11 @@ abstract class EcoSpigotPlugin : EcoPlugin() {
Entities.registerArgParser(EntityArgParserSilent()) Entities.registerArgParser(EntityArgParserSilent())
Entities.registerArgParser(EntityArgParserEquipment()) Entities.registerArgParser(EntityArgParserEquipment())
ShapedRecipeListener.registerListener(ComplexInComplex()) CraftingRecipeListener.registerListener(ComplexInComplex())
ShapedRecipeListener.registerListener(ComplexInVanilla()) CraftingRecipeListener.registerListener(ComplexInVanilla())
StackedRecipeListener.registerHandler(ShapedCraftingRecipeStackHandler())
StackedRecipeListener.registerHandler(ShapelessCraftingRecipeStackHandler())
SegmentParserGroup().register() SegmentParserGroup().register()
SegmentParserUseIfPresent().register() SegmentParserUseIfPresent().register()
@@ -171,7 +176,7 @@ abstract class EcoSpigotPlugin : EcoPlugin() {
val tpsProxy = getProxy(TPSProxy::class.java) val tpsProxy = getProxy(TPSProxy::class.java)
ServerUtils.initialize { tpsProxy.getTPS() } ServerUtils.initialize { tpsProxy.getTPS() }
NumberUtils.initCrunch { exp, player -> evaluateExpression(exp, player) } NumberUtils.initCrunch { expression, player, statics -> evaluateExpression(expression, player, statics) }
postInit() postInit()
} }
@@ -263,9 +268,8 @@ abstract class EcoSpigotPlugin : EcoPlugin() {
IntegrationLoader("HeadDatabase") { CustomItemsManager.register(CustomItemsHeadDatabase(this)) }, IntegrationLoader("HeadDatabase") { CustomItemsManager.register(CustomItemsHeadDatabase(this)) },
IntegrationLoader("ExecutableItems") { CustomItemsManager.register(CustomItemsExecutableItems()) }, IntegrationLoader("ExecutableItems") { CustomItemsManager.register(CustomItemsExecutableItems()) },
IntegrationLoader("CustomCrafting") { IntegrationLoader("CustomCrafting") {
CustomItemsManager.register(CustomItemsCustomCrafting()); ShapedRecipeListener.registerValidator( CustomItemsManager.register(CustomItemsCustomCrafting())
CustomRecipeCustomCrafting() CraftingRecipeListener.registerValidator(CustomRecipeCustomCrafting())
)
}, },
IntegrationLoader("MythicMobs") { CustomItemsManager.register(CustomItemsMythicMobs(this)) }, IntegrationLoader("MythicMobs") { CustomItemsManager.register(CustomItemsMythicMobs(this)) },
@@ -317,7 +321,7 @@ abstract class EcoSpigotPlugin : EcoPlugin() {
NaturalExpGainListeners(), NaturalExpGainListeners(),
ArmorListener(), ArmorListener(),
EntityDeathByEntityListeners(this), EntityDeathByEntityListeners(this),
ShapedRecipeListener(), CraftingRecipeListener(),
StackedRecipeListener(this), StackedRecipeListener(this),
GUIListener(this), GUIListener(this),
ArrowDataListener(this), ArrowDataListener(this),

View File

@@ -1,67 +1,42 @@
package com.willfp.eco.internal.spigot.gui package com.willfp.eco.internal.spigot.gui
import com.willfp.eco.core.EcoPlugin import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.drops.DropQueue
import com.willfp.eco.internal.gui.menu.EcoMenu import com.willfp.eco.internal.gui.menu.EcoMenu
import com.willfp.eco.internal.gui.menu.MenuHandler import com.willfp.eco.internal.gui.menu.MenuHandler
import com.willfp.eco.internal.gui.menu.asRenderedInventory
import com.willfp.eco.internal.gui.menu.getMenu
import com.willfp.eco.internal.gui.slot.EcoSlot import com.willfp.eco.internal.gui.slot.EcoSlot
import com.willfp.eco.util.MenuUtils import com.willfp.eco.util.MenuUtils
import org.apache.commons.lang.Validate
import org.bukkit.entity.Player import org.bukkit.entity.Player
import org.bukkit.event.EventHandler import org.bukkit.event.EventHandler
import org.bukkit.event.EventPriority import org.bukkit.event.EventPriority
import org.bukkit.event.Listener import org.bukkit.event.Listener
import org.bukkit.event.inventory.ClickType
import org.bukkit.event.inventory.InventoryClickEvent import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.event.inventory.InventoryCloseEvent import org.bukkit.event.inventory.InventoryCloseEvent
class GUIListener(private val plugin: EcoPlugin) : Listener { class GUIListener(private val plugin: EcoPlugin) : Listener {
@EventHandler @EventHandler(priority = EventPriority.HIGH)
fun handleSlotClick(event: InventoryClickEvent) { fun handleSlotClick(event: InventoryClickEvent) {
val player = event.whoClicked val rendered = event.clickedInventory?.asRenderedInventory() ?: return
if (player !is Player) {
return val menu = rendered.menu
}
val menu = MenuHandler.getMenu(event.clickedInventory ?: return) ?: return
val (row, column) = MenuUtils.convertSlotToRowColumn(event.slot) val (row, column) = MenuUtils.convertSlotToRowColumn(event.slot)
val slot = menu.getSlot(row, column)
Validate.isTrue(slot is EcoSlot, "Slot not instance of EcoSlot!")
val ecoSlot = menu.getSlot(row, column) as EcoSlot
event.isCancelled = true
ecoSlot.handleInventoryClick(event, menu)
if (event.clickedInventory == null) { val slot = menu.getSlot(row, column) as? EcoSlot ?: return
return
}
val extendedInventory = MenuHandler.getExtendedInventory(event.clickedInventory!!) ?: return slot.handleInventoryClick(event, menu)
plugin.scheduler.run { extendedInventory.refresh(player) } plugin.scheduler.run { rendered.render() }
} }
@EventHandler @EventHandler(priority = EventPriority.HIGH)
fun handleCaptivatorSlots(event: InventoryClickEvent) {
val player = event.whoClicked
if (player !is Player) {
return
}
MenuHandler.getMenu(player.openInventory.topInventory) ?: return
MenuHandler.getExtendedInventory(player.openInventory.topInventory) ?: return
plugin.scheduler.run { MenuHandler.getExtendedInventory(player.openInventory.topInventory)?.refresh(player) }
}
@EventHandler
fun handleShiftClick(event: InventoryClickEvent) { fun handleShiftClick(event: InventoryClickEvent) {
if (!(event.click == ClickType.SHIFT_RIGHT || event.click == ClickType.SHIFT_LEFT)) { if (!event.isShiftClick) {
return return
} }
val player = event.whoClicked val player = event.whoClicked as? Player ?: return
if (player !is Player) {
return
}
val inv = player.openInventory.topInventory val inv = player.openInventory.topInventory
@@ -69,33 +44,26 @@ class GUIListener(private val plugin: EcoPlugin) : Listener {
return return
} }
val menu = MenuHandler.getMenu(inv) ?: return val menu = inv.getMenu() ?: return
val rendered = inv.asRenderedInventory() ?: return
val (row, column) = MenuUtils.convertSlotToRowColumn(inv.firstEmpty()) val (row, column) = MenuUtils.convertSlotToRowColumn(inv.firstEmpty())
val slot = menu.getSlot(row, column) val slot = menu.getSlot(row, column)
if (!slot.isCaptive) { if (!slot.isCaptive) {
event.isCancelled = true event.isCancelled = true
} }
plugin.scheduler.run { rendered.render() }
} }
@EventHandler(priority = EventPriority.HIGH) @EventHandler(priority = EventPriority.HIGH)
fun handleClose(event: InventoryCloseEvent) { fun handleClose(event: InventoryCloseEvent) {
val player = event.player val menu = event.inventory.getMenu() as? EcoMenu ?: return
if (player !is Player) {
return
}
val menu = MenuHandler.getMenu(event.inventory) ?: return
Validate.isTrue(menu is EcoMenu, "Menu not instance of EcoMenu!")
val ecoMenu = menu as EcoMenu
ecoMenu.handleClose(event)
DropQueue(player) menu.handleClose(event)
.addItems(ecoMenu.getCaptiveItems(player))
.setLocation(player.location)
.forceTelekinesis()
.push()
plugin.scheduler.run { MenuHandler.unregisterMenu(event.inventory) } plugin.scheduler.run { MenuHandler.unregisterInventory(event.inventory) }
} }
} }

View File

@@ -2,13 +2,13 @@ package com.willfp.eco.internal.spigot.integrations.customentities
import com.willfp.eco.core.entities.CustomEntity import com.willfp.eco.core.entities.CustomEntity
import com.willfp.eco.core.integrations.customentities.CustomEntitiesWrapper import com.willfp.eco.core.integrations.customentities.CustomEntitiesWrapper
import io.lumine.xikage.mythicmobs.MythicMobs import io.lumine.mythic.bukkit.MythicBukkit
import org.bukkit.NamespacedKey import org.bukkit.NamespacedKey
class CustomEntitiesMythicMobs : CustomEntitiesWrapper { class CustomEntitiesMythicMobs : CustomEntitiesWrapper {
override fun registerAllEntities() { override fun registerAllEntities() {
val mobManager = MythicMobs.inst().mobManager val mobManager = MythicBukkit.inst().mobManager
val api = MythicMobs.inst().apiHelper val api = MythicBukkit.inst().apiHelper
for (id in mobManager.mobNames) { for (id in mobManager.mobNames) {
val key = NamespacedKey.fromString("mythicmobs:${id.lowercase()}") val key = NamespacedKey.fromString("mythicmobs:${id.lowercase()}")

View File

@@ -35,7 +35,7 @@ class CustomItemsHeadDatabase(
private lateinit var api: HeadDatabaseAPI private lateinit var api: HeadDatabaseAPI
override fun provideForKey(key: String): TestableItem? { override fun provideForKey(key: String): TestableItem? {
if (this::api.isInitialized) { if (!this::api.isInitialized) {
return null return null
} }

View File

@@ -4,14 +4,14 @@ import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.integrations.customitems.CustomItemsWrapper import com.willfp.eco.core.integrations.customitems.CustomItemsWrapper
import com.willfp.eco.core.items.Items import com.willfp.eco.core.items.Items
import com.willfp.eco.core.recipe.parts.EmptyTestableItem import com.willfp.eco.core.recipe.parts.EmptyTestableItem
import io.lumine.xikage.mythicmobs.adapters.bukkit.BukkitItemStack import io.lumine.mythic.api.config.MythicLineConfig
import io.lumine.xikage.mythicmobs.api.bukkit.events.MythicDropLoadEvent import io.lumine.mythic.api.drops.DropMetadata
import io.lumine.xikage.mythicmobs.drops.Drop import io.lumine.mythic.api.drops.IMultiDrop
import io.lumine.xikage.mythicmobs.drops.DropMetadata import io.lumine.mythic.bukkit.adapters.BukkitItemStack
import io.lumine.xikage.mythicmobs.drops.IMultiDrop import io.lumine.mythic.bukkit.events.MythicDropLoadEvent
import io.lumine.xikage.mythicmobs.drops.LootBag import io.lumine.mythic.core.drops.Drop
import io.lumine.xikage.mythicmobs.drops.droppables.ItemDrop import io.lumine.mythic.core.drops.LootBag
import io.lumine.xikage.mythicmobs.io.MythicLineConfig import io.lumine.mythic.core.drops.droppables.ItemDrop
import org.bukkit.event.EventHandler import org.bukkit.event.EventHandler
import org.bukkit.event.Listener import org.bukkit.event.Listener
@@ -42,9 +42,9 @@ class CustomItemsMythicMobs(
private class MythicMobsDrop( private class MythicMobsDrop(
private val plugin: EcoPlugin, private val plugin: EcoPlugin,
private val config: MythicLineConfig itemConfig: MythicLineConfig
) : Drop(config.line, config), IMultiDrop { ) : Drop(itemConfig.line, itemConfig), IMultiDrop {
private val id = config.getString(arrayOf("type", "t", "item", "i"), this.dropVar) private val id = itemConfig.getString(arrayOf("type", "t", "item", "i"), this.dropVar)
override fun get(data: DropMetadata): LootBag { override fun get(data: DropMetadata): LootBag {
val bag = LootBag(data) val bag = LootBag(data)

View File

@@ -2,33 +2,31 @@ package com.willfp.eco.internal.spigot.integrations.customitems
import com.willfp.eco.core.integrations.customitems.CustomItemsWrapper import com.willfp.eco.core.integrations.customitems.CustomItemsWrapper
import com.willfp.eco.core.items.CustomItem import com.willfp.eco.core.items.CustomItem
import com.willfp.eco.core.items.Items
import com.willfp.eco.core.items.TestableItem
import com.willfp.eco.core.items.provider.ItemProvider
import com.willfp.eco.util.NamespacedKeyUtils import com.willfp.eco.util.NamespacedKeyUtils
import io.th0rgal.oraxen.items.OraxenItems import io.th0rgal.oraxen.items.OraxenItems
import org.bukkit.NamespacedKey
import org.bukkit.inventory.ItemStack
import java.util.Objects
import java.util.function.Predicate
class CustomItemsOraxen : CustomItemsWrapper { class CustomItemsOraxen : CustomItemsWrapper {
override fun registerAllItems() { override fun registerAllItems() {
for (item in OraxenItems.getItems()) { Items.registerItemProvider(OraxenProvider())
val stack = item.build()
val id: String = Objects.requireNonNullElse(OraxenItems.getIdByItem(item), "")
if (id.isEmpty()) {
continue
}
val key: NamespacedKey = NamespacedKeyUtils.create("oraxen", id.lowercase())
CustomItem(
key, Predicate { test: ItemStack ->
val oraxenId: String = OraxenItems.getIdByItem(test) ?: return@Predicate false
oraxenId.equals(id, ignoreCase = true)
},
stack
).register()
}
} }
override fun getPluginName(): String { override fun getPluginName(): String {
return "Oraxen" return "Oraxen"
} }
}
private class OraxenProvider : ItemProvider("oraxen") {
override fun provideForKey(key: String): TestableItem? {
val item = OraxenItems.getItemById(key) ?: return null
val id = OraxenItems.getIdByItem(item)
val namespacedKey = NamespacedKeyUtils.create("oraxen", id)
return CustomItem(
namespacedKey,
{ id.equals(OraxenItems.getIdByItem(it), ignoreCase = true) },
item.build()
)
}
}
}

View File

@@ -1,38 +1,30 @@
package com.willfp.eco.internal.spigot.math package com.willfp.eco.internal.spigot.math
import com.github.benmanes.caffeine.cache.Cache
import com.github.benmanes.caffeine.cache.Caffeine
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager import com.willfp.eco.core.integrations.placeholder.PlaceholderManager
import com.willfp.eco.core.placeholder.StaticPlaceholder
import org.bukkit.entity.Player import org.bukkit.entity.Player
import redempt.crunch.CompiledExpression import redempt.crunch.CompiledExpression
import redempt.crunch.Crunch import redempt.crunch.Crunch
import redempt.crunch.data.FastNumberParsing import redempt.crunch.data.FastNumberParsing
import redempt.crunch.functional.EvaluationEnvironment import redempt.crunch.functional.EvaluationEnvironment
private val cache = mutableMapOf<String, CompiledExpression>() private val cache: Cache<String, CompiledExpression> = Caffeine.newBuilder().build()
private val goToZero = Crunch.compileExpression("0") private val goToZero = Crunch.compileExpression("0")
fun evaluateExpression(expression: String, player: Player?): Double { fun evaluateExpression(expression: String, player: Player?, statics: Iterable<StaticPlaceholder>): Double {
val placeholderValues = PlaceholderManager.findPlaceholdersIn(expression) val placeholderValues = PlaceholderManager.findPlaceholdersIn(expression)
.map { PlaceholderManager.translatePlaceholders(expression, player) } .map { PlaceholderManager.translatePlaceholders(it, player, statics.toList()) }
.map { runCatching { FastNumberParsing.parseDouble(it) }.getOrDefault(0.0) } .map { runCatching { FastNumberParsing.parseDouble(it) }.getOrDefault(0.0) }
.toDoubleArray() .toDoubleArray()
val compiled = generateExpression(expression) val compiled = cache.get(expression) {
return runCatching { compiled.evaluate(*placeholderValues) }.getOrDefault(0.0) val placeholders = PlaceholderManager.findPlaceholdersIn(it)
} val env = EvaluationEnvironment()
env.setVariableNames(*placeholders.toTypedArray())
private fun generateExpression(expression: String): CompiledExpression { runCatching { Crunch.compileExpression(expression, env) }.getOrDefault(goToZero)
val cached = cache[expression]
if (cached != null) {
return cached
} }
val placeholders = PlaceholderManager.findPlaceholdersIn(expression) return runCatching { compiled.evaluate(*placeholderValues) }.getOrDefault(0.0)
val env = EvaluationEnvironment()
env.setVariableNames(*placeholders.toTypedArray())
val compiled = runCatching { Crunch.compileExpression(expression, env) }.getOrDefault(goToZero)
cache[expression] = compiled
return compiled
} }

View File

@@ -9,7 +9,7 @@ import org.bukkit.event.inventory.CraftItemEvent
import org.bukkit.event.inventory.PrepareItemCraftEvent import org.bukkit.event.inventory.PrepareItemCraftEvent
import org.bukkit.event.player.PlayerRecipeDiscoverEvent import org.bukkit.event.player.PlayerRecipeDiscoverEvent
class ShapedRecipeListener : Listener { class CraftingRecipeListener : Listener {
@EventHandler @EventHandler
fun preventLearningDisplayedRecipes(event: PlayerRecipeDiscoverEvent) { fun preventLearningDisplayedRecipes(event: PlayerRecipeDiscoverEvent) {
if (!EcoPlugin.getPluginNames().contains(event.recipe.namespace)) { if (!EcoPlugin.getPluginNames().contains(event.recipe.namespace)) {

View File

@@ -1,16 +1,19 @@
package com.willfp.eco.internal.spigot.recipes package com.willfp.eco.internal.spigot.recipes
import com.willfp.eco.core.EcoPlugin import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.items.TestableItem
import com.willfp.eco.core.recipe.Recipes import com.willfp.eco.core.recipe.Recipes
import com.willfp.eco.core.recipe.parts.EmptyTestableItem import com.willfp.eco.core.recipe.parts.EmptyTestableItem
import com.willfp.eco.core.recipe.parts.GroupedTestableItems import com.willfp.eco.core.recipe.parts.GroupedTestableItems
import com.willfp.eco.core.recipe.parts.TestableStack import com.willfp.eco.core.recipe.parts.TestableStack
import com.willfp.eco.core.recipe.recipes.CraftingRecipe
import org.bukkit.Material import org.bukkit.Material
import org.bukkit.event.EventHandler import org.bukkit.event.EventHandler
import org.bukkit.event.EventPriority import org.bukkit.event.EventPriority
import org.bukkit.event.Listener import org.bukkit.event.Listener
import org.bukkit.event.inventory.InventoryClickEvent import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.inventory.CraftingInventory import org.bukkit.inventory.CraftingInventory
import org.bukkit.inventory.ItemStack
import kotlin.math.max import kotlin.math.max
import kotlin.math.min import kotlin.math.min
@@ -38,13 +41,18 @@ class StackedRecipeListener(
val recipe = Recipes.getMatch(matrix) ?: return val recipe = Recipes.getMatch(matrix) ?: return
// Get the handler for the type of recipe
@Suppress("UNCHECKED_CAST")
val handler = handlers.firstOrNull { recipe::class.java.isAssignableFrom(it.recipeType) } ?: return
var isStackedRecipe = false var isStackedRecipe = false
var maxCraftable = Int.MAX_VALUE var maxCraftable = Int.MAX_VALUE
// Start by calculating the maximum number of items to craft // Start by calculating the maximum number of items to craft
val maxToCraftData = handler.makeData(recipe)
for (i in 0..8) { for (i in 0..8) {
val item = inventory.matrix.getOrNull(i) ?: continue val item = inventory.matrix.getOrNull(i) ?: continue
val part = recipe.parts[i].let { val part = handler.getPart(recipe, i, item, maxToCraftData).let {
if (it is GroupedTestableItems) { if (it is GroupedTestableItems) {
it.getMatchingChild(item) it.getMatchingChild(item)
} else it } else it
@@ -68,9 +76,11 @@ class StackedRecipeListener(
val existingResult = inventory.result val existingResult = inventory.result
// Deduct the correct number of items from the inventory // Deduct the correct number of items from the inventory
val deductionData = handler.makeData(recipe)
for (i in 0..8) { for (i in 0..8) {
val item = inventory.matrix.getOrNull(i) ?: continue val item = inventory.matrix.getOrNull(i) ?: continue
val part = recipe.parts[i].let { val part = handler.getPart(recipe, i, item, deductionData).let {
if (it is GroupedTestableItems) { if (it is GroupedTestableItems) {
it.getMatchingChild(item) it.getMatchingChild(item)
} else it } else it
@@ -128,4 +138,18 @@ class StackedRecipeListener(
block() block()
plugin.scheduler.run(block) plugin.scheduler.run(block)
} }
companion object {
private val handlers = mutableListOf<StackedRecipeHandler>()
fun registerHandler(handler: StackedRecipeHandler) {
handlers.add(handler)
}
}
}
interface StackedRecipeHandler {
fun makeData(recipe: CraftingRecipe): Any?
fun getPart(recipe: CraftingRecipe, position: Int, item: ItemStack, data: Any?): TestableItem?
val recipeType: Class<out CraftingRecipe>
} }

View File

@@ -2,9 +2,9 @@ package com.willfp.eco.internal.spigot.recipes.listeners
import com.willfp.eco.core.EcoPlugin import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.recipe.Recipes import com.willfp.eco.core.recipe.Recipes
import com.willfp.eco.internal.spigot.recipes.CraftingRecipeListener
import com.willfp.eco.internal.spigot.recipes.GenericCraftEvent import com.willfp.eco.internal.spigot.recipes.GenericCraftEvent
import com.willfp.eco.internal.spigot.recipes.RecipeListener import com.willfp.eco.internal.spigot.recipes.RecipeListener
import com.willfp.eco.internal.spigot.recipes.ShapedRecipeListener
import org.bukkit.entity.Player import org.bukkit.entity.Player
class ComplexInComplex : RecipeListener { class ComplexInComplex : RecipeListener {
@@ -19,7 +19,7 @@ class ComplexInComplex : RecipeListener {
val matrix = event.inventory.matrix val matrix = event.inventory.matrix
if (ShapedRecipeListener.validators.any { it.validate(event) }) { if (CraftingRecipeListener.validators.any { it.validate(event) }) {
return return
} }
@@ -44,4 +44,4 @@ class ComplexInComplex : RecipeListener {
event.deny() event.deny()
} }
} }
} }

View File

@@ -2,9 +2,9 @@ package com.willfp.eco.internal.spigot.recipes.listeners
import com.willfp.eco.core.EcoPlugin import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.items.Items import com.willfp.eco.core.items.Items
import com.willfp.eco.internal.spigot.recipes.CraftingRecipeListener
import com.willfp.eco.internal.spigot.recipes.GenericCraftEvent import com.willfp.eco.internal.spigot.recipes.GenericCraftEvent
import com.willfp.eco.internal.spigot.recipes.RecipeListener import com.willfp.eco.internal.spigot.recipes.RecipeListener
import com.willfp.eco.internal.spigot.recipes.ShapedRecipeListener
class ComplexInVanilla : RecipeListener { class ComplexInVanilla : RecipeListener {
override fun handle(event: GenericCraftEvent) { override fun handle(event: GenericCraftEvent) {
@@ -12,7 +12,7 @@ class ComplexInVanilla : RecipeListener {
return return
} }
if (ShapedRecipeListener.validators.any { it.validate(event) }) { if (CraftingRecipeListener.validators.any { it.validate(event) }) {
return return
} }
@@ -22,4 +22,4 @@ class ComplexInVanilla : RecipeListener {
} }
} }
} }
} }

View File

@@ -0,0 +1,15 @@
package com.willfp.eco.internal.spigot.recipes.stackhandlers
import com.willfp.eco.core.items.TestableItem
import com.willfp.eco.core.recipe.recipes.CraftingRecipe
import com.willfp.eco.core.recipe.recipes.ShapedCraftingRecipe
import com.willfp.eco.internal.spigot.recipes.StackedRecipeHandler
import org.bukkit.inventory.ItemStack
class ShapedCraftingRecipeStackHandler : StackedRecipeHandler {
override val recipeType = ShapedCraftingRecipe::class.java
override fun makeData(recipe: CraftingRecipe): Any? = null
override fun getPart(recipe: CraftingRecipe, position: Int, item: ItemStack, data: Any?): TestableItem? =
recipe.parts[position]
}

View File

@@ -0,0 +1,28 @@
package com.willfp.eco.internal.spigot.recipes.stackhandlers
import com.willfp.eco.core.items.TestableItem
import com.willfp.eco.core.recipe.recipes.CraftingRecipe
import com.willfp.eco.core.recipe.recipes.ShapelessCraftingRecipe
import com.willfp.eco.internal.spigot.recipes.StackedRecipeHandler
import org.bukkit.inventory.ItemStack
class ShapelessCraftingRecipeStackHandler :
StackedRecipeHandler {
override val recipeType = ShapelessCraftingRecipe::class.java
override fun makeData(recipe: CraftingRecipe): Any {
recipe as ShapelessCraftingRecipe
return recipe.newTest()
}
override fun getPart(
recipe: CraftingRecipe,
position: Int,
item: ItemStack,
data: Any?
): TestableItem? {
recipe as ShapelessCraftingRecipe
data as ShapelessCraftingRecipe.RecipeTest
return data.matchAndRemove(item)
}
}

View File

@@ -1,3 +1,3 @@
version = 6.27.1 version = 6.30.0
plugin-name = eco plugin-name = eco
kotlin.code.style = official kotlin.code.style = official