Compare commits

..

144 Commits

Author SHA1 Message Date
Auxilor
710cec4bc1 Merge branch 'develop' 2023-05-19 13:17:05 +01:00
Auxilor
fa0ec7d6b0 Updated to 6.60.4 2023-05-19 13:16:20 +01:00
Auxilor
5093799775 Merge remote-tracking branch 'origin/develop' into develop 2023-05-19 13:15:34 +01:00
Will FP
8535f23ede Merge pull request #274
Fix PvPManager integration
2023-05-19 13:15:24 +01:00
Will FP
8e09ae7f4c Merge pull request #275
Add RoyaleEconomy to prices system
2023-05-19 13:14:54 +01:00
Sen2000
bbc2513b40 Fix RoyaleEconomy integration 2023-05-19 16:54:05 +07:00
Sen2000
c7f8063a3a Add RoyaleEconomy to prices system 2023-05-19 09:11:17 +07:00
ChanceSD
14b0f1be0c Fix PvPManager integration 2023-05-18 22:42:49 +01:00
Auxilor
af20bb315b Updated to 6.60.3 2023-05-18 15:35:54 +01:00
Auxilor
6645e216d5 Fixed stupid profile saver bug 2023-05-18 15:35:47 +01:00
Auxilor
eddf240f0c Updated to 6.60.2 2023-05-18 14:33:21 +01:00
Auxilor
4f406353ba Fixed data bug 2023-05-18 14:33:14 +01:00
Auxilor
095494dd2e Fixed PersistentDataKeyType.BIG_DECIMAL and javadoc 2023-05-17 18:39:48 +01:00
Auxilor
fd92645500 Updated to 6.60.1 2023-05-17 17:27:00 +01:00
Auxilor
1a6ac29083 Fixed PersistentDataKeyType.BIG_DECIMAL 2023-05-17 17:26:48 +01:00
Auxilor
7edc00d459 Added createTasks 2023-05-16 14:50:02 +01:00
Auxilor
a51bad058f Updated to 6.60.0 2023-05-16 14:47:06 +01:00
Auxilor
89ebb8ba59 Updated to 6.59.1 2023-05-15 17:43:03 +01:00
Auxilor
f0ae8f4f84 Fixed DelegatedBukkitCommand (again) 2023-05-15 17:42:49 +01:00
Auxilor
7d6cf78442 Cleanup 2023-05-15 16:38:33 +01:00
Auxilor
780d8f3b86 Fixed DefaultMap 2023-05-15 16:33:58 +01:00
Auxilor
146a0130f9 Fixed DefaultMap 2023-05-15 16:33:32 +01:00
Auxilor
df8c3411cb Updated to 6.59.0 2023-05-15 12:06:56 +01:00
Auxilor
4fc3c22a7d Added PersistentDataKeyType#BIG_DECIMAL 2023-05-15 12:05:37 +01:00
Auxilor
cfc4808bb8 Updated to 6.58.1 2023-05-14 16:36:05 +01:00
Auxilor
4ac6325a41 Fixed T?.toSingletonList() 2023-05-14 16:35:56 +01:00
Auxilor
4aed33751d EcoPlugin#afterLoad now runs 2 ticks after, with a preliminary reload 1 tick after startup to fix load order bugs 2023-05-13 17:43:31 +01:00
Auxilor
3fe1c2c69f Added EcoPlugin#cancelsTasksOnReload 2023-05-13 12:10:08 +01:00
Auxilor
5feaa84b2c Revert "Probably janky solution to command aliases"
This reverts commit 862b588c8d.
2023-05-12 16:57:12 +01:00
Auxilor
d8793bc2bb Fixed command aliases 2023-05-12 16:54:03 +01:00
Auxilor
15c512f3ca Cleanup 2023-05-12 15:40:28 +01:00
Auxilor
15ff2fb1d6 Fixed registry locks 2023-05-12 15:39:51 +01:00
Auxilor
862b588c8d Probably janky solution to command aliases 2023-05-12 15:37:29 +01:00
Auxilor
3c2a99b5f4 Added MenuBuilder#defaultPage 2023-05-12 14:53:44 +01:00
Auxilor
2d23c05c47 Added kotlin extensions to NumberUtils methods 2023-05-12 14:26:00 +01:00
Auxilor
8fc55d3393 Merge remote-tracking branch 'origin/develop' into develop 2023-05-12 13:53:45 +01:00
Auxilor
5900a756e4 Added margin options to line wrapping 2023-05-12 13:53:38 +01:00
Auxilor
c18b85f223 AAAAAAA 2023-05-12 13:53:16 +01:00
Auxilor
85116108c2 Oops 2023-05-12 13:47:47 +01:00
Auxilor
044f141bd0 Added margin options to line wrapping 2023-05-12 13:45:21 +01:00
Auxilor
9ca7f99fdb Added StringUtils#lineWrap kotlin extensions 2023-05-12 13:28:21 +01:00
Auxilor
7aa7770a3e Added StringUtils#lineWrap for lists 2023-05-12 13:27:13 +01:00
Auxilor
10202917fa Added StringUtils#lineWrap to wrap strings while preserving formatting 2023-05-11 17:24:05 +01:00
Auxilor
43df79e3b1 Fixed captive slots not forcing a re-render on shift click 2023-05-10 16:03:29 +01:00
Auxilor
5ef244f0bc Added option to store plugin data locally, even if eco is set to use a database 2023-05-10 15:19:15 +01:00
Auxilor
861f076c11 Updated to 6.58.0 2023-05-10 14:52:52 +01:00
Auxilor
7bed43059f Added Slot#shouldRenderOnClick 2023-05-08 18:39:27 +01:00
Auxilor
37e271c96c More optimisations to EcoConfig 2023-05-04 14:32:52 +01:00
Auxilor
3dad48e24d Updated to 6.57.2 2023-05-03 23:45:01 +01:00
Auxilor
ae77e4810b Digsusting hacks to optimise eval pipeline 2023-05-03 23:44:53 +01:00
Auxilor
3d50e37c37 Merge branch 'master' into develop 2023-05-03 23:01:41 +01:00
Auxilor
421fd3bd04 Finally removed LegacyMySQLDataHandler 2023-05-03 16:03:36 +01:00
Auxilor
5ecae0a366 Updated to 6.57.1 2023-05-03 14:19:01 +01:00
Auxilor
5de4914fd7 Fixed expression loading, improved hash codes down evaluation pipeline 2023-05-03 14:18:50 +01:00
Auxilor
0f9bf094ae Ignore case 2023-05-02 18:47:54 +01:00
Auxilor
e67680f397 Improved PlaceholderAPI 2023-05-02 17:39:15 +01:00
Auxilor
73c0a5d655 Fixed more stupidity 2023-05-02 17:17:41 +01:00
Auxilor
220ed26f4a Fixed stupidity 2023-05-02 17:14:03 +01:00
Auxilor
edf2ea41c7 Cleaned up RECIPE ERROR message to be nicer 2023-05-02 16:13:33 +01:00
Auxilor
16859b8ce5 Revert "Temporarily disabled Wolfyscript"
This reverts commit f973281dd9.
2023-05-02 15:01:01 +01:00
Auxilor
10fe7d190a Clean 2023-05-02 14:58:25 +01:00
Auxilor
60a1f2429c Added plugin-version and plugin to extension.yml 2023-05-02 14:26:53 +01:00
Auxilor
cc6dc1e67c Improved PAPI expansion 2023-05-02 13:57:23 +01:00
Auxilor
f4f5941691 Fixed duplicate config placeholder bug 2023-05-02 13:56:10 +01:00
Auxilor
f973281dd9 Temporarily disabled Wolfyscript 2023-05-02 13:55:50 +01:00
Auxilor
8acd76f363 Updated to 6.57.0 2023-05-01 19:53:45 +01:00
Auxilor
4a165a86dc Added PluginLike#getFile 2023-05-01 19:53:37 +01:00
Auxilor
43b7c393b9 Cleaned up data desync PR 2023-04-29 15:52:13 +01:00
Auxilor
714952bc45 PR Cleanup 2023-04-29 15:47:42 +01:00
Will FP
322e179b81 Merge pull request #269
Add PlayerPoints to prices system
2023-04-29 15:43:54 +01:00
Auxilor
469be73ada Added option to translate placeholders without a context 2023-04-29 15:03:02 +01:00
Auxilor
7eac60146f Added placeholderContext#copy 2023-04-29 13:44:03 +01:00
Auxilor
ad0223e1bb Cleaned up PlaceholderContext 2023-04-29 13:22:18 +01:00
BuildTools
430117f342 Add PlayerPoints to prices system 2023-04-29 10:20:53 +07:00
Auxilor
fa87cae81e Clarified PlaceholderParser 2023-04-28 19:59:13 +01:00
Auxilor
5695750fc5 Optimised additional player placeholder parsing 2023-04-28 17:05:40 +01:00
Auxilor
45e8a57880 Fixed DynamicInjectablePlaceholder 2023-04-28 16:24:34 +01:00
Auxilor
17fcd2c1b5 Added DynamicPlaceholder and DynamicInjectablePlaceholder 2023-04-28 16:24:18 +01:00
Auxilor
0c1e17c351 Massively optimised placeholder parsing with ListViewOfCollection 2023-04-28 16:11:21 +01:00
Auxilor
9415515849 Simplified enable logs 2023-04-28 14:34:30 +01:00
Auxilor
d0e957ea37 Cleaned up imports 2023-04-28 14:23:44 +01:00
Will FP
69514e2f85 Merge pull request #268
Denizen Integration
2023-04-28 14:22:29 +01:00
FireML
259e35c978 Script Name Alternative 2023-04-28 00:47:01 +01:00
FireML
40aec26f15 Initial Denizen Support 2023-04-27 23:03:34 +01:00
Auxilor
67b2fdd594 Merge branch 'master' into develop
# Conflicts:
#	gradle.properties
2023-04-27 19:27:57 +01:00
Auxilor
83f86983f6 Updated to 6.55.4 2023-04-27 19:22:30 +01:00
Auxilor
3ba98a9a5e Fixed UltraEconomy 2023-04-27 19:22:20 +01:00
Auxilor
f19e565fbe Moved to native fastToDoubleOrNull function 2023-04-27 19:08:10 +01:00
Auxilor
ee3ecb643b Cleanup 2023-04-27 18:55:03 +01:00
Auxilor
3aefb0e481 Switched back to ConcurrentHashMap in EcoConfig 2023-04-27 18:04:02 +01:00
Auxilor
d9eb1e1c26 Merge branch 'fix-data-desync' into develop 2023-04-27 18:02:09 +01:00
Auxilor
70631e67c5 Config injections no longer use a ConcurrentHashMap 2023-04-27 15:51:16 +01:00
Auxilor
82797e7342 Rewrote placeholder parsing 2023-04-27 15:42:46 +01:00
Auxilor
d2917341b1 Removed use-lower-protocollib-priority 2023-04-26 20:31:53 +01:00
Auxilor
760f42be0c Refactor, made math cache TTL configurable 2023-04-26 20:31:44 +01:00
Auxilor
bb01af2ab2 Optimised StringUtils#replaceQuickly 2023-04-26 18:22:56 +01:00
Auxilor
517d890c05 More optimisations to evaluation pipeline 2023-04-26 17:37:31 +01:00
Auxilor
f02fc56778 Added alternate crunch evaluation option 2023-04-26 16:55:40 +01:00
Auxilor
4417b09c14 Various optimisations along the evaluation pipeline 2023-04-26 16:10:04 +01:00
Auxilor
6b37fafa90 Further simplified Config 2023-04-26 15:25:50 +01:00
Auxilor
f8c5c9f06d Revert "Added KPlaceholderContext"
This reverts commit 0fd6cbaa08.
2023-04-26 15:02:05 +01:00
Auxilor
0fd6cbaa08 Added KPlaceholderContext 2023-04-26 14:58:48 +01:00
Auxilor
81afa32eb0 Added placeholderContext kotlin builder 2023-04-26 14:47:40 +01:00
Auxilor
7387fe2332 Improved default config methods 2023-04-26 14:44:13 +01:00
Auxilor
80fa05da98 Added PlaceholderContext#copyWithItem 2023-04-26 14:07:22 +01:00
Auxilor
77754249ad Added SimplePlaceholder and SimpleInjectablePlaceholder 2023-04-26 13:23:29 +01:00
Auxilor
1843cf0f8a PlaceholderManager#translatePlaceholders now includes injections 2023-04-26 13:13:59 +01:00
Auxilor
05eb5ee993 Rewrote more placeholder backend, deprecated MathContext 2023-04-26 13:07:18 +01:00
Auxilor
7bc11ee716 Added new format options 2023-04-25 19:19:43 +01:00
Auxilor
f566aec00e PlaceholderContext now extends MathContext 2023-04-25 19:11:15 +01:00
Auxilor
36d47c55a1 Updated to 6.56.0 2023-04-25 18:57:22 +01:00
Auxilor
40463213ac Added PlaceholderContext as a unified way to parse placeholders 2023-04-25 18:57:06 +01:00
Auxilor
6ec80d30ad Updated to 6.55.3 2023-04-25 10:28:38 +01:00
Auxilor
7ccee60a0c Fixed IntegrationRegistry#executeSafely 2023-04-25 10:28:23 +01:00
Auxilor
0805b48763 Updated to 6.55.2 2023-04-24 22:12:47 +01:00
Auxilor
d737322aaa Merge remote-tracking branch 'origin/master'
# Conflicts:
#	gradle.properties
2023-04-24 22:12:19 +01:00
Auxilor
4ddc150e1c Updated to 6.54.1 2023-04-24 22:11:55 +01:00
Auxilor
0da119d89d Finally reimplemented BlockUtils#getVein 2023-04-24 22:10:42 +01:00
Auxilor
fc0a07d1c5 Config injections now use a ConcurrentHashMap 2023-04-24 22:05:37 +01:00
Cyramek
4ce2850138 fix data desync 2023-04-23 14:35:17 +02:00
Auxilor
58316c2a06 Fixed locale bug with Registry 2023-04-20 20:32:07 +01:00
Auxilor
d96ad10960 Added locker to registry 2023-04-20 19:27:52 +01:00
Auxilor
21283b0928 Added ExternalDataStoreObjectAdapter 2023-04-20 19:19:00 +01:00
Auxilor
2797687f01 Cleanup 2023-04-20 17:54:23 +01:00
Auxilor
9c68d1fbc3 Merge branch 'master' into develop
# Conflicts:
#	eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/integrations/customitems/CustomItemsOraxen.kt
2023-04-20 17:45:06 +01:00
Auxilor
9371d9b88d Upstream 2023-04-20 17:44:58 +01:00
Auxilor
14e2ead488 Fixed javadoc, updated Kingdoms 2023-04-20 17:42:46 +01:00
Auxilor
5473bb8ef8 Cleaned up IntegrationRegistry 2023-04-20 17:34:18 +01:00
Auxilor
90c55849ae Isolated integration registration 2023-04-20 17:24:56 +01:00
Auxilor
9809140cf9 Isolated remaining integrations 2023-04-20 17:23:24 +01:00
Auxilor
49a82dc005 Isolated ShopManager 2023-04-20 17:15:18 +01:00
Auxilor
92dec03b9a Added IntegrationRegistry 2023-04-20 17:09:46 +01:00
Auxilor
7453c70b87 Fixed imports 2023-04-20 16:52:49 +01:00
Auxilor
3038ea43d0 Updated to 6.55.0 2023-04-20 16:51:11 +01:00
Auxilor
3c6ddd8255 Added OutdatedEcoVersionError 2023-04-20 16:50:35 +01:00
Auxilor
925ee04cc1 Added Version as a cross-version compatible version of DefaultArtifactVersion 2023-04-20 16:48:20 +01:00
Auxilor
6f7de8716b Added KRegistrable 2023-04-20 16:41:35 +01:00
Auxilor
a17b951a8b Made registry lockable and iterable 2023-04-20 16:40:06 +01:00
Auxilor
f003ed06a8 Fixed imports 2023-04-19 18:00:19 +01:00
Auxilor
f864953da2 Updated to 6.54.1 2023-04-19 17:38:22 +01:00
Auxilor
6ef31444ac Removed Checkstyle CI, cleaned up Oraxen PR 2023-04-19 17:38:09 +01:00
Will FP
99258116de Merge pull request #264 from MCCasper/master
fix oraxen and SNBT
2023-04-19 12:33:14 -04:00
casper
a59c68102e fix SNBT matching the same tag for diff materials 2023-04-19 11:09:26 -05:00
casper
2482525fe2 load oraxen later 2023-04-18 22:23:49 -05:00
122 changed files with 3620 additions and 1331 deletions

View File

@@ -1,15 +0,0 @@
name: Check PR Codestyle
on: [ pull_request ]
jobs:
checkstyle:
name: Checkstyle
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: dbelyaev/action-checkstyle@v0.5.1
with:
github_token: ${{ secrets.github_token }}
reporter: github-pr-review
level: warning
checkstyle_config: ../../config/checkstyle/checkstyle.xml

View File

@@ -82,6 +82,12 @@ allprojects {
// UltraEconomy
maven("https://repo.techscode.com/repository/maven-releases/")
// PlayerPoints
maven("https://repo.rosewooddev.io/repository/public/")
// Denizen
maven("https://maven.citizensnpcs.co/repo")
}
dependencies {

View File

@@ -25,8 +25,8 @@ import com.willfp.eco.core.gui.menu.MenuType;
import com.willfp.eco.core.gui.slot.SlotBuilder;
import com.willfp.eco.core.gui.slot.functional.SlotProvider;
import com.willfp.eco.core.items.TestableItem;
import com.willfp.eco.core.math.MathContext;
import com.willfp.eco.core.packet.Packet;
import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import com.willfp.eco.core.proxy.ProxyFactory;
import com.willfp.eco.core.scheduling.Scheduler;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
@@ -135,6 +135,14 @@ public interface Eco {
@NotNull
Logger createLogger(@NotNull EcoPlugin plugin);
/**
* Get NOOP logger.
*
* @return The logger.
*/
@NotNull
Logger getNOOPLogger();
/**
* Create a PAPI integration.
*
@@ -170,7 +178,7 @@ public interface Eco {
* @return The PluginCommandBase implementation
*/
@NotNull
PluginCommandBase createPluginCommand(@NotNull CommandBase parentDelegate,
PluginCommandBase createPluginCommand(@NotNull PluginCommandBase parentDelegate,
@NotNull EcoPlugin plugin,
@NotNull String name,
@NotNull String permission,
@@ -393,15 +401,6 @@ public interface Eco {
@NotNull
ServerProfile getServerProfile();
/**
* Unload a player profile from memory.
* <p>
* This will not save the profile first.
*
* @param uuid The uuid.
*/
void unloadPlayerProfile(@NotNull UUID uuid);
/**
* Create dummy entity - never spawned, exists purely in code.
*
@@ -531,7 +530,7 @@ public interface Eco {
* @return The value of the expression, or zero if invalid.
*/
double evaluate(@NotNull String expression,
@NotNull MathContext context);
@NotNull PlaceholderContext context);
/**
* Get the menu a player currently has open.
@@ -563,6 +562,30 @@ public interface Eco {
void sendPacket(@NotNull Player player,
@NotNull Packet packet);
/**
* Translate placeholders in a string.
*
* @param text The text.
* @param context The context.
* @return The translated text.
*/
@NotNull
String translatePlaceholders(@NotNull String text,
@NotNull PlaceholderContext context);
/**
* Get the value of a placeholder.
*
* @param plugin The plugin that owns the placeholder.
* @param args The placeholder arguments.
* @param context The context.
* @return The value, or null if invalid.
*/
@Nullable
String getPlaceholderValue(@Nullable EcoPlugin plugin,
@NotNull String args,
@NotNull PlaceholderContext context);
/**
* Get the instance of eco; the bridge between the api frontend and the implementation backend.
*

View File

@@ -19,9 +19,10 @@ import com.willfp.eco.core.proxy.ProxyFactory;
import com.willfp.eco.core.registry.Registrable;
import com.willfp.eco.core.registry.Registry;
import com.willfp.eco.core.scheduling.Scheduler;
import com.willfp.eco.core.version.OutdatedEcoVersionError;
import com.willfp.eco.core.version.Version;
import com.willfp.eco.core.web.UpdateChecker;
import org.apache.commons.lang.Validate;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.bukkit.Bukkit;
import org.bukkit.NamespacedKey;
import org.bukkit.configuration.file.FileConfiguration;
@@ -32,6 +33,7 @@ import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
@@ -124,7 +126,7 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike, Regist
/**
* The logger for the plugin.
*/
private final Logger logger;
private Logger logger;
/**
* If the server is running an outdated version of the plugin.
@@ -162,6 +164,11 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike, Regist
*/
private final ListMap<LifecyclePosition, Runnable> afterLoad = new ListMap<>();
/**
* The tasks to run on task creation.
*/
private final ListMap<LifecyclePosition, Runnable> createTasks = new ListMap<>();
/**
* Create a new plugin.
* <p>
@@ -347,14 +354,14 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike, Regist
they have an outdated version of eco installed.
*/
DefaultArtifactVersion runningVersion = new DefaultArtifactVersion(Eco.get().getEcoPlugin().getDescription().getVersion());
DefaultArtifactVersion requiredVersion = new DefaultArtifactVersion(this.getMinimumEcoVersion());
Version runningVersion = new Version(Eco.get().getEcoPlugin().getDescription().getVersion());
Version requiredVersion = new Version(this.getMinimumEcoVersion());
if (!(runningVersion.compareTo(requiredVersion) > 0 || runningVersion.equals(requiredVersion))) {
this.getLogger().severe("You are running an outdated version of eco!");
this.getLogger().severe("You must be on at least" + this.getMinimumEcoVersion());
this.getLogger().severe("Download the newest version here:");
this.getLogger().severe("https://polymart.org/download/773/recent/JSpprMspkuyecf5y1wQ2Jn8OoLQSQ_IW");
Bukkit.getPluginManager().disablePlugin(this);
throw new OutdatedEcoVersionError("This plugin requires at least eco version " + this.getMinimumEcoVersion() + " to run.");
}
}
@@ -365,13 +372,12 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike, Regist
public final void onEnable() {
super.onEnable();
this.getLogger().info("");
this.getLogger().info("Loading " + this.getColor() + this.getName());
if (this.getResourceId() != 0 && !Eco.get().getEcoPlugin().getConfigYml().getBool("no-update-checker")) {
new UpdateChecker(this).getVersion(version -> {
DefaultArtifactVersion currentVersion = new DefaultArtifactVersion(this.getDescription().getVersion());
DefaultArtifactVersion mostRecentVersion = new DefaultArtifactVersion(version);
Version currentVersion = new Version(this.getDescription().getVersion());
Version mostRecentVersion = new Version(version);
if (!(currentVersion.compareTo(mostRecentVersion) > 0 || currentVersion.equals(mostRecentVersion))) {
this.outdated = true;
this.getLogger().warning(this.getName() + " is out of date! (Version " + this.getDescription().getVersion() + ")");
@@ -405,7 +411,9 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike, Regist
this.loadedIntegrations.removeIf(pl -> pl.equalsIgnoreCase(this.getName()));
this.getLogger().info("Loaded integrations: " + String.join(", ", this.getLoadedIntegrations()));
if (!this.getLoadedIntegrations().isEmpty()) {
this.getLogger().info("Loaded integrations: " + String.join(", ", this.getLoadedIntegrations()));
}
Prerequisite.update();
@@ -422,22 +430,35 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike, Regist
this.loadPluginCommands().forEach(PluginCommand::register);
this.getScheduler().runLater(this::afterLoad, 1);
// Run preliminary reload to resolve load order issues
this.getScheduler().runLater(() -> {
Logger before = this.getLogger();
// Temporary silence logger.
this.logger = Eco.get().getNOOPLogger();
this.reload(false);
this.logger = before;
}, 1);
this.getScheduler().runLater(this::afterLoad, 2);
if (this.isSupportingExtensions()) {
this.getExtensionLoader().loadExtensions();
if (this.getExtensionLoader().getLoadedExtensions().isEmpty()) {
this.getLogger().info("&cNo extensions found");
} else {
this.getLogger().info("Extensions Loaded:");
this.getExtensionLoader().getLoadedExtensions().forEach(extension -> this.getLogger().info("- " + extension.getName() + " v" + extension.getVersion()));
if (!this.getExtensionLoader().getLoadedExtensions().isEmpty()) {
List<String> loadedExtensions = this.getExtensionLoader().getLoadedExtensions().stream().map(
extension -> extension.getName() + " v" + extension.getVersion()
).toList();
this.getLogger().info(
"Loaded extensions: " +
String.join(", ", loadedExtensions)
);
}
}
this.handleLifecycle(this.onEnable, this::handleEnable);
this.getLogger().info("");
}
/**
@@ -596,14 +617,30 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike, Regist
* Reload the plugin.
*/
public final void reload() {
this.reload(true);
}
/**
* Reload the plugin.
*
* @param cancelTasks If tasks should be cancelled.
*/
public final void reload(final boolean cancelTasks) {
this.getConfigHandler().updateConfigs();
this.getScheduler().cancelAll();
if (cancelTasks) {
this.getScheduler().cancelAll();
}
this.getConfigHandler().callUpdate();
this.getConfigHandler().callUpdate(); // Call twice to fix issues
this.handleLifecycle(this.onReload, this::handleReload);
if (cancelTasks) {
this.handleLifecycle(this.createTasks, this::createTasks);
}
for (Extension extension : this.extensionLoader.getLoadedExtensions()) {
extension.handleReload();
}
@@ -717,6 +754,15 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike, Regist
}
/**
* The plugin-specific code to create tasks.
* <p>
* Override when needed.
*/
protected void createTasks() {
}
/**
* The plugin-specific code to be executed after the server is up.
* <p>
@@ -1081,7 +1127,7 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike, Regist
*
* @return The config handler.
*/
public ConfigHandler getConfigHandler() {
public @NotNull ConfigHandler getConfigHandler() {
return this.configHandler;
}
@@ -1145,9 +1191,24 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike, Regist
return this.getMetadataValueFactory().create(value);
}
/**
* Get if all {@link com.willfp.eco.core.data.keys.PersistentDataKey}'s for this
* plugin should be saved locally (via data.yml.) even if eco is using a database.
*
* @return If using local storage.
*/
public boolean isUsingLocalStorage() {
return this.configYml.isUsingLocalStorage();
}
@Override
@NotNull
public final String getID() {
return Registry.tryFitPattern(this.getName());
}
@Override
public @NotNull File getFile() {
return super.getFile();
}
}

View File

@@ -1,6 +1,8 @@
package com.willfp.eco.core;
import com.willfp.eco.core.config.updating.ConfigHandler;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.logging.Logger;
@@ -13,12 +15,13 @@ import java.util.logging.Logger;
*/
public interface PluginLike {
/**
* Get the data folder of the object.
* Get the data folder.
* <p>
* Returns the plugin data folder for a plugin, or the extension's parent plugin's folder
*
* @return The data folder.
*/
@NotNull
File getDataFolder();
/**
@@ -26,6 +29,7 @@ public interface PluginLike {
*
* @return The config handler.
*/
@NotNull
ConfigHandler getConfigHandler();
/**
@@ -33,5 +37,16 @@ public interface PluginLike {
*
* @return The logger.
*/
@NotNull
Logger getLogger();
/**
* Get the actual file.
*
* @return The file, i.e. the jar file.
*/
@Nullable
default File getFile() {
return null;
}
}

View File

@@ -9,6 +9,11 @@ import org.jetbrains.annotations.NotNull;
* Default plugin config.yml.
*/
public class ConfigYml extends BaseConfig {
/**
* The use local storage key.
*/
public static final String KEY_USES_LOCAL_STORAGE = "use-local-storage";
/**
* Config.yml.
*
@@ -52,4 +57,13 @@ public class ConfigYml extends BaseConfig {
final boolean removeUnused) {
super(name, plugin, removeUnused, ConfigType.YAML);
}
/**
* Get if the plugin is using local storage.
*
* @return The prefix.
*/
public boolean isUsingLocalStorage() {
return this.getBool(KEY_USES_LOCAL_STORAGE);
}
}

View File

@@ -6,6 +6,7 @@ import com.willfp.eco.core.config.Configs;
import com.willfp.eco.core.placeholder.AdditionalPlayer;
import com.willfp.eco.core.placeholder.InjectablePlaceholder;
import com.willfp.eco.core.placeholder.PlaceholderInjectable;
import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import com.willfp.eco.util.NumberUtils;
import com.willfp.eco.util.StringUtils;
import org.bukkit.configuration.ConfigurationSection;
@@ -134,7 +135,7 @@ public interface Config extends Cloneable, PlaceholderInjectable {
* @return The computed value, or 0 if not found or invalid.
*/
default int getIntFromExpression(@NotNull String path) {
return getIntFromExpression(path, null);
return getIntFromExpression(path, PlaceholderContext.of(this));
}
/**
@@ -163,6 +164,18 @@ public interface Config extends Cloneable, PlaceholderInjectable {
return Double.valueOf(getDoubleFromExpression(path, player, additionalPlayers)).intValue();
}
/**
* Get a decimal value via a mathematical expression.
*
* @param path The key to fetch the value from.
* @param context The placeholder context.
* @return The computed value, or 0 if not found or invalid.
*/
default int getIntFromExpression(@NotNull String path,
@NotNull PlaceholderContext context) {
return Double.valueOf(getDoubleFromExpression(path, context)).intValue();
}
/**
* Get an integer from config.
@@ -256,6 +269,22 @@ public interface Config extends Cloneable, PlaceholderInjectable {
return getString(path, true, option);
}
/**
* Get a formatted string from config.
*
* @param path The key to fetch the value from.
* @param context The placeholder context.
* @return The found value, or an empty string if not found.
*/
@NotNull
default String getFormattedString(@NotNull String path,
@NotNull PlaceholderContext context) {
return Objects.requireNonNullElse(
getFormattedStringOrNull(path, context),
""
);
}
/**
* Get a string from config.
* <p>
@@ -288,7 +317,7 @@ public interface Config extends Cloneable, PlaceholderInjectable {
* Get a formatted string from config.
*
* @param path The key to fetch the value from.
* @return The found value, or an empty string if not found.
* @return The found value, or null if not found.
*/
@Nullable
default String getFormattedStringOrNull(@NotNull String path) {
@@ -300,7 +329,7 @@ public interface Config extends Cloneable, PlaceholderInjectable {
*
* @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.
* @return The found value, or null if not found.
*/
@Nullable
default String getFormattedStringOrNull(@NotNull String path,
@@ -308,6 +337,25 @@ public interface Config extends Cloneable, PlaceholderInjectable {
return getStringOrNull(path, true, option);
}
/**
* Get a formatted string from config.
*
* @param path The key to fetch the value from.
* @param context The placeholder context.
* @return The found value, or null if not found.
*/
@Nullable
default String getFormattedStringOrNull(@NotNull String path,
@NotNull PlaceholderContext context) {
String nullable = getStringOrNull(path);
if (nullable == null) {
return null;
}
return StringUtils.format(nullable, context.withInjectableContext(this));
}
/**
* Get a string from config.
* <p>
@@ -362,6 +410,24 @@ public interface Config extends Cloneable, PlaceholderInjectable {
return getStrings(path, true, option);
}
/**
* Get a list of strings from config.
* <p>
* Formatted.
*
* @param path The key to fetch the value from.
* @param context The placeholder context.
* @return The found value, or a blank {@link java.util.ArrayList} if not found.
*/
@NotNull
default List<String> getFormattedStrings(@NotNull String path,
@NotNull PlaceholderContext context) {
return Objects.requireNonNullElse(
getFormattedStringsOrNull(path, context),
new ArrayList<>()
);
}
/**
* Get a list of strings from config.
* <p>
@@ -418,6 +484,30 @@ public interface Config extends Cloneable, PlaceholderInjectable {
return getStringsOrNull(path, true, option);
}
/**
* Get a list of strings from config.
* <p>
* Formatted.
*
* @param path The key to fetch the value from.
* @param context The placeholder context.
* @return The found value, or null if not found.
*/
@Nullable
default List<String> getFormattedStringsOrNull(@NotNull String path,
@NotNull PlaceholderContext context) {
List<String> nullable = getStringsOrNull(path);
if (nullable == null) {
return null;
}
return StringUtils.formatList(
nullable,
context.withInjectableContext(this)
);
}
/**
* Get a list of strings from config.
* <p>
@@ -463,7 +553,7 @@ public interface Config extends Cloneable, PlaceholderInjectable {
* @return The computed value, or 0 if not found or invalid.
*/
default double getDoubleFromExpression(@NotNull String path) {
return getDoubleFromExpression(path, null);
return getDoubleFromExpression(path, PlaceholderContext.of(this));
}
/**
@@ -475,21 +565,38 @@ public interface Config extends Cloneable, PlaceholderInjectable {
*/
default double getDoubleFromExpression(@NotNull String path,
@Nullable Player player) {
return NumberUtils.evaluateExpression(this.getString(path), player, this);
return getDoubleFromExpression(path, player, Collections.emptyList());
}
/**
* Get a decimal value via a mathematical expression.
*
* @param path The key to fetch the value from.
* @param player The player to evaluate placeholders with respect to.
* @param path The key to fetch the value from.
* @param player The player to evaluate placeholders with respect to.
* @param additionalPlayers The additional players to evaluate placeholders with respect to.
* @return The computed value, or 0 if not found or invalid.
*/
default double getDoubleFromExpression(@NotNull String path,
@Nullable Player player,
@NotNull Collection<AdditionalPlayer> additionalPlayers) {
return NumberUtils.evaluateExpression(this.getString(path), player, this, additionalPlayers);
return getDoubleFromExpression(path, new PlaceholderContext(
player,
null,
this,
additionalPlayers
));
}
/**
* Get a decimal value via a mathematical expression.
*
* @param path The key to fetch the value from.
* @param context The placeholder context.
* @return The computed value, or 0 if not found or invalid.
*/
default double getDoubleFromExpression(@NotNull String path,
@NotNull PlaceholderContext context) {
return NumberUtils.evaluateExpression(this.getString(path), context.withInjectableContext(this));
}
/**

View File

@@ -3,17 +3,26 @@ package com.willfp.eco.core.data;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
/**
* A simple store key-value store for data to be stored outside of plugins.
*/
@SuppressWarnings("unchecked")
public final class ExternalDataStore {
/**
* The store.
*/
private static final HashMap<String, Object> data = new HashMap<>();
private static final Map<String, Object> DATA = new HashMap<>();
/**
* The store adapters.
*/
private static final List<ExternalDataStoreObjectAdapter<?, ?>> STORE_ADAPTERS = new ArrayList<>();
/**
* Put data into the store.
@@ -23,7 +32,29 @@ public final class ExternalDataStore {
*/
public static void put(@NotNull final String key,
@NotNull final Object value) {
data.put(key, value);
doPut(key, value);
}
/**
* Put data into the store.
*
* @param key The key.
* @param value The value.
* @param <A> The stored type.
*/
private static <A> void doPut(@NotNull final String key,
@NotNull final A value) {
Object storedValue = value;
for (ExternalDataStoreObjectAdapter<?, ?> unknownAdapter : STORE_ADAPTERS) {
if (unknownAdapter.getAccessedClass().isInstance(value)) {
ExternalDataStoreObjectAdapter<A, ?> adapter = (ExternalDataStoreObjectAdapter<A, ?>) unknownAdapter;
storedValue = adapter.toStoredObject(value);
break;
}
}
DATA.put(key, storedValue);
}
/**
@@ -37,7 +68,30 @@ public final class ExternalDataStore {
@Nullable
public static <T> T get(@NotNull final String key,
@NotNull final Class<T> clazz) {
Object value = data.get(key);
return doGet(key, clazz);
}
/**
* Get data from the store.
*
* @param key The key.
* @param clazz The class.
* @param <A> The accessed type.
* @param <S> The stored type.
* @return The value.
*/
@Nullable
private static <A, S> A doGet(@NotNull final String key,
@NotNull final Class<A> clazz) {
Object value = DATA.get(key);
for (ExternalDataStoreObjectAdapter<?, ?> unknownAdapter : STORE_ADAPTERS) {
if (unknownAdapter.getStoredClass().isInstance(value) && unknownAdapter.getAccessedClass().equals(clazz)) {
ExternalDataStoreObjectAdapter<A, S> adapter = (ExternalDataStoreObjectAdapter<A, S>) unknownAdapter;
value = adapter.toAccessedObject((S) value);
break;
}
}
if (clazz.isInstance(value)) {
return clazz.cast(value);
@@ -79,6 +133,15 @@ public final class ExternalDataStore {
return get(key, clazz, defaultValue.get());
}
/**
* Register a new adapter.
*
* @param adapter The adapter.
*/
public static void registerAdapter(@NotNull final ExternalDataStoreObjectAdapter<?, ?> adapter) {
STORE_ADAPTERS.add(adapter);
}
private ExternalDataStore() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}

View File

@@ -0,0 +1,69 @@
package com.willfp.eco.core.data;
import org.jetbrains.annotations.NotNull;
/**
* An adapter for objects stored in {@link ExternalDataStore}.
*
* @param <A> The accessed class.
* @param <S> The stored class.
*/
public abstract class ExternalDataStoreObjectAdapter<A, S> {
/**
* The class that is accessed (read / written).
*/
private final Class<? extends A> accessedClass;
/**
* The class that is stored internally.
*/
private final Class<? extends S> storedClass;
/**
* Create a new adapter.
*
* @param accessedClass The class that is accessed (read / written).
* @param storedClass The class that is stored internally.
*/
protected ExternalDataStoreObjectAdapter(@NotNull final Class<? extends A> accessedClass,
@NotNull final Class<? extends S> storedClass) {
this.accessedClass = accessedClass;
this.storedClass = storedClass;
}
/**
* Convert an object to the stored object.
*
* @param obj The object.
* @return The stored object.
*/
@NotNull
public abstract S toStoredObject(@NotNull final A obj);
/**
* Convert an object to the accessed object.
*
* @param obj The object.
* @return The accessed object.
*/
@NotNull
public abstract A toAccessedObject(@NotNull final S obj);
/**
* Get the class that is accessed (read / written).
*
* @return The class.
*/
public Class<? extends A> getAccessedClass() {
return accessedClass;
}
/**
* Get the class that is stored internally.
*
* @return The class.
*/
public Class<? extends S> getStoredClass() {
return storedClass;
}
}

View File

@@ -4,6 +4,7 @@ import com.willfp.eco.core.config.interfaces.Config;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -49,6 +50,11 @@ public final class PersistentDataKeyType<T> {
*/
public static final PersistentDataKeyType<Config> CONFIG = new PersistentDataKeyType<>("CONFIG");
/**
* Big Decimal.
*/
public static final PersistentDataKeyType<BigDecimal> BIG_DECIMAL = new PersistentDataKeyType<>("BIG_DECIMAL");
/**
* The name of the key type.
*/

View File

@@ -135,20 +135,26 @@ public abstract class Extension implements PluginLike {
}
@Override
public File getDataFolder() {
public @NotNull File getDataFolder() {
return this.plugin.getDataFolder();
}
@Override
public ConfigHandler getConfigHandler() {
public @NotNull ConfigHandler getConfigHandler() {
return this.plugin.getConfigHandler();
}
@Override
public Logger getLogger() {
public @NotNull Logger getLogger() {
return this.plugin.getLogger();
}
@Override
public @NotNull File getFile() {
Validate.notNull(metadata, "Metadata cannot be null!");
return this.metadata.file();
}
/**
* Get the plugin for the extension.
*

View File

@@ -0,0 +1,17 @@
package com.willfp.eco.core.extensions;
import org.jetbrains.annotations.NotNull;
/**
* Generic exception in extension loading.
*/
public class ExtensionLoadException extends RuntimeException {
/**
* Create a new ExtensionLoadException.
*
* @param errorMessage The error message to show.
*/
public ExtensionLoadException(@NotNull final String errorMessage) {
super(errorMessage);
}
}

View File

@@ -1,7 +1,10 @@
package com.willfp.eco.core.extensions;
import com.willfp.eco.core.version.Version;
import org.jetbrains.annotations.NotNull;
import java.io.File;
/**
* The extension's metadata.
* <p>
@@ -13,6 +16,23 @@ import org.jetbrains.annotations.NotNull;
*/
public record ExtensionMetadata(@NotNull String version,
@NotNull String name,
@NotNull String author) {
@NotNull String author,
@NotNull File file,
@NotNull Version minimumPluginVersion) {
/**
* Legacy constructor.
*
* @param version The extension version.
* @param name The extension name.
* @param author The extension's author.
* @deprecated Use {@link ExtensionMetadata#ExtensionMetadata(String, String, String, File, Version)} instead.
*/
@SuppressWarnings("ConstantConditions")
@Deprecated(since = "6.57.0", forRemoval = true)
public ExtensionMetadata(@NotNull String version,
@NotNull String name,
@NotNull String author) {
this(version, name, author, null, null);
throw new UnsupportedOperationException("Legacy constructor is not supported.");
}
}

View File

@@ -7,7 +7,7 @@ import org.jetbrains.annotations.NotNull;
* Missing or invalid extension.yml.
* Invalid filetype.
*/
public class MalformedExtensionException extends RuntimeException {
public class MalformedExtensionException extends ExtensionLoadException {
/**
* Create a new MalformedExtensionException.
*

View File

@@ -142,6 +142,26 @@ public interface MenuBuilder extends PageBuilder {
return this.onRender((player, menu) -> menu.setState(player, Page.MAX_PAGE_KEY, pages.apply(player)));
}
/**
* Set the default page.
*
* @param page The page.
* @return The builder.
*/
default MenuBuilder defaultPage(final int page) {
return this.maxPages(player -> page);
}
/**
* Set the default page dynamically for a player.
*
* @param page The default page.
* @return The builder.
*/
default MenuBuilder defaultPage(@NotNull final Function<Player, Integer> page) {
return this.onOpen((player, menu) -> menu.setState(player, Page.PAGE_KEY, page.apply(player)));
}
/**
* Add a menu close handler.
*

View File

@@ -76,6 +76,15 @@ public abstract class CustomSlot implements Slot {
return delegate;
}
@Override
public boolean shouldRenderOnClick() {
if (delegate == null) {
throw new IllegalStateException("Custom Slot was not initialized!");
}
return delegate.shouldRenderOnClick();
}
@Override
public final int getRows() {
return Slot.super.getRows();

View File

@@ -92,6 +92,15 @@ public interface Slot extends GUIComponent {
return false;
}
/**
* If the slot should re-render the menu if clicked.
*
* @return If the slot should re-render.
*/
default boolean shouldRenderOnClick() {
return true;
}
@Override
default int getRows() {
return 1;

View File

@@ -1,13 +1,22 @@
package com.willfp.eco.core.integrations;
import com.willfp.eco.core.registry.Registrable;
import com.willfp.eco.core.registry.Registry;
import org.jetbrains.annotations.NotNull;
/**
* Abstract class for integrations.
*/
public interface Integration {
public interface Integration extends Registrable {
/**
* Get the name of integration.
*
* @return The name.
*/
String getPluginName();
@Override
default @NotNull String getID() {
return Registry.tryFitPattern(this.getPluginName());
}
}

View File

@@ -0,0 +1,138 @@
package com.willfp.eco.core.integrations;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.registry.Registry;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashSet;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
/**
* Registry for integrations.
*
* @param <T> The type of integration.
*/
public class IntegrationRegistry<T extends Integration> extends Registry<T> {
/**
* Create a new integration registry.
*/
public IntegrationRegistry() {
super();
}
@Override
public @NotNull T register(@NotNull final T element) {
return executeSafely(() -> super.register(element), element);
}
/**
* Iterate over all integrations, safely.
*
* @param action The action to perform.
*/
public void forEachSafely(@NotNull final Consumer<T> action) {
for (T integration : new HashSet<>(this.values())) {
executeSafely(() -> action.accept(integration), integration);
}
}
/**
* If any integrations return true, safely.
*
* @param predicate The predicate to test.
* @return If any integrations return true.
*/
public boolean anySafely(@NotNull final Predicate<T> predicate) {
for (T integration : new HashSet<>(this.values())) {
Boolean result = executeSafely(() -> predicate.test(integration), integration);
if (result != null && result) {
return true;
}
}
return false;
}
/**
* Get the first integration that returns a value, safely.
*
* @param function The function to apply.
* @param defaultValue The default value.
* @param <R> The type of value.
* @return The first value that returns a value.
*/
@NotNull
public <R> R firstSafely(@NotNull final Function<T, R> function,
@NotNull final R defaultValue) {
if (this.isEmpty()) {
return defaultValue;
}
T integration = this.iterator().next();
return executeSafely(() -> function.apply(integration), integration, defaultValue);
}
/**
* Executes a given action safely, catching any exceptions and logging the issue.
*
* @param action The action to execute.
* @param integration The integration to apply the action on.
*/
private void executeSafely(@NotNull final Runnable action,
@NotNull final T integration) {
executeSafely(() -> {
action.run();
return null;
}, integration);
}
/**
* Executes a given action safely, catching any exceptions and logging the issue.
*
* @param action The action to execute.
* @param integration The integration to apply the action on.
* @param <R> The return type of the action.
* @return The result of the action, or null if an exception was thrown.
*/
private <R> R executeSafely(@NotNull final Supplier<R> action,
@NotNull final T integration) {
return executeSafely(action, integration, null);
}
/**
* Executes a given action safely, catching any exceptions and logging the issue.
*
* @param action The action to execute.
* @param integration The integration to apply the action on.
* @param defaultValue The default value to return if an exception is thrown.
* @param <R> The return type of the action.
* @return The result of the action, or the default value if an exception was thrown.
*/
private <R> R executeSafely(@NotNull final Supplier<R> action,
@NotNull final T integration,
@Nullable final R defaultValue) {
try {
return action.get();
} catch (final Exception e) {
Eco.get().getEcoPlugin().getLogger().warning("Integration for " + integration.getPluginName() + " threw an exception!");
Eco.get().getEcoPlugin().getLogger().warning("The integration will be disabled.");
e.printStackTrace();
this.remove(integration);
return defaultValue;
}
}
/**
* If all integrations return true, safely.
*
* @param predicate The predicate to test.
* @return If all integrations return true.
*/
public boolean allSafely(@NotNull final Predicate<T> predicate) {
return !this.anySafely(predicate.negate());
}
}

View File

@@ -1,11 +1,9 @@
package com.willfp.eco.core.integrations.afk;
import com.willfp.eco.core.integrations.IntegrationRegistry;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
import java.util.Set;
/**
* Class to handle afk integrations.
*/
@@ -13,7 +11,7 @@ public final class AFKManager {
/**
* A set of all registered integrations.
*/
private static final Set<AFKIntegration> REGISTERED = new HashSet<>();
private static final IntegrationRegistry<AFKIntegration> REGISTRY = new IntegrationRegistry<>();
/**
* Register a new integration.
@@ -21,8 +19,7 @@ public final class AFKManager {
* @param integration The integration to register.
*/
public static void register(@NotNull final AFKIntegration integration) {
REGISTERED.removeIf(it -> it.getPluginName().equalsIgnoreCase(integration.getPluginName()));
REGISTERED.add(integration);
REGISTRY.register(integration);
}
/**
@@ -32,13 +29,7 @@ public final class AFKManager {
* @return If afk.
*/
public static boolean isAfk(@NotNull final Player player) {
for (AFKIntegration integration : REGISTERED) {
if (integration.isAfk(player)) {
return true;
}
}
return false;
return REGISTRY.anySafely(integration -> integration.isAfk(player));
}
private AFKManager() {

View File

@@ -1,13 +1,11 @@
package com.willfp.eco.core.integrations.anticheat;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.integrations.IntegrationRegistry;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
import java.util.Set;
/**
* Class to handle anticheat integrations.
*/
@@ -15,7 +13,7 @@ public final class AnticheatManager {
/**
* A set of all registered anticheats.
*/
private static final Set<AnticheatIntegration> ANTICHEATS = new HashSet<>();
private static final IntegrationRegistry<AnticheatIntegration> REGISTRY = new IntegrationRegistry<>();
/**
* Register a new anticheat.
@@ -26,8 +24,7 @@ public final class AnticheatManager {
if (anticheat instanceof Listener) {
Eco.get().getEcoPlugin().getEventManager().registerListener((Listener) anticheat);
}
ANTICHEATS.removeIf(it -> it.getPluginName().equalsIgnoreCase(anticheat.getPluginName()));
ANTICHEATS.add(anticheat);
REGISTRY.register(anticheat);
}
/**
@@ -36,17 +33,16 @@ public final class AnticheatManager {
* @param player The player to exempt.
*/
public static void exemptPlayer(@NotNull final Player player) {
ANTICHEATS.forEach(anticheat -> anticheat.exempt(player));
REGISTRY.forEachSafely(anticheat -> anticheat.exempt(player));
}
/**
* Unexempt a player from triggering anticheats.
* This is ran a tick after it is called to ensure that there are no event timing conflicts.
*
* @param player The player to remove the exemption.
*/
public static void unexemptPlayer(@NotNull final Player player) {
ANTICHEATS.forEach(anticheat -> anticheat.unexempt(player));
REGISTRY.forEachSafely(anticheat -> anticheat.unexempt(player));
}
private AnticheatManager() {

View File

@@ -1,5 +1,6 @@
package com.willfp.eco.core.integrations.antigrief;
import com.willfp.eco.core.integrations.IntegrationRegistry;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.entity.LivingEntity;
@@ -16,7 +17,7 @@ public final class AntigriefManager {
/**
* Registered antigriefs.
*/
private static final Set<AntigriefIntegration> REGISTERED = new HashSet<>();
private static final IntegrationRegistry<AntigriefIntegration> REGISTRY = new IntegrationRegistry<>();
/**
* Register a new AntiGrief/Land Management integration.
@@ -24,8 +25,7 @@ public final class AntigriefManager {
* @param antigrief The integration to register.
*/
public static void register(@NotNull final AntigriefIntegration antigrief) {
REGISTERED.removeIf(it -> it.getPluginName().equalsIgnoreCase(antigrief.getPluginName()));
REGISTERED.add(antigrief);
REGISTRY.register(antigrief);
}
/**
@@ -34,8 +34,7 @@ public final class AntigriefManager {
* @param antigrief The integration to unregister.
*/
public static void unregister(@NotNull final AntigriefIntegration antigrief) {
REGISTERED.removeIf(it -> it.getPluginName().equalsIgnoreCase(antigrief.getPluginName()));
REGISTERED.remove(antigrief);
REGISTRY.remove(antigrief);
}
/**
@@ -47,7 +46,7 @@ public final class AntigriefManager {
*/
public static boolean canPickupItem(@NotNull final Player player,
@NotNull final Location location) {
return REGISTERED.stream().allMatch(antigriefIntegration -> antigriefIntegration.canPickupItem(player, location));
return REGISTRY.allSafely(integration -> integration.canPickupItem(player, location));
}
/**
@@ -59,7 +58,7 @@ public final class AntigriefManager {
*/
public static boolean canBreakBlock(@NotNull final Player player,
@NotNull final Block block) {
return REGISTERED.stream().allMatch(antigriefIntegration -> antigriefIntegration.canBreakBlock(player, block));
return REGISTRY.allSafely(integration -> integration.canBreakBlock(player, block));
}
/**
@@ -71,7 +70,7 @@ public final class AntigriefManager {
*/
public static boolean canCreateExplosion(@NotNull final Player player,
@NotNull final Location location) {
return REGISTERED.stream().allMatch(antigriefIntegration -> antigriefIntegration.canCreateExplosion(player, location));
return REGISTRY.allSafely(integration -> integration.canCreateExplosion(player, location));
}
/**
@@ -83,7 +82,7 @@ public final class AntigriefManager {
*/
public static boolean canPlaceBlock(@NotNull final Player player,
@NotNull final Block block) {
return REGISTERED.stream().allMatch(antigriefIntegration -> antigriefIntegration.canPlaceBlock(player, block));
return REGISTRY.allSafely(integration -> integration.canPlaceBlock(player, block));
}
/**
@@ -95,7 +94,7 @@ public final class AntigriefManager {
*/
public static boolean canInjure(@NotNull final Player player,
@NotNull final LivingEntity victim) {
return REGISTERED.stream().allMatch(antigriefIntegration -> antigriefIntegration.canInjure(player, victim));
return REGISTRY.allSafely(integration -> integration.canInjure(player, victim));
}
private AntigriefManager() {

View File

@@ -1,10 +1,8 @@
package com.willfp.eco.core.integrations.customentities;
import com.willfp.eco.core.integrations.IntegrationRegistry;
import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
import java.util.Set;
/**
* Class to handle custom entity integrations.
*/
@@ -12,7 +10,7 @@ public final class CustomEntitiesManager {
/**
* A set of all registered integrations.
*/
private static final Set<CustomEntitiesIntegration> REGISTERED = new HashSet<>();
private static final IntegrationRegistry<CustomEntitiesIntegration> REGISTRY = new IntegrationRegistry<>();
/**
* Register a new integration.
@@ -20,8 +18,7 @@ public final class CustomEntitiesManager {
* @param integration The integration to register.
*/
public static void register(@NotNull final CustomEntitiesIntegration integration) {
REGISTERED.removeIf(it -> it.getPluginName().equalsIgnoreCase(integration.getPluginName()));
REGISTERED.add(integration);
REGISTRY.register(integration);
}
/**
@@ -30,9 +27,7 @@ public final class CustomEntitiesManager {
* @see com.willfp.eco.core.entities.Entities
*/
public static void registerAllEntities() {
for (CustomEntitiesIntegration integration : REGISTERED) {
integration.registerAllEntities();
}
REGISTRY.forEachSafely(CustomEntitiesIntegration::registerAllEntities);
}
private CustomEntitiesManager() {

View File

@@ -1,5 +1,6 @@
package com.willfp.eco.core.integrations.customitems;
import com.willfp.eco.core.integrations.IntegrationRegistry;
import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
@@ -12,7 +13,7 @@ public final class CustomItemsManager {
/**
* A set of all registered integrations.
*/
private static final Set<CustomItemsIntegration> REGISTERED = new HashSet<>();
private static final IntegrationRegistry<CustomItemsIntegration> REGISTRY = new IntegrationRegistry<>();
/**
* Register a new integration.
@@ -20,8 +21,7 @@ public final class CustomItemsManager {
* @param integration The integration to register.
*/
public static void register(@NotNull final CustomItemsIntegration integration) {
REGISTERED.removeIf(it -> it.getPluginName().equalsIgnoreCase(integration.getPluginName()));
REGISTERED.add(integration);
REGISTRY.register(integration);
}
/**
@@ -30,9 +30,7 @@ public final class CustomItemsManager {
* @see com.willfp.eco.core.items.Items
*/
public static void registerAllItems() {
for (CustomItemsIntegration customItemsIntegration : REGISTERED) {
customItemsIntegration.registerAllItems();
}
REGISTRY.forEachSafely(CustomItemsIntegration::registerAllItems);
}
/**
@@ -41,9 +39,7 @@ public final class CustomItemsManager {
* @see com.willfp.eco.core.items.Items
*/
public static void registerProviders() {
for (CustomItemsIntegration customItemsIntegration : REGISTERED) {
customItemsIntegration.registerProvider();
}
REGISTRY.forEachSafely(CustomItemsIntegration::registerProvider);
}
private CustomItemsManager() {

View File

@@ -1,11 +1,10 @@
package com.willfp.eco.core.integrations.economy;
import com.willfp.eco.core.integrations.IntegrationRegistry;
import org.bukkit.OfflinePlayer;
import org.jetbrains.annotations.NotNull;
import java.math.BigDecimal;
import java.util.HashSet;
import java.util.Set;
/**
* Class to handle economy.
@@ -14,7 +13,7 @@ public final class EconomyManager {
/**
* A set of all registered integrations.
*/
private static final Set<EconomyIntegration> REGISTERED = new HashSet<>();
private static final IntegrationRegistry<EconomyIntegration> REGISTRY = new IntegrationRegistry<>();
/**
* Register a new integration.
@@ -22,8 +21,7 @@ public final class EconomyManager {
* @param integration The integration to register.
*/
public static void register(@NotNull final EconomyIntegration integration) {
REGISTERED.removeIf(it -> it.getPluginName().equalsIgnoreCase(integration.getPluginName()));
REGISTERED.add(integration);
REGISTRY.register(integration);
}
/**
@@ -32,7 +30,7 @@ public final class EconomyManager {
* @return If any economy.
*/
public static boolean hasRegistrations() {
return !REGISTERED.isEmpty();
return REGISTRY.isNotEmpty();
}
/**
@@ -56,11 +54,10 @@ public final class EconomyManager {
*/
public static boolean hasAmount(@NotNull final OfflinePlayer player,
final BigDecimal amount) {
for (EconomyIntegration integration : REGISTERED) {
return integration.hasAmount(player, amount);
}
return false;
return REGISTRY.firstSafely(
integration -> integration.hasAmount(player, amount),
false
);
}
/**
@@ -84,11 +81,10 @@ public final class EconomyManager {
*/
public static boolean giveMoney(@NotNull final OfflinePlayer player,
@NotNull final BigDecimal amount) {
for (EconomyIntegration integration : REGISTERED) {
return integration.giveMoney(player, amount);
}
return false;
return REGISTRY.firstSafely(
integration -> integration.giveMoney(player, amount),
false
);
}
/**
@@ -112,11 +108,10 @@ public final class EconomyManager {
*/
public static boolean removeMoney(@NotNull final OfflinePlayer player,
@NotNull final BigDecimal amount) {
for (EconomyIntegration integration : REGISTERED) {
return integration.removeMoney(player, amount);
}
return false;
return REGISTRY.firstSafely(
integration -> integration.removeMoney(player, amount),
false
);
}
/**
@@ -136,11 +131,10 @@ public final class EconomyManager {
* @return The balance.
*/
public static BigDecimal getExactBalance(@NotNull final OfflinePlayer player) {
for (EconomyIntegration integration : REGISTERED) {
return integration.getExactBalance(player);
}
return BigDecimal.ZERO;
return REGISTRY.firstSafely(
integration -> integration.getExactBalance(player),
BigDecimal.ZERO
);
}
private EconomyManager() {

View File

@@ -1,12 +1,10 @@
package com.willfp.eco.core.integrations.guidetection;
import com.willfp.eco.core.integrations.IntegrationRegistry;
import com.willfp.eco.util.MenuUtils;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
import java.util.Set;
/**
* Class to handle GUI detection.
*/
@@ -14,7 +12,7 @@ public final class GUIDetectionManager {
/**
* A set of all registered integrations.
*/
private static final Set<GUIDetectionIntegration> REGISTERED = new HashSet<>();
private static final IntegrationRegistry<GUIDetectionIntegration> REGISTRY = new IntegrationRegistry<>();
/**
* Register a new integration.
@@ -22,8 +20,7 @@ public final class GUIDetectionManager {
* @param integration The integration to register.
*/
public static void register(@NotNull final GUIDetectionIntegration integration) {
REGISTERED.removeIf(it -> it.getPluginName().equalsIgnoreCase(integration.getPluginName()));
REGISTERED.add(integration);
REGISTRY.register(integration);
}
/**
@@ -37,13 +34,7 @@ public final class GUIDetectionManager {
return true;
}
for (GUIDetectionIntegration integration : REGISTERED) {
if (integration.hasGUIOpen(player)) {
return true;
}
}
return false;
return REGISTRY.anySafely(integration -> integration.hasGUIOpen(player));
}
private GUIDetectionManager() {

View File

@@ -1,11 +1,10 @@
package com.willfp.eco.core.integrations.hologram;
import com.willfp.eco.core.integrations.IntegrationRegistry;
import org.bukkit.Location;
import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Class to handle hologram integrations.
@@ -14,7 +13,7 @@ public final class HologramManager {
/**
* A set of all registered integrations.
*/
private static final Set<HologramIntegration> REGISTERED = new HashSet<>();
private static final IntegrationRegistry<HologramIntegration> REGISTRY = new IntegrationRegistry<>();
/**
* Register a new integration.
@@ -22,8 +21,7 @@ public final class HologramManager {
* @param integration The integration to register.
*/
public static void register(@NotNull final HologramIntegration integration) {
REGISTERED.removeIf(it -> it.getPluginName().equalsIgnoreCase(integration.getPluginName()));
REGISTERED.add(integration);
REGISTRY.register(integration);
}
/**
@@ -35,11 +33,10 @@ public final class HologramManager {
*/
public static Hologram createHologram(@NotNull final Location location,
@NotNull final List<String> contents) {
for (HologramIntegration integration : REGISTERED) {
return integration.createHologram(location, contents);
}
return new DummyHologram();
return REGISTRY.firstSafely(
integration -> integration.createHologram(location, contents),
new DummyHologram()
);
}
private HologramManager() {

View File

@@ -1,5 +1,6 @@
package com.willfp.eco.core.integrations.mcmmo;
import com.willfp.eco.core.integrations.IntegrationRegistry;
import org.bukkit.block.Block;
import org.bukkit.event.Event;
import org.jetbrains.annotations.NotNull;
@@ -14,7 +15,7 @@ public final class McmmoManager {
/**
* A set of all registered integrations.
*/
private static final Set<McmmoIntegration> REGISTERED = new HashSet<>();
private static final IntegrationRegistry<McmmoIntegration> REGISTERED = new IntegrationRegistry<>();
/**
* Register a new integration.
@@ -22,8 +23,7 @@ public final class McmmoManager {
* @param integration The integration to register.
*/
public static void register(@NotNull final McmmoIntegration integration) {
REGISTERED.removeIf(it -> it.getPluginName().equalsIgnoreCase(integration.getPluginName()));
REGISTERED.add(integration);
REGISTERED.register(integration);
}
/**
@@ -34,13 +34,11 @@ public final class McmmoManager {
*/
public static int getBonusDropCount(@NotNull final Block block) {
int finalValue = 0;
for (McmmoIntegration mcmmoIntegration : REGISTERED) {
finalValue += mcmmoIntegration.getBonusDropCount(block);
}
return finalValue;
}
@@ -51,13 +49,7 @@ public final class McmmoManager {
* @return If the event is fake.
*/
public static boolean isFake(@NotNull final Event event) {
for (McmmoIntegration mcmmoIntegration : REGISTERED) {
if (mcmmoIntegration.isFake(event)) {
return true;
}
}
return false;
return REGISTERED.anySafely(integration -> integration.isFake(event));
}
private McmmoManager() {

View File

@@ -9,7 +9,7 @@ import java.util.ArrayList;
import java.util.List;
/**
* Wrapper class for placeholder integrations.
* Wrapper class for arguments integrations.
*/
public interface PlaceholderIntegration extends Integration {
/**

View File

@@ -1,21 +1,15 @@
package com.willfp.eco.core.integrations.placeholder;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.google.common.collect.ImmutableSet;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.map.DefaultMap;
import com.willfp.eco.core.placeholder.AdditionalPlayer;
import com.willfp.eco.core.placeholder.DynamicPlaceholder;
import com.willfp.eco.core.placeholder.InjectablePlaceholder;
import com.willfp.eco.core.placeholder.Placeholder;
import com.willfp.eco.core.placeholder.PlaceholderInjectable;
import com.willfp.eco.core.placeholder.PlayerDynamicPlaceholder;
import com.willfp.eco.core.placeholder.PlayerPlaceholder;
import com.willfp.eco.core.placeholder.PlayerStaticPlaceholder;
import com.willfp.eco.core.placeholder.PlayerlessPlaceholder;
import com.willfp.eco.core.placeholder.StaticPlaceholder;
import com.willfp.eco.util.StringUtils;
import org.apache.commons.lang.Validate;
import com.willfp.eco.core.placeholder.RegistrablePlaceholder;
import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -23,58 +17,34 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Class to handle placeholder integrations.
* Class to handle arguments integrations.
*/
public final class PlaceholderManager {
/**
* All registered placeholders.
*/
private static final Map<EcoPlugin, Map<Pattern, Placeholder>> REGISTERED_PLACEHOLDERS = new HashMap<>();
private static final DefaultMap<EcoPlugin, Set<Placeholder>> REGISTERED_PLACEHOLDERS = new DefaultMap<>(HashSet::new);
/**
* All registered placeholder integrations.
* All registered arguments integrations.
*/
private static final Set<PlaceholderIntegration> REGISTERED_INTEGRATIONS = new HashSet<>();
/**
* Placeholder Lookup Cache.
*/
private static final Cache<PlaceholderLookup, Optional<Placeholder>> PLACEHOLDER_LOOKUP_CACHE = Caffeine.newBuilder()
.expireAfterWrite(1, TimeUnit.SECONDS)
.build();
/**
* Placeholder Cache.
*/
private static final LoadingCache<EntryWithPlayer, String> PLACEHOLDER_CACHE = Caffeine.newBuilder()
.expireAfterWrite(50, TimeUnit.MILLISECONDS)
.build(key -> key.entry.getValue(key.player));
/**
* Dynamic Placeholder Cache.
*/
private static final LoadingCache<DynamicEntryWithPlayer, String> DYNAMIC_PLACEHOLDER_CACHE = Caffeine.newBuilder()
.expireAfterWrite(50, TimeUnit.MILLISECONDS)
.build(key -> key.entry.getValue(key.args, key.player));
/**
* The default PlaceholderAPI pattern; brought in for compatibility.
*/
private static final Pattern PATTERN = Pattern.compile("%([^% ]+)%");
/**
* Empty injectable object.
* Empty injectableContext object.
*/
public static final PlaceholderInjectable EMPTY_INJECTABLE = new PlaceholderInjectable() {
@Override
@@ -105,97 +75,68 @@ public final class PlaceholderManager {
}
/**
* Register a placeholder.
* Register a arguments.
*
* @param placeholder The placeholder to register.
* @param placeholder The arguments to register.
* @deprecated Use {@link #registerPlaceholder(RegistrablePlaceholder)} instead.
*/
@Deprecated(since = "6.56.0", forRemoval = true)
public static void registerPlaceholder(@NotNull final Placeholder placeholder) {
if (placeholder instanceof StaticPlaceholder || placeholder instanceof PlayerStaticPlaceholder) {
throw new IllegalArgumentException("Static placeholders cannot be registered!");
if (!(placeholder instanceof RegistrablePlaceholder)) {
throw new IllegalArgumentException("Placeholder must be RegistrablePlaceholder!");
}
Map<Pattern, Placeholder> pluginPlaceholders = REGISTERED_PLACEHOLDERS
.getOrDefault(placeholder.getPlugin(), new HashMap<>());
pluginPlaceholders.put(placeholder.getPattern(), placeholder);
REGISTERED_PLACEHOLDERS.put(placeholder.getPlugin(), pluginPlaceholders);
registerPlaceholder((RegistrablePlaceholder) placeholder);
}
/**
* Get the result of a placeholder with respect to a player.
* Register a arguments.
*
* @param player The player to get the result from.
* @param identifier The placeholder identifier.
* @return The value of the placeholder.
* @deprecated Specify a plugin to get the result from.
* @param placeholder The arguments to register.
*/
@Deprecated(since = "6.52.2", forRemoval = true)
@SuppressWarnings("unused")
public static String getResult(@Nullable final Player player,
@NotNull final String identifier) {
throw new UnsupportedOperationException("Please specify a plugin to get the result from!");
public static void registerPlaceholder(@NotNull final RegistrablePlaceholder placeholder) {
// Storing as immutable set leads to slower times to register placeholders, but much
// faster times to access registrations.
Set<Placeholder> pluginPlaceholders = new HashSet<>(REGISTERED_PLACEHOLDERS.get(placeholder.getPlugin()));
pluginPlaceholders.add(placeholder);
REGISTERED_PLACEHOLDERS.put(placeholder.getPlugin(), ImmutableSet.copyOf(pluginPlaceholders));
}
/**
* Get the result of a placeholder with respect to a player.
*
* @param player The player to get the result from.
* @param identifier The placeholder identifier.
* @param plugin The plugin for the placeholder.
* @return The value of the placeholder.
* @param identifier The placeholder args.
* @param plugin The plugin for the arguments.
* @return The value of the arguments.
*/
@NotNull
public static String getResult(@Nullable final Player player,
@NotNull final String identifier,
@NotNull final EcoPlugin plugin) {
Validate.notNull(plugin, "Plugin cannot be null!");
@Nullable final EcoPlugin plugin) {
return Objects.requireNonNullElse(
getResult(
plugin,
identifier,
new PlaceholderContext(player)
),
""
);
}
// This is really janky, and it sucks, but it works so?
// Compensating for regex being slow so that's why we get it.
Placeholder placeholder = PLACEHOLDER_LOOKUP_CACHE.get(
new PlaceholderLookup(identifier, plugin),
(it) -> {
// I hate the streams API.
return REGISTERED_PLACEHOLDERS
.getOrDefault(plugin, new HashMap<>())
.entrySet()
.stream().filter(entry -> entry.getKey().matcher(identifier).matches())
.map(Map.Entry::getValue)
.findFirst();
}
).orElse(null);
if (placeholder == null) {
return "";
}
/*
This code here is *really* not very good. It's mega externalized logic hacked
together and made worse by the addition of dynamic placeholders. But it works,
and it means I don't have to rewrite the whole placeholder system. So it's
good enough for me.
*/
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 if (placeholder instanceof PlayerDynamicPlaceholder playerDynamicPlaceholder) {
if (player == null) {
return "";
} else {
return DYNAMIC_PLACEHOLDER_CACHE.get(new DynamicEntryWithPlayer(playerDynamicPlaceholder, identifier, player));
}
} else if (placeholder instanceof DynamicPlaceholder dynamicPlaceholder) {
return dynamicPlaceholder.getValue(identifier);
} else {
return "";
}
/**
* Get the result of a placeholder given a plugin and arguments.
*
* @param plugin The plugin for the placeholder.
* @param args The arguments.
* @param context The context.
* @return The value of the arguments.
*/
@Nullable
public static String getResult(@Nullable final EcoPlugin plugin,
@NotNull final String args,
@NotNull final PlaceholderContext context) {
return Eco.get().getPlaceholderValue(plugin, args, context);
}
/**
@@ -204,7 +145,10 @@ public final class PlaceholderManager {
* @param text The text that may contain placeholders to translate.
* @param player The player to translate the placeholders with respect to.
* @return The text, translated.
* @deprecated Use {@link #translatePlaceholders(String, PlaceholderContext)} instead.
*/
@Deprecated(since = "6.56.0", forRemoval = true)
@NotNull
public static String translatePlaceholders(@NotNull final String text,
@Nullable final Player player) {
return translatePlaceholders(text, player, EMPTY_INJECTABLE);
@@ -215,9 +159,12 @@ public final class PlaceholderManager {
*
* @param text The text that may contain placeholders to translate.
* @param player The player to translate the placeholders with respect to.
* @param context The injectable context.
* @param context The injectableContext parseContext.
* @return The text, translated.
* @deprecated Use {@link #translatePlaceholders(String, PlaceholderContext)} instead.
*/
@Deprecated(since = "6.56.0", forRemoval = true)
@NotNull
public static String translatePlaceholders(@NotNull final String text,
@Nullable final Player player,
@NotNull final PlaceholderInjectable context) {
@@ -229,84 +176,50 @@ public final class PlaceholderManager {
*
* @param text The text that may contain placeholders to translate.
* @param player The player to translate the placeholders with respect to.
* @param context The injectable context.
* @param context The injectableContext parseContext.
* @param additionalPlayers Additional players to translate placeholders for.
* @return The text, translated.
* @deprecated Use {@link #translatePlaceholders(String, PlaceholderContext)} instead.
*/
@Deprecated(since = "6.56.0", forRemoval = true)
@NotNull
public static String translatePlaceholders(@NotNull final String text,
@Nullable final Player player,
@NotNull final PlaceholderInjectable context,
@NotNull final Collection<AdditionalPlayer> additionalPlayers) {
String processed = text;
return translatePlaceholders(
text,
new PlaceholderContext(
player,
null,
context,
additionalPlayers
)
);
}
/*
/**
* Translate all placeholders without a placeholder context.
*
* @param text The text that may contain placeholders to translate.
* @return The text, translated.
*/
@NotNull
public static String translatePlaceholders(@NotNull final String text) {
return Eco.get().translatePlaceholders(text, PlaceholderContext.EMPTY);
}
Why am I doing statics at the start, but player statics at the end?
Additional players let you use something like victim as a player to parse in relation to,
for example doing %victim_player_health%, which would parse the health of the victim.
However, something like libreforge will also inject %victim_max_health%, which is unrelated
to additional players, and instead holds a constant value. So, eco saw this, smartly thought
"ah, it's an additional player, let's parse it", and then tried to parse %max_health% with
relation to the victim, which resolved to zero. So, we have to parse statics and player statics
that might include a prefix first, then additional players, then player statics with the support
of additional players.
This was a massive headache and took so many reports before I clocked what was going on.
Oh well, at least it's fixed now.
*/
for (InjectablePlaceholder injection : context.getPlaceholderInjections()) {
if (injection instanceof StaticPlaceholder placeholder) {
processed = processed.replace("%" + placeholder.getIdentifier() + "%", placeholder.getValue());
} else if (injection instanceof PlayerStaticPlaceholder placeholder && player != null) {
processed = processed.replace("%" + placeholder.getIdentifier() + "%", placeholder.getValue(player));
}
}
// Prevent running 2 scans if there are no additional players.
if (!additionalPlayers.isEmpty()) {
List<String> found = findPlaceholdersIn(text);
for (AdditionalPlayer additionalPlayer : additionalPlayers) {
for (String placeholder : found) {
String prefix = "%" + additionalPlayer.getIdentifier() + "_";
if (placeholder.startsWith(prefix)) {
processed = processed.replace(
placeholder,
translatePlaceholders(
"%" + StringUtils.removePrefix(prefix, placeholder),
additionalPlayer.getPlayer()
)
);
}
}
}
}
// Only run jank code if there are no integrations.
if (REGISTERED_INTEGRATIONS.isEmpty()) {
processed = setWithoutIntegration(processed, player);
}
for (PlaceholderIntegration integration : REGISTERED_INTEGRATIONS) {
processed = integration.translate(processed, player);
}
// DON'T REMOVE THIS, IT'S NOT DUPLICATE CODE.
for (InjectablePlaceholder injection : context.getPlaceholderInjections()) {
// Do I know this is a bad way of doing this? Yes.
if (injection instanceof PlayerStaticPlaceholder placeholder && player != null) {
processed = processed.replace("%" + placeholder.getIdentifier() + "%", placeholder.getValue(player));
}
}
return processed;
/**
* Translate all placeholders in a translation context.
*
* @param text The text that may contain placeholders to translate.
* @param context The translation context.
* @return The text, translated.
*/
@NotNull
public static String translatePlaceholders(@NotNull final String text,
@NotNull final PlaceholderContext context) {
return Eco.get().translatePlaceholders(text, context);
}
/**
@@ -331,108 +244,22 @@ public final class PlaceholderManager {
}
/**
* Set placeholders without any integrations.
* <p>
* This is fallback if for some reason you don't have PAPI installed.
* It's a cut-down version of the actual PAPI code, and I don't
* really know how it works.
* <p>
* Original source
* <a href="https://github.com/PlaceholderAPI/PlaceholderAPI/blob/master/src/main/java/me/clip/placeholderapi/replacer/CharsReplacer.java">here</a>.
* Get all registered placeholder integrations.
*
* @param text The text.
* @param player The player.
* @return The text.
* @return The integrations.
*/
private static String setWithoutIntegration(@NotNull final String text,
@Nullable final Player player) {
char[] chars = text.toCharArray();
StringBuilder builder = new StringBuilder(text.length());
StringBuilder identifier = new StringBuilder();
StringBuilder parameters = new StringBuilder();
for (int i = 0; i < chars.length; i++) {
char currentChar = chars[i];
if (currentChar == '%' && i + 1 < chars.length) {
boolean identified = false;
boolean badPlaceholder = true;
boolean hadSpace = false;
while (true) {
i++;
if (i >= chars.length) {
break;
}
char p = chars[i];
if (p == ' ' && !identified) {
hadSpace = true;
break;
}
if (p == '%') {
badPlaceholder = false;
break;
}
if (p == '_' && !identified) {
identified = true;
} else if (identified) {
parameters.append(p);
} else {
identifier.append(p);
}
}
String pluginName = identifier.toString().toLowerCase();
EcoPlugin plugin = EcoPlugin.getPlugin(pluginName);
String placeholderIdentifier = parameters.toString();
identifier.setLength(0);
parameters.setLength(0);
if (badPlaceholder) {
builder.append('%').append(pluginName);
if (identified) {
builder.append('_').append(placeholderIdentifier);
}
if (hadSpace) {
builder.append(' ');
}
} else {
if (plugin == null) {
builder.append('%').append(pluginName);
if (identified) {
builder.append('_');
}
builder.append(placeholderIdentifier).append('%');
} else {
builder.append(getResult(player, placeholderIdentifier, plugin));
}
}
} else {
builder.append(currentChar);
}
}
return builder.toString();
public static Set<PlaceholderIntegration> getRegisteredIntegrations() {
return Set.copyOf(REGISTERED_INTEGRATIONS);
}
private record PlaceholderLookup(@NotNull String identifier,
@Nullable EcoPlugin plugin) {
}
private record EntryWithPlayer(@NotNull PlayerPlaceholder entry,
@NotNull Player player) {
}
private record DynamicEntryWithPlayer(@NotNull PlayerDynamicPlaceholder entry,
@NotNull String args,
@NotNull Player player) {
/**
* Get all registered placeholders for a plugin.
*
* @param plugin The plugin.
* @return The placeholders.
*/
public static Set<Placeholder> getRegisteredPlaceholders(@NotNull final EcoPlugin plugin) {
return REGISTERED_PLACEHOLDERS.get(plugin);
}
private PlaceholderManager() {

View File

@@ -1,5 +1,6 @@
package com.willfp.eco.core.integrations.shop;
import com.willfp.eco.core.integrations.IntegrationRegistry;
import com.willfp.eco.core.price.Price;
import com.willfp.eco.core.price.impl.PriceFree;
import org.bukkit.entity.Player;
@@ -13,11 +14,12 @@ import java.util.Set;
/**
* Class to handle shop integrations.
*/
@SuppressWarnings("DeprecatedIsStillUsed")
public final class ShopManager {
/**
* A set of all registered integrations.
*/
private static final Set<ShopIntegration> REGISTERED = new HashSet<>();
private static final IntegrationRegistry<ShopIntegration> REGISTRY = new IntegrationRegistry<>();
/**
* Register a new integration.
@@ -25,17 +27,14 @@ public final class ShopManager {
* @param integration The integration to register.
*/
public static void register(@NotNull final ShopIntegration integration) {
REGISTERED.removeIf(it -> it.getPluginName().equalsIgnoreCase(integration.getPluginName()));
REGISTERED.add(integration);
REGISTRY.register(integration);
}
/**
* Register eco item provider for shop plugins.
*/
public static void registerEcoProvider() {
for (ShopIntegration shopIntegration : REGISTERED) {
shopIntegration.registerEcoProvider();
}
REGISTRY.forEachSafely(ShopIntegration::registerEcoProvider);
}
/**
@@ -51,11 +50,7 @@ public final class ShopManager {
return false;
}
for (ShopIntegration integration : REGISTERED) {
return integration.isSellable(itemStack, player);
}
return false;
return REGISTRY.anySafely(integration -> integration.isSellable(itemStack, player));
}
/**
@@ -74,11 +69,10 @@ public final class ShopManager {
return new PriceFree();
}
for (ShopIntegration integration : REGISTERED) {
return integration.getUnitValue(itemStack, player);
}
return new PriceFree();
return REGISTRY.firstSafely(
integration -> integration.getUnitValue(itemStack, player),
new PriceFree()
);
}
/**
@@ -108,11 +102,10 @@ public final class ShopManager {
return 0.0;
}
for (ShopIntegration shopIntegration : REGISTERED) {
return shopIntegration.getUnitValue(itemStack, player).getValue(player, itemStack.getAmount());
}
return 0.0;
return REGISTRY.firstSafely(
integration -> integration.getUnitValue(itemStack, player).getValue(player, itemStack.getAmount()),
0.0
);
}
/**
@@ -121,7 +114,7 @@ public final class ShopManager {
* @return The integrations.
*/
public static Set<ShopIntegration> getRegisteredIntegrations() {
return new HashSet<>(REGISTERED);
return new HashSet<>(REGISTRY.values());
}
private ShopManager() {

View File

@@ -1,7 +1,6 @@
package com.willfp.eco.core.items;
import com.willfp.eco.core.Eco;
import org.bukkit.Bukkit;
import org.bukkit.NamespacedKey;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
@@ -55,7 +54,7 @@ public class CustomItem implements TestableItem {
*/
Eco.get().getEcoPlugin().getScheduler().runLater(() -> {
if (!matches(getItem())) {
Bukkit.getLogger().severe("Item with key " + key + " is invalid!");
Eco.get().getEcoPlugin().getLogger().severe("Item with key " + key + " is invalid!");
}
}, 1);
}

View File

@@ -150,7 +150,7 @@ public class DefaultMap<K, V> implements Map<K, V> {
*/
@NotNull
public static <K, K1, V> DefaultMap<K, Map<K1, V>> createNestedMap() {
return new DefaultMap<>(new HashMap<>());
return new DefaultMap<>(HashMap::new);
}
/**
@@ -163,6 +163,6 @@ public class DefaultMap<K, V> implements Map<K, V> {
*/
@NotNull
public static <K, K1, V> DefaultMap<K, ListMap<K1, V>> createNestedListMap() {
return new DefaultMap<>(new ListMap<>());
return new DefaultMap<>(ListMap::new);
}
}

View File

@@ -3,27 +3,25 @@ package com.willfp.eco.core.math;
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager;
import com.willfp.eco.core.placeholder.AdditionalPlayer;
import com.willfp.eco.core.placeholder.PlaceholderInjectable;
import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
/**
* Represents a context to do math in.
* Represents a context to parse math in.
*
* @param injectableContext The PlaceholderInjectable context.
* @param player The player.
* @param additionalPlayers The additional players.
* @deprecated Use {@link PlaceholderContext} instead.
*/
public record MathContext(
@NotNull PlaceholderInjectable injectableContext,
@Nullable Player player,
@NotNull Collection<AdditionalPlayer> additionalPlayers
) {
@SuppressWarnings("DeprecatedIsStillUsed")
@Deprecated(since = "6.56.0", forRemoval = true)
public class MathContext {
/**
* Empty math context.
* Returns an empty math parseContext.
*/
public static final MathContext EMPTY = new MathContext(
PlaceholderManager.EMPTY_INJECTABLE,
@@ -32,9 +30,157 @@ public record MathContext(
);
/**
* Create MathContext of a PlaceholderInjectable context.
* The PlaceholderInjectable parse context.
*/
@NotNull
private final PlaceholderInjectable injectableContext;
/**
* The player.
*/
@Nullable
private final Player player;
/**
* The additional players.
*/
@NotNull
private final Collection<AdditionalPlayer> additionalPlayers;
/**
* Constructs a new MathContext with the given parameters.
*
* @param injectableContext The PlaceholderInjectable context.
* @param injectableContext The PlaceholderInjectable parseContext.
* @param player The player.
* @param additionalPlayers The additional players.
*/
public MathContext(@NotNull PlaceholderInjectable injectableContext,
@Nullable Player player,
@NotNull Collection<AdditionalPlayer> additionalPlayers) {
this.injectableContext = injectableContext;
this.player = player;
this.additionalPlayers = additionalPlayers;
}
/**
* Returns the PlaceholderInjectable parse context.
* <p>
* Duplicate method because MathContext used to be a record.
*
* @return The injectable context.
* @deprecated Use {@link #getInjectableContext()} instead.
*/
@Deprecated(since = "6.56.0", forRemoval = true)
public PlaceholderInjectable injectableContext() {
return injectableContext;
}
/**
* Returns the PlaceholderInjectable parse context.
*
* @return The injectable context.
*/
@NotNull
public PlaceholderInjectable getInjectableContext() {
return injectableContext;
}
/**
* Returns the player.
* <p>
* Duplicate method because MathContext used to be a record.
*
* @return The player.
* @deprecated Use {@link #getPlayer()} instead.
*/
@Deprecated(since = "6.56.0", forRemoval = true)
@Nullable
public Player player() {
return player;
}
/**
* Returns the player.
*
* @return The player.
*/
@Nullable
public Player getPlayer() {
return player;
}
/**
* Returns the additional players.
* <p>
* Duplicate method because MathContext used to be a record.
*
* @return The additional players.
* @deprecated Use {@link #getAdditionalPlayers()} instead.
*/
@Deprecated(since = "6.56.0", forRemoval = true)
@NotNull
public Collection<AdditionalPlayer> additionalPlayers() {
return additionalPlayers;
}
/**
* Returns the additional players.
*
* @return The additional players.
*/
@NotNull
public Collection<AdditionalPlayer> getAdditionalPlayers() {
return additionalPlayers;
}
/**
* Convert to PlaceholderContext.
*
* @return The PlaceholderContext.
*/
@NotNull
public PlaceholderContext toPlaceholderContext() {
return new PlaceholderContext(
this.player,
null,
this.injectableContext,
this.additionalPlayers
);
}
@Override
public String toString() {
return "MathContext{" +
"injectableContext=" + injectableContext +
", player=" + player +
", additionalPlayers=" + additionalPlayers +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof MathContext that)) {
return false;
}
return injectableContext.equals(that.injectableContext) &&
Objects.equals(player, that.player) &&
additionalPlayers.equals(that.additionalPlayers);
}
@Override
public int hashCode() {
return Objects.hash(injectableContext, player, additionalPlayers);
}
/**
* Create MathContext of a PlaceholderInjectable parseContext.
*
* @param injectableContext The PlaceholderInjectable parseContext.
* @return The MathContext.
*/
public static MathContext of(@NotNull final PlaceholderInjectable injectableContext) {
@@ -48,16 +194,16 @@ public record MathContext(
/**
* Copy a MathContext with a player.
*
* @param context The context.
* @param context The parseContext.
* @param player The player.
* @return The new MathContext.
*/
public static MathContext copyWithPlayer(@NotNull final MathContext context,
@Nullable final Player player) {
return new MathContext(
context.injectableContext(),
context.injectableContext,
player,
context.additionalPlayers()
context.additionalPlayers
);
}
}

View File

@@ -1,7 +1,7 @@
package com.willfp.eco.core.placeholder;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager;
import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -12,24 +12,24 @@ import java.util.regex.Pattern;
/**
* A placeholder that does not require a player and supports dynamic styles.
*/
public final class DynamicPlaceholder implements Placeholder {
public final class DynamicPlaceholder implements RegistrablePlaceholder {
/**
* The placeholder pattern.
* The arguments pattern.
*/
private final Pattern pattern;
/**
* The function to retrieve the output of the placeholder.
* The function to retrieve the output of the arguments.
*/
private final Function<String, String> function;
private final Function<@NotNull String, @Nullable String> function;
/**
* The plugin for the placeholder.
* The plugin for the arguments.
*/
private final EcoPlugin plugin;
/**
* Create a new dynamic placeholder.
* Create a new dynamic arguments.
*
* @param plugin The plugin.
* @param pattern The pattern.
@@ -37,31 +37,33 @@ public final class DynamicPlaceholder implements Placeholder {
*/
public DynamicPlaceholder(@NotNull final EcoPlugin plugin,
@NotNull final Pattern pattern,
@NotNull final Function<String, String> function) {
@NotNull final Function<@NotNull String, @Nullable String> function) {
this.plugin = plugin;
this.pattern = pattern;
this.function = function;
}
/**
* Get the value of the placeholder.
*
* @param args The args.
* @return The value.
*/
@NotNull
public String getValue(@NotNull final String args) {
@Override
@Nullable
public String getValue(@NotNull final String args,
@NotNull final PlaceholderContext context) {
return function.apply(args);
}
/**
* Register the placeholder.
* Get the value of the arguments.
*
* @return The placeholder.
* @param args The args.
* @return The value.
* @deprecated Use {@link #getValue(String, PlaceholderContext)} instead.
*/
public DynamicPlaceholder register() {
PlaceholderManager.registerPlaceholder(this);
return this;
@Deprecated(since = "6.56.0", forRemoval = true)
@NotNull
public String getValue(@NotNull final String args) {
return Objects.requireNonNullElse(
function.apply(args),
""
);
}
@Override
@@ -69,18 +71,17 @@ public final class DynamicPlaceholder implements Placeholder {
return this.plugin;
}
@Override
@Deprecated
public @NotNull String getIdentifier() {
return "dynamic";
}
@NotNull
@Override
public Pattern getPattern() {
return this.pattern;
}
@Override
public @NotNull DynamicPlaceholder register() {
return (DynamicPlaceholder) RegistrablePlaceholder.super.register();
}
@Override
public boolean equals(@Nullable final Object o) {
if (this == o) {
@@ -97,6 +98,6 @@ public final class DynamicPlaceholder implements Placeholder {
@Override
public int hashCode() {
return Objects.hash(this.getIdentifier(), this.getPlugin());
return Objects.hash(this.getPattern(), this.getPlugin());
}
}

View File

@@ -1,15 +1,20 @@
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;
/**
* Placeholders that can be injected into {@link PlaceholderInjectable} objects.
*/
public sealed interface InjectablePlaceholder extends Placeholder permits PlayerStaticPlaceholder, StaticPlaceholder {
public interface InjectablePlaceholder extends Placeholder {
/**
* Get the plugin that holds the arguments.
*
* @return The plugin.
*/
@Nullable
@Override
default @NotNull EcoPlugin getPlugin() {
return Eco.get().getEcoPlugin();
default EcoPlugin getPlugin() {
return null;
}
}

View File

@@ -1,38 +1,64 @@
package com.willfp.eco.core.placeholder;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.regex.Pattern;
/**
* A placeholder represents a string that can hold a value.
*/
public sealed interface Placeholder permits PlayerPlaceholder, PlayerlessPlaceholder,
DynamicPlaceholder, PlayerDynamicPlaceholder, InjectablePlaceholder {
public interface Placeholder {
/**
* Get the plugin that holds the placeholder.
* Get the plugin that holds the arguments.
*
* @return The plugin.
*/
@NotNull
@Nullable
EcoPlugin getPlugin();
/**
* Get the identifier for the placeholder.
* Get the value of the arguments.
*
* @return The identifier.
* @param args The args.
* @param context The context.
* @return The value.
*/
@NotNull
String getIdentifier();
@Nullable
String getValue(@NotNull String args,
@NotNull PlaceholderContext context);
/**
* Get the pattern for the placeholder.
* Get the pattern for the arguments.
*
* @return The pattern.
*/
@NotNull
default Pattern getPattern() {
return Pattern.compile(this.getIdentifier());
Pattern getPattern();
/**
* Try to translate all instances of this placeholder in text quickly.
*
* @param text The text to translate.
* @param context The context.
* @return The translated text.
*/
default String tryTranslateQuickly(@NotNull final String text,
@NotNull final PlaceholderContext context) {
return text;
}
/**
* Get the identifier for the arguments.
*
* @return The identifier.
* @deprecated Some arguments may not have an identifier. Use {@link #getPattern()} instead.
*/
@Deprecated(since = "6.56.0", forRemoval = true)
@NotNull
default String getIdentifier() {
return this.getPattern().pattern();
}
}

View File

@@ -9,7 +9,7 @@ import java.util.List;
*/
public interface PlaceholderInjectable {
/**
* Inject placeholder.
* Inject arguments.
*
* @param placeholders The placeholders.
*/
@@ -18,7 +18,7 @@ public interface PlaceholderInjectable {
}
/**
* Inject placeholder.
* Inject arguments.
*
* @param placeholders The placeholders.
*/
@@ -29,7 +29,7 @@ public interface PlaceholderInjectable {
/**
* Inject placeholders.
* <p>
* When implementing a PlaceholderInjectable object, override this method.
* If a placeholder already has the same pattern, it should be replaced.
*
* @param placeholders The placeholders.
*/
@@ -43,7 +43,7 @@ public interface PlaceholderInjectable {
/**
* Get injected placeholders.
* <p>
* Override this method in implementations.
* This method should always return an immutable list.
*
* @return Injected placeholders.
*/

View File

@@ -1,7 +1,7 @@
package com.willfp.eco.core.placeholder;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager;
import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -11,26 +11,26 @@ import java.util.function.BiFunction;
import java.util.regex.Pattern;
/**
* A placeholder that does not require a player and supports dynamic styles.
* A arguments that does not require a player and supports dynamic styles.
*/
public final class PlayerDynamicPlaceholder implements Placeholder {
public final class PlayerDynamicPlaceholder implements RegistrablePlaceholder {
/**
* The placeholder pattern.
* The arguments pattern.
*/
private final Pattern pattern;
/**
* The function to retrieve the output of the placeholder.
* The function to retrieve the output of the arguments.
*/
private final BiFunction<String, Player, String> function;
private final BiFunction<@NotNull String, @NotNull Player, @Nullable String> function;
/**
* The plugin for the placeholder.
* The plugin for the arguments.
*/
private final EcoPlugin plugin;
/**
* Create a new dynamic placeholder.
* Create a new dynamic arguments.
*
* @param plugin The plugin.
* @param pattern The pattern.
@@ -38,33 +38,40 @@ public final class PlayerDynamicPlaceholder implements Placeholder {
*/
public PlayerDynamicPlaceholder(@NotNull final EcoPlugin plugin,
@NotNull final Pattern pattern,
@NotNull final BiFunction<String, Player, String> function) {
@NotNull final BiFunction<@NotNull String, @NotNull Player, @Nullable String> function) {
this.plugin = plugin;
this.pattern = pattern;
this.function = function;
}
/**
* Get the value of the placeholder.
*
* @param args The args.
* @param player The player.
* @return The value.
*/
@NotNull
public String getValue(@NotNull final String args,
@NotNull final Player player) {
@Override
public @Nullable String getValue(@NotNull final String args,
@NotNull final PlaceholderContext context) {
Player player = context.getPlayer();
if (player == null) {
return null;
}
return function.apply(args, player);
}
/**
* Register the placeholder.
* Get the value of the arguments.
*
* @return The placeholder.
* @param args The args.
* @param player The player.
* @return The value.
* @deprecated Use {@link #getValue(String, PlaceholderContext)} instead.
*/
public PlayerDynamicPlaceholder register() {
PlaceholderManager.registerPlaceholder(this);
return this;
@Deprecated(since = "6.56.0", forRemoval = true)
@NotNull
public String getValue(@NotNull final String args,
@NotNull final Player player) {
return Objects.requireNonNullElse(
function.apply(args, player),
""
);
}
@Override
@@ -72,18 +79,17 @@ public final class PlayerDynamicPlaceholder implements Placeholder {
return this.plugin;
}
@Override
@Deprecated
public @NotNull String getIdentifier() {
return "dynamic";
}
@NotNull
@Override
public Pattern getPattern() {
return this.pattern;
}
@Override
public @NotNull PlayerDynamicPlaceholder register() {
return (PlayerDynamicPlaceholder) RegistrablePlaceholder.super.register();
}
@Override
public boolean equals(@Nullable final Object o) {
if (this == o) {
@@ -100,6 +106,6 @@ public final class PlayerDynamicPlaceholder implements Placeholder {
@Override
public int hashCode() {
return Objects.hash(this.getIdentifier(), this.getPlugin());
return Objects.hash(this.getPattern(), this.getPlugin());
}
}

View File

@@ -1,7 +1,7 @@
package com.willfp.eco.core.placeholder;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager;
import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -11,31 +11,26 @@ import java.util.function.Function;
import java.util.regex.Pattern;
/**
* A placeholder that requires a player.
* A arguments that requires a player.
*/
public final class PlayerPlaceholder implements Placeholder {
public final class PlayerPlaceholder implements RegistrablePlaceholder {
/**
* The name of the placeholder.
*/
private final String identifier;
/**
* The placeholder pattern.
* The arguments pattern.
*/
private final Pattern pattern;
/**
* The function to retrieve the output of the placeholder given a player.
* The function to retrieve the output of the arguments given a player.
*/
private final Function<Player, String> function;
private final Function<@NotNull Player, @Nullable String> function;
/**
* The plugin for the placeholder.
* The plugin for the arguments.
*/
private final EcoPlugin plugin;
/**
* Create a new player placeholder.
* Create a new player arguments.
*
* @param plugin The plugin.
* @param identifier The identifier.
@@ -43,32 +38,38 @@ public final class PlayerPlaceholder implements Placeholder {
*/
public PlayerPlaceholder(@NotNull final EcoPlugin plugin,
@NotNull final String identifier,
@NotNull final Function<Player, String> function) {
@NotNull final Function<@NotNull Player, @Nullable String> function) {
this.plugin = plugin;
this.identifier = identifier;
this.pattern = Pattern.compile(identifier);
this.pattern = Pattern.compile(identifier, Pattern.LITERAL);
this.function = function;
}
/**
* Get the value of the placeholder for a given player.
*
* @param player The player.
* @return The value.
*/
@NotNull
public String getValue(@NotNull final Player player) {
@Override
public @Nullable String getValue(@NotNull final String args,
@NotNull final PlaceholderContext context) {
Player player = context.getPlayer();
if (player == null) {
return null;
}
return function.apply(player);
}
/**
* Register the placeholder.
* Get the value of the arguments for a given player.
*
* @return The placeholder.
* @param player The player.
* @return The value.
* @deprecated Use {@link #getValue(String, PlaceholderContext)} instead.
*/
public PlayerPlaceholder register() {
PlaceholderManager.registerPlaceholder(this);
return this;
@Deprecated(since = "6.56.0", forRemoval = true)
@NotNull
public String getValue(@NotNull final Player player) {
return Objects.requireNonNullElse(
function.apply(player),
""
);
}
@Override
@@ -76,17 +77,17 @@ public final class PlayerPlaceholder implements Placeholder {
return this.plugin;
}
@Override
public @NotNull String getIdentifier() {
return this.identifier;
}
@NotNull
@Override
public Pattern getPattern() {
return this.pattern;
}
@Override
public @NotNull PlayerPlaceholder register() {
return (PlayerPlaceholder) RegistrablePlaceholder.super.register();
}
@Override
public boolean equals(@Nullable final Object o) {
if (this == o) {
@@ -95,12 +96,12 @@ public final class PlayerPlaceholder implements Placeholder {
if (!(o instanceof PlayerPlaceholder that)) {
return false;
}
return Objects.equals(this.getIdentifier(), that.getIdentifier())
return Objects.equals(this.getPattern(), that.getPattern())
&& Objects.equals(this.getPlugin(), that.getPlugin());
}
@Override
public int hashCode() {
return Objects.hash(this.getIdentifier(), this.getPlugin());
return Objects.hash(this.getPattern(), this.getPlugin());
}
}

View File

@@ -1,5 +1,7 @@
package com.willfp.eco.core.placeholder;
import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import com.willfp.eco.util.StringUtils;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -13,47 +15,72 @@ import java.util.regex.Pattern;
*/
public final class PlayerStaticPlaceholder implements InjectablePlaceholder {
/**
* The name of the placeholder.
* The identifier.
*/
private final String identifier;
/**
* The placeholder pattern.
* The arguments pattern.
*/
private final Pattern pattern;
/**
* The function to retrieve the output of the placeholder.
* The function to retrieve the output of the arguments.
*/
private final Function<Player, String> function;
private final Function<@NotNull Player, @Nullable String> function;
/**
* Create a new player placeholder.
* Create a new player arguments.
*
* @param identifier The identifier.
* @param function The function to retrieve the value.
*/
public PlayerStaticPlaceholder(@NotNull final String identifier,
@NotNull final Function<Player, String> function) {
this.identifier = identifier;
this.pattern = Pattern.compile(identifier);
@NotNull final Function<@NotNull Player, @Nullable String> function) {
this.identifier = "%" + identifier + "%";
this.pattern = Pattern.compile(identifier, Pattern.LITERAL);
this.function = function;
}
@Override
public @Nullable String getValue(@NotNull final String args,
@NotNull final PlaceholderContext context) {
Player player = context.getPlayer();
if (player == null) {
return null;
}
return this.getValue(player);
}
/**
* Get the value of the placeholder.
* Get the value of the arguments.
*
* @param player The player.
* @return The value.
* @deprecated Use {@link #getValue(String, PlaceholderContext)} instead.
*/
@Deprecated(since = "6.56.0", forRemoval = true)
@NotNull
public String getValue(@NotNull final Player player) {
return function.apply(player);
return Objects.requireNonNullElse(
function.apply(player),
""
);
}
@Override
public @NotNull String getIdentifier() {
return this.identifier;
public String tryTranslateQuickly(@NotNull final String text,
@NotNull final PlaceholderContext context) {
return StringUtils.replaceQuickly(
text,
this.identifier,
Objects.requireNonNullElse(
this.getValue(identifier, context),
""
)
);
}
@NotNull
@@ -70,11 +97,11 @@ public final class PlayerStaticPlaceholder implements InjectablePlaceholder {
if (!(o instanceof PlayerStaticPlaceholder that)) {
return false;
}
return Objects.equals(this.getIdentifier(), that.getIdentifier());
return Objects.equals(this.getPattern(), that.getPattern());
}
@Override
public int hashCode() {
return Objects.hash(this.getIdentifier());
return Objects.hash(this.getPattern());
}
}

View File

@@ -1,7 +1,7 @@
package com.willfp.eco.core.placeholder;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager;
import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -10,31 +10,26 @@ import java.util.function.Supplier;
import java.util.regex.Pattern;
/**
* A placeholder that does not require a player.
* A arguments that does not require a player.
*/
public final class PlayerlessPlaceholder implements Placeholder {
public final class PlayerlessPlaceholder implements RegistrablePlaceholder {
/**
* The placeholder identifier.
*/
private final String identifier;
/**
* The placeholder pattern.
* The arguments pattern.
*/
private final Pattern pattern;
/**
* The function to retrieve the output of the placeholder.
* The function to retrieve the output of the arguments.
*/
private final Supplier<String> function;
private final Supplier<@Nullable String> function;
/**
* The plugin for the placeholder.
* The plugin for the arguments.
*/
private final EcoPlugin plugin;
/**
* Create a new player placeholder.
* Create a new player arguments.
*
* @param plugin The plugin.
* @param identifier The identifier.
@@ -42,30 +37,31 @@ public final class PlayerlessPlaceholder implements Placeholder {
*/
public PlayerlessPlaceholder(@NotNull final EcoPlugin plugin,
@NotNull final String identifier,
@NotNull final Supplier<String> function) {
@NotNull final Supplier<@Nullable String> function) {
this.plugin = plugin;
this.identifier = identifier;
this.pattern = Pattern.compile(identifier);
this.pattern = Pattern.compile(identifier, Pattern.LITERAL);
this.function = function;
}
/**
* Get the value of the placeholder.
*
* @return The value.
*/
public String getValue() {
@Override
public @Nullable String getValue(@NotNull final String args,
@NotNull final PlaceholderContext context) {
return function.get();
}
/**
* Register the placeholder.
* Get the value of the arguments.
*
* @return The placeholder.
* @return The value.
* @deprecated Use {@link #getValue(String, PlaceholderContext)} instead.
*/
public PlayerlessPlaceholder register() {
PlaceholderManager.registerPlaceholder(this);
return this;
@Deprecated(since = "6.56.0", forRemoval = true)
@NotNull
public String getValue() {
return Objects.requireNonNullElse(
this.function.get(),
""
);
}
@Override
@@ -73,17 +69,17 @@ public final class PlayerlessPlaceholder implements Placeholder {
return this.plugin;
}
@Override
public @NotNull String getIdentifier() {
return this.identifier;
}
@NotNull
@Override
public Pattern getPattern() {
return this.pattern;
}
@Override
public @NotNull PlayerlessPlaceholder register() {
return (PlayerlessPlaceholder) RegistrablePlaceholder.super.register();
}
@Override
public boolean equals(@Nullable final Object o) {
if (this == o) {
@@ -92,12 +88,12 @@ public final class PlayerlessPlaceholder implements Placeholder {
if (!(o instanceof PlayerlessPlaceholder that)) {
return false;
}
return Objects.equals(this.getIdentifier(), that.getIdentifier())
return Objects.equals(this.getPattern(), that.getPattern())
&& Objects.equals(this.getPlugin(), that.getPlugin());
}
@Override
public int hashCode() {
return Objects.hash(this.getIdentifier(), this.getPlugin());
return Objects.hash(this.getPattern(), this.getPlugin());
}
}

View File

@@ -0,0 +1,31 @@
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;
/**
* Represents a placeholder that can be registered.
*/
public interface RegistrablePlaceholder extends Placeholder {
/**
* Get the plugin that holds the arguments.
*
* @return The plugin.
*/
@NotNull
@Override
EcoPlugin getPlugin();
/**
* Register the arguments.
*
* @return The arguments.
*/
@NotNull
default RegistrablePlaceholder register() {
PlaceholderManager.registerPlaceholder(this);
return this;
}
}

View File

@@ -1,5 +1,7 @@
package com.willfp.eco.core.placeholder;
import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import com.willfp.eco.util.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -8,50 +10,66 @@ import java.util.function.Supplier;
import java.util.regex.Pattern;
/**
* A placeholder that cannot be registered, and exists purely in injection.
* A arguments that cannot be registered, and exists purely in injection.
*/
public final class StaticPlaceholder implements InjectablePlaceholder {
/**
* The name of the placeholder.
* The name of the arguments.
*/
private final String identifier;
/**
* The placeholder pattern.
* The arguments pattern.
*/
private final Pattern pattern;
/**
* The function to retrieve the output of the placeholder.
* The function to retrieve the output of the arguments.
*/
private final Supplier<String> function;
private final Supplier<@Nullable String> function;
/**
* Create a new player placeholder.
* Create a new player arguments.
*
* @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.pattern = Pattern.compile(identifier);
@NotNull final Supplier<@Nullable String> function) {
this.identifier = "%" + identifier + "%";
this.pattern = Pattern.compile(identifier, Pattern.LITERAL);
this.function = function;
}
/**
* Get the value of the placeholder.
*
* @return The value.
*/
@NotNull
public String getValue() {
@Override
public @Nullable String getValue(@NotNull final String args,
@NotNull final PlaceholderContext context) {
return function.get();
}
/**
* Get the value of the arguments.
*
* @return The value.
* @deprecated Use {@link #getValue(String, PlaceholderContext)} instead.
*/
@Deprecated(since = "6.56.0", forRemoval = true)
@NotNull
public String getValue() {
return Objects.requireNonNullElse(
function.get(),
""
);
}
@Override
public @NotNull String getIdentifier() {
return this.identifier;
public String tryTranslateQuickly(@NotNull final String text,
@NotNull final PlaceholderContext context) {
return StringUtils.replaceQuickly(
text,
this.identifier,
Objects.requireNonNullElse(this.getValue(this.identifier, context), "")
);
}
@NotNull
@@ -68,11 +86,11 @@ public final class StaticPlaceholder implements InjectablePlaceholder {
if (!(o instanceof StaticPlaceholder that)) {
return false;
}
return Objects.equals(this.getIdentifier(), that.getIdentifier());
return Objects.equals(this.getPattern(), that.getPattern());
}
@Override
public int hashCode() {
return Objects.hash(this.getIdentifier());
return Objects.hash(this.getPattern());
}
}

View File

@@ -0,0 +1,92 @@
package com.willfp.eco.core.placeholder.context;
import com.willfp.eco.core.placeholder.InjectablePlaceholder;
import com.willfp.eco.core.placeholder.PlaceholderInjectable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
/**
* A merged injectable context.
*/
public class MergedInjectableContext implements PlaceholderInjectable {
/**
* The base context.
*/
private final PlaceholderInjectable baseContext;
/**
* The additional context.
*/
private final PlaceholderInjectable additionalContext;
/**
* Extra injections.
*/
private final Set<InjectablePlaceholder> extraInjections = new HashSet<>();
/**
* Create a new merged injectable context.
*
* @param baseContext The base context.
* @param additionalContext The additional context.
*/
public MergedInjectableContext(@NotNull final PlaceholderInjectable baseContext,
@NotNull final PlaceholderInjectable additionalContext) {
this.baseContext = baseContext;
this.additionalContext = additionalContext;
}
@Override
public void addInjectablePlaceholder(@NotNull final Iterable<InjectablePlaceholder> placeholders) {
for (InjectablePlaceholder placeholder : placeholders) {
extraInjections.add(placeholder);
}
}
@Override
public void clearInjectedPlaceholders() {
baseContext.clearInjectedPlaceholders();
additionalContext.clearInjectedPlaceholders();
extraInjections.clear();
}
@Override
public @NotNull List<InjectablePlaceholder> getPlaceholderInjections() {
List<InjectablePlaceholder> base = baseContext.getPlaceholderInjections();
List<InjectablePlaceholder> additional = additionalContext.getPlaceholderInjections();
List<InjectablePlaceholder> injections = new ArrayList<>(base.size() + additional.size() + extraInjections.size());
injections.addAll(base);
injections.addAll(additional);
injections.addAll(extraInjections);
return injections;
}
@Override
public boolean equals(@Nullable final Object o) {
if (this == o) {
return true;
}
if (!(o instanceof MergedInjectableContext that)) {
return false;
}
return Objects.equals(baseContext, that.baseContext)
&& Objects.equals(additionalContext, that.additionalContext)
&& Objects.equals(extraInjections, that.extraInjections);
}
@Override
public int hashCode() {
return Objects.hash(baseContext, additionalContext, extraInjections);
}
}

View File

@@ -0,0 +1,252 @@
package com.willfp.eco.core.placeholder.context;
import com.willfp.eco.core.placeholder.AdditionalPlayer;
import com.willfp.eco.core.placeholder.InjectablePlaceholder;
import com.willfp.eco.core.placeholder.PlaceholderInjectable;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
/**
* Represents a context to translate placeholders in.
*/
public class PlaceholderContext {
/**
* An empty injectable.
*/
private static final PlaceholderInjectable EMPTY_INJECTABLE = new PlaceholderInjectable() {
@Override
public void addInjectablePlaceholder(@NotNull final Iterable<InjectablePlaceholder> placeholders) {
// Do nothing.
}
@Override
public void clearInjectedPlaceholders() {
// Do nothing.
}
@Override
public @NotNull List<InjectablePlaceholder> getPlaceholderInjections() {
return Collections.emptyList();
}
};
/**
* An empty context.
*/
public static final PlaceholderContext EMPTY = new PlaceholderContext(
null,
null,
null,
Collections.emptyList()
);
/**
* The player.
*/
@Nullable
private final Player player;
/**
* The ItemStack.
*/
@Nullable
private final ItemStack itemStack;
/**
* The PlaceholderInjectable context.
*/
@NotNull
private final PlaceholderInjectable injectableContext;
/**
* The additional players.
*/
@NotNull
private final Collection<AdditionalPlayer> additionalPlayers;
/**
* Create an empty PlaceholderContext.
*/
public PlaceholderContext() {
this(null, null, null, Collections.emptyList());
}
/**
* Create a PlaceholderContext for a player.
*
* @param player The player.
*/
public PlaceholderContext(@Nullable final Player player) {
this(player, null, null, Collections.emptyList());
}
/**
* Constructs a new PlaceholderContext with the given parameters.
*
* @param player The player.
* @param itemStack The ItemStack.
* @param injectableContext The PlaceholderInjectable parseContext.
* @param additionalPlayers The additional players.
*/
public PlaceholderContext(@Nullable final Player player,
@Nullable final ItemStack itemStack,
@Nullable final PlaceholderInjectable injectableContext,
@NotNull final Collection<AdditionalPlayer> additionalPlayers) {
this.player = player;
this.itemStack = itemStack;
this.injectableContext = Objects.requireNonNullElse(injectableContext, EMPTY_INJECTABLE);
this.additionalPlayers = additionalPlayers;
}
/**
* Get the player.
*
* @return The player.
*/
@Nullable
public Player getPlayer() {
return player;
}
/**
* Get the ItemStack.
*
* @return The ItemStack.
*/
@Nullable
public ItemStack getItemStack() {
return itemStack;
}
/**
* Get the PlaceholderInjectable context.
*
* @return The PlaceholderInjectable context.
*/
@NotNull
public PlaceholderInjectable getInjectableContext() {
return injectableContext;
}
/**
* Get the additional players.
*
* @return The additional players.
*/
@NotNull
public Collection<AdditionalPlayer> getAdditionalPlayers() {
return additionalPlayers;
}
/**
* Convert to a {@link com.willfp.eco.core.math.MathContext}.
*
* @return The math context.
* @deprecated MathContext is deprecated, use {@link PlaceholderContext} instead.
*/
@Deprecated(since = "6.56.0", forRemoval = true)
@SuppressWarnings({"removal", "DeprecatedIsStillUsed"})
public com.willfp.eco.core.math.MathContext toMathContext() {
return new com.willfp.eco.core.math.MathContext(this.getInjectableContext(), this.getPlayer(), this.getAdditionalPlayers());
}
/**
* Copy with a player.
*
* @param player The player.
* @return The new context.
*/
public PlaceholderContext copyWithPlayer(@Nullable final Player player) {
return new PlaceholderContext(
player,
this.getItemStack(),
this.getInjectableContext(),
this.getAdditionalPlayers()
);
}
/**
* Copy with an item.
*
* @param itemStack The ItemStack.
* @return The new context.
*/
public PlaceholderContext copyWithItem(@Nullable final ItemStack itemStack) {
return new PlaceholderContext(
this.getPlayer(),
itemStack,
this.getInjectableContext(),
this.getAdditionalPlayers()
);
}
/**
* Copy with an extra injectable context.
*
* @param injectableContext The injectable context to add.
* @return The new context.
*/
public PlaceholderContext withInjectableContext(@NotNull final PlaceholderInjectable injectableContext) {
return new PlaceholderContext(
this.getPlayer(),
this.getItemStack(),
new MergedInjectableContext(this.getInjectableContext(), injectableContext),
this.getAdditionalPlayers()
);
}
@Override
public String toString() {
return "PlaceholderContext{" +
"player=" + player +
", itemStack=" + itemStack +
", injectableContext=" + injectableContext +
", additionalPlayers=" + additionalPlayers +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof PlaceholderContext that)) {
return false;
}
return Objects.equals(
getPlayer(), that.getPlayer())
&& Objects.equals(getItemStack(), that.getItemStack())
&& getInjectableContext().equals(that.getInjectableContext())
&& getAdditionalPlayers().equals(that.getAdditionalPlayers()
);
}
@Override
public int hashCode() {
return Objects.hash(getPlayer(), getItemStack(), getInjectableContext(), getAdditionalPlayers());
}
/**
* Create PlaceholderContext of a PlaceholderInjectable parseContext.
*
* @param injectableContext The PlaceholderInjectable parseContext.
* @return The context.
*/
public static PlaceholderContext of(@NotNull final PlaceholderInjectable injectableContext) {
return new PlaceholderContext(
null,
null,
injectableContext,
Collections.emptyList()
);
}
}

View File

@@ -0,0 +1,19 @@
package com.willfp.eco.core.placeholder.context;
import org.jetbrains.annotations.NotNull;
/**
* A supplier that takes a {@link PlaceholderContext} and returns a value.
*
* @param <T> The type of value to return.
*/
public interface PlaceholderContextSupplier<T> {
/**
* Get the value.
*
* @param context The context.
* @return The value.
*/
@NotNull
T get(@NotNull PlaceholderContext context);
}

View File

@@ -0,0 +1,57 @@
package com.willfp.eco.core.placeholder.templates;
import com.willfp.eco.core.placeholder.InjectablePlaceholder;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
import java.util.regex.Pattern;
/**
* A template class for dynamic placeholders.
*/
public abstract class DynamicInjectablePlaceholder implements InjectablePlaceholder {
/**
* The placeholder pattern.
*/
private final Pattern pattern;
/**
* Create a new dynamic injectable placeholder.
*
* @param pattern The pattern.
*/
protected DynamicInjectablePlaceholder(@NotNull final Pattern pattern) {
this.pattern = pattern;
}
@Override
public @NotNull Pattern getPattern() {
return pattern;
}
@Override
public String toString() {
return "DynamicInjectablePlaceholder{" +
"pattern='" + pattern + '\'' +
'}';
}
@Override
public boolean equals(@NotNull final Object o) {
if (this == o) {
return true;
}
if (!(o instanceof DynamicInjectablePlaceholder that)) {
return false;
}
return Objects.equals(pattern, that.getPattern());
}
@Override
public int hashCode() {
return Objects.hash(pattern);
}
}

View File

@@ -0,0 +1,73 @@
package com.willfp.eco.core.placeholder.templates;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.placeholder.RegistrablePlaceholder;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
import java.util.regex.Pattern;
/**
* A template class for dynamic placeholders.
*/
public abstract class DynamicPlaceholder implements RegistrablePlaceholder {
/**
* The plugin.
*/
private final EcoPlugin plugin;
/**
* The placeholder pattern.
*/
private final Pattern pattern;
/**
* Create a new dynamic placeholder.
*
* @param plugin The plugin.
* @param pattern The pattern.
*/
protected DynamicPlaceholder(@NotNull final EcoPlugin plugin,
@NotNull final Pattern pattern) {
this.plugin = plugin;
this.pattern = pattern;
}
@Override
public @NotNull Pattern getPattern() {
return pattern;
}
@NotNull
@Override
public EcoPlugin getPlugin() {
return plugin;
}
@Override
public String toString() {
return "DynamicPlaceholder{" +
"plugin='" + plugin + '\'' +
"pattern='" + pattern + '\'' +
'}';
}
@Override
public boolean equals(@NotNull final Object o) {
if (this == o) {
return true;
}
if (!(o instanceof DynamicPlaceholder that)) {
return false;
}
return Objects.equals(pattern, that.getPattern())
&& Objects.equals(plugin, that.getPlugin());
}
@Override
public int hashCode() {
return Objects.hash(pattern, plugin);
}
}

View File

@@ -0,0 +1,72 @@
package com.willfp.eco.core.placeholder.templates;
import com.willfp.eco.core.placeholder.InjectablePlaceholder;
import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
import java.util.regex.Pattern;
/**
* A template class for simple placeholders.
*/
public abstract class SimpleInjectablePlaceholder implements InjectablePlaceholder {
/**
* The name of the placeholder.
*/
private final String identifier;
/**
* The placeholder pattern.
*/
private final Pattern pattern;
/**
* Create a new simple injectable placeholder.
*
* @param identifier The identifier.
*/
protected SimpleInjectablePlaceholder(@NotNull final String identifier) {
this.identifier = identifier;
this.pattern = Pattern.compile(identifier, Pattern.LITERAL);
}
@Override
public String tryTranslateQuickly(@NotNull final String text,
@NotNull final PlaceholderContext context) {
return text.replace(
"%" + this.identifier + "%",
Objects.requireNonNullElse(this.getValue(this.identifier, context), "")
);
}
@Override
public @NotNull Pattern getPattern() {
return pattern;
}
@Override
public String toString() {
return "SimpleInjectablePlaceholder{" +
"identifier='" + identifier + '\'' +
'}';
}
@Override
public boolean equals(@NotNull final Object o) {
if (this == o) {
return true;
}
if (!(o instanceof SimpleInjectablePlaceholder that)) {
return false;
}
return Objects.equals(identifier, that.identifier);
}
@Override
public int hashCode() {
return Objects.hash(identifier);
}
}

View File

@@ -0,0 +1,89 @@
package com.willfp.eco.core.placeholder.templates;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.placeholder.RegistrablePlaceholder;
import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
import java.util.regex.Pattern;
/**
* A template class for simple placeholders.
*/
public abstract class SimplePlaceholder implements RegistrablePlaceholder {
/**
* The plugin.
*/
private final EcoPlugin plugin;
/**
* The name of the placeholder.
*/
private final String identifier;
/**
* The placeholder pattern.
*/
private final Pattern pattern;
/**
* Create a new simple placeholder.
*
* @param plugin The plugin.
* @param identifier The identifier.
*/
protected SimplePlaceholder(@NotNull final EcoPlugin plugin,
@NotNull final String identifier) {
this.plugin = plugin;
this.identifier = identifier;
this.pattern = Pattern.compile(identifier, Pattern.LITERAL);
}
@Override
public String tryTranslateQuickly(@NotNull final String text,
@NotNull final PlaceholderContext context) {
return text.replace(
"%" + this.identifier + "%",
Objects.requireNonNullElse(this.getValue(this.identifier, context), "")
);
}
@Override
public @NotNull Pattern getPattern() {
return pattern;
}
@NotNull
@Override
public EcoPlugin getPlugin() {
return plugin;
}
@Override
public String toString() {
return "SimplePlaceholder{" +
"plugin='" + plugin + '\'' +
"identifier='" + identifier + '\'' +
'}';
}
@Override
public boolean equals(@NotNull final Object o) {
if (this == o) {
return true;
}
if (!(o instanceof SimplePlaceholder that)) {
return false;
}
return Objects.equals(pattern, that.getPattern())
&& Objects.equals(plugin, that.getPlugin());
}
@Override
public int hashCode() {
return Objects.hash(identifier, plugin);
}
}

View File

@@ -1,7 +1,7 @@
package com.willfp.eco.core.price;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.math.MathContext;
import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import com.willfp.eco.core.price.impl.PriceFree;
import com.willfp.eco.core.serialization.ConfigDeserializer;
import com.willfp.eco.util.NumberUtils;
@@ -168,7 +168,7 @@ public final class ConfiguredPrice implements Price {
Price price = Prices.create(
config.getString("value"),
config.getString("type"),
MathContext.of(config)
PlaceholderContext.of(config)
);
return new ConfiguredPrice(price, formatString);

View File

@@ -1,6 +1,7 @@
package com.willfp.eco.core.price;
import com.willfp.eco.core.math.MathContext;
import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import com.willfp.eco.core.placeholder.context.PlaceholderContextSupplier;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
@@ -10,7 +11,8 @@ import java.util.function.Function;
/**
* Create prices.
* <p>
* You must override one of the create methods.
* Override create(PlaceholderContext, PlaceholderContextSupplier), other methods
* are for backwards compatibility.
*/
public interface PriceFactory {
/**
@@ -27,20 +29,38 @@ public interface PriceFactory {
*
* @param value The value.
* @return The price.
* @deprecated Use {@link #create(PlaceholderContext, PlaceholderContextSupplier)} instead.
*/
@Deprecated(since = "6.56.0", forRemoval = true)
default @NotNull Price create(final double value) {
return create(MathContext.EMPTY, (ctx) -> value);
return create(PlaceholderContext.EMPTY, (ctx) -> value);
}
/**
* Create the price.
*
* @param baseContext The base MathContext.
* @param function The function to use. Should use {@link MathContext#copyWithPlayer(MathContext, Player)} on calls.
* @param function The function to use. Should use {@link com.willfp.eco.core.math.MathContext#copyWithPlayer(com.willfp.eco.core.math.MathContext, Player)} on calls.
* @return The price.
* @deprecated Use {@link #create(PlaceholderContext, PlaceholderContextSupplier)} instead.
*/
@Deprecated(since = "6.56.0", forRemoval = true)
@SuppressWarnings("removal")
default @NotNull Price create(@NotNull final com.willfp.eco.core.math.MathContext baseContext,
@NotNull final Function<com.willfp.eco.core.math.MathContext, Double> function) {
return create(baseContext.toPlaceholderContext(), (PlaceholderContext ctx) -> function.apply(ctx.toMathContext()));
}
/**
* Create the price.
*
* @param baseContext The base PlaceholderContext.
* @param function The function to use. Should use {@link PlaceholderContext#copyWithPlayer(Player)} on calls.
* @return The price.
*/
default @NotNull Price create(@NotNull final MathContext baseContext,
@NotNull final Function<MathContext, Double> function) {
return create(function.apply(baseContext));
@SuppressWarnings("removal")
default @NotNull Price create(@NotNull final PlaceholderContext baseContext,
@NotNull final PlaceholderContextSupplier<Double> function) {
return create(baseContext.toMathContext(), (com.willfp.eco.core.math.MathContext ctx) -> function.get(ctx.toPlaceholderContext()));
}
}

View File

@@ -2,7 +2,8 @@ package com.willfp.eco.core.price;
import com.willfp.eco.core.items.Items;
import com.willfp.eco.core.items.TestableItem;
import com.willfp.eco.core.math.MathContext;
import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import com.willfp.eco.core.placeholder.context.PlaceholderContextSupplier;
import com.willfp.eco.core.price.impl.PriceEconomy;
import com.willfp.eco.core.price.impl.PriceFree;
import com.willfp.eco.core.price.impl.PriceItem;
@@ -13,7 +14,6 @@ import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
/**
* Class to manage prices.
@@ -48,7 +48,28 @@ public final class Prices {
@NotNull
public static Price create(@NotNull final String expression,
@Nullable final String priceName) {
return create(expression, priceName, MathContext.EMPTY);
return create(expression, priceName, PlaceholderContext.EMPTY);
}
/**
* Create price from an expression (representing the value),
* and a price name. Uses a context to parse the expression.
* <p>
* Supports items as price names.
*
* @param expression The expression for the value.
* @param priceName The price name.
* @param context The math context to parse the expression.
* @return The price, or free if invalid.
* @deprecated Use {@link #create(String, String, PlaceholderContext)} instead.
*/
@NotNull
@Deprecated(since = "6.56.0", forRemoval = true)
@SuppressWarnings("removal")
public static Price create(@NotNull final String expression,
@Nullable final String priceName,
@NotNull final com.willfp.eco.core.math.MathContext context) {
return create(expression, priceName, context.toPlaceholderContext());
}
/**
@@ -65,8 +86,8 @@ public final class Prices {
@NotNull
public static Price create(@NotNull final String expression,
@Nullable final String priceName,
@NotNull final MathContext context) {
Function<MathContext, Double> function = (ctx) -> NumberUtils.evaluateExpression(
@NotNull final PlaceholderContext context) {
PlaceholderContextSupplier<Double> function = (ctx) -> NumberUtils.evaluateExpression(
expression,
ctx
);

View File

@@ -1,7 +1,8 @@
package com.willfp.eco.core.price.impl;
import com.willfp.eco.core.integrations.economy.EconomyManager;
import com.willfp.eco.core.math.MathContext;
import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import com.willfp.eco.core.placeholder.context.PlaceholderContextSupplier;
import com.willfp.eco.core.price.Price;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
@@ -18,12 +19,12 @@ public final class PriceEconomy implements Price {
/**
* The value of the price.
*/
private final Function<MathContext, Double> function;
private final PlaceholderContextSupplier<Double> function;
/**
* The base math context.
* The base placeholder context.
*/
private final MathContext baseContext;
private final PlaceholderContext baseContext;
/**
* The multipliers.
@@ -36,7 +37,21 @@ public final class PriceEconomy implements Price {
* @param value The value.
*/
public PriceEconomy(final double value) {
this(MathContext.EMPTY, ctx -> value);
this(PlaceholderContext.EMPTY, (PlaceholderContext ctx) -> value);
}
/**
* Create a new economy-based price.
*
* @param baseContext The base context.
* @param function The function.
* @deprecated Use {@link #PriceEconomy(PlaceholderContext, PlaceholderContextSupplier)} instead.
*/
@Deprecated(since = "6.56.0", forRemoval = true)
@SuppressWarnings("removal")
public PriceEconomy(@NotNull final com.willfp.eco.core.math.MathContext baseContext,
@NotNull final Function<com.willfp.eco.core.math.MathContext, Double> function) {
this(baseContext.toPlaceholderContext(), (PlaceholderContext ctx) -> function.apply(ctx.toMathContext()));
}
/**
@@ -45,8 +60,8 @@ public final class PriceEconomy implements Price {
* @param baseContext The base context.
* @param function The function.
*/
public PriceEconomy(@NotNull final MathContext baseContext,
@NotNull final Function<MathContext, Double> function) {
public PriceEconomy(@NotNull final PlaceholderContext baseContext,
@NotNull final PlaceholderContextSupplier<Double> function) {
this.baseContext = baseContext;
this.function = function;
}
@@ -72,7 +87,7 @@ public final class PriceEconomy implements Price {
@Override
public double getValue(@NotNull final Player player,
final double multiplier) {
return this.function.apply(MathContext.copyWithPlayer(baseContext, player)) * getMultiplier(player) * multiplier;
return this.function.get(baseContext.copyWithPlayer(player)) * getMultiplier(player) * multiplier;
}
@Override

View File

@@ -3,7 +3,8 @@ package com.willfp.eco.core.price.impl;
import com.willfp.eco.core.drops.DropQueue;
import com.willfp.eco.core.items.HashedItem;
import com.willfp.eco.core.items.TestableItem;
import com.willfp.eco.core.math.MathContext;
import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import com.willfp.eco.core.placeholder.context.PlaceholderContextSupplier;
import com.willfp.eco.core.price.Price;
import org.bukkit.Material;
import org.bukkit.entity.Player;
@@ -20,14 +21,14 @@ import java.util.function.Function;
*/
public final class PriceItem implements Price {
/**
* The base MathContext.
* The base PlaceholderContext.
*/
private final MathContext baseContext;
private final PlaceholderContext baseContext;
/**
* The amount of items.
*/
private final Function<MathContext, Double> function;
private final PlaceholderContextSupplier<Double> function;
/**
* The item.
@@ -47,7 +48,23 @@ public final class PriceItem implements Price {
*/
public PriceItem(final int amount,
@NotNull final TestableItem item) {
this(MathContext.EMPTY, ctx -> (double) amount, item);
this(PlaceholderContext.EMPTY, (PlaceholderContext ctx) -> (double) amount, item);
}
/**
* Create a new item-based price.
*
* @param baseContext The base MathContext.
* @param function The function to get the amount of items to remove.
* @param item The item.
* @deprecated Use {@link #PriceItem(PlaceholderContext, PlaceholderContextSupplier, TestableItem)} instead.
*/
@Deprecated(since = "6.56.0", forRemoval = true)
@SuppressWarnings("removal")
public PriceItem(@NotNull final com.willfp.eco.core.math.MathContext baseContext,
@NotNull final Function<com.willfp.eco.core.math.MathContext, Double> function,
@NotNull final TestableItem item) {
this(baseContext.toPlaceholderContext(), (PlaceholderContext ctx) -> function.apply(ctx.toMathContext()), item);
}
/**
@@ -57,8 +74,8 @@ public final class PriceItem implements Price {
* @param function The function to get the amount of items to remove.
* @param item The item.
*/
public PriceItem(@NotNull final MathContext baseContext,
@NotNull final Function<MathContext, Double> function,
public PriceItem(@NotNull final PlaceholderContext baseContext,
@NotNull final PlaceholderContextSupplier<Double> function,
@NotNull final TestableItem item) {
this.baseContext = baseContext;
this.function = function;
@@ -137,7 +154,7 @@ public final class PriceItem implements Price {
public double getValue(@NotNull final Player player,
final double multiplier) {
return Math.toIntExact(Math.round(
this.function.apply(MathContext.copyWithPlayer(baseContext, player))
this.function.get(baseContext.copyWithPlayer(player))
* getMultiplier(player) * multiplier
));
}

View File

@@ -9,7 +9,6 @@ import com.willfp.eco.core.items.Items;
import com.willfp.eco.core.recipe.recipes.CraftingRecipe;
import com.willfp.eco.core.recipe.recipes.ShapedCraftingRecipe;
import com.willfp.eco.util.NamespacedKeyUtils;
import org.bukkit.Bukkit;
import org.bukkit.NamespacedKey;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
@@ -127,8 +126,8 @@ public final class Recipes {
}
if (builder.isAir()) {
Bukkit.getLogger().warning("RECIPE ERROR! " + plugin.getName() + ":" + key + " consists only");
Bukkit.getLogger().warning("of air or invalid items! Please change that or disable this recipe.");
plugin.getLogger().warning("Crafting recipe " + plugin.getID() + ":" + key + " consists only");
plugin.getLogger().warning("of air or invalid items! It will not be registered.");
return null;
}

View File

@@ -5,6 +5,8 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
@@ -14,7 +16,7 @@ import java.util.regex.Pattern;
*
* @param <T> The type of {@link Registrable}.
*/
public class Registry<T extends Registrable> {
public class Registry<T extends Registrable> implements Iterable<T> {
/**
* The ID pattern.
*/
@@ -25,6 +27,17 @@ public class Registry<T extends Registrable> {
*/
private final Map<String, T> registry = new HashMap<>();
/**
* If the registry is locked.
*/
private boolean isLocked = false;
/**
* The locker, used to 'secure' registries and prevent random unlocking.
*/
@Nullable
private Object locker = null;
/**
* Instantiate a new registry.
*/
@@ -40,6 +53,10 @@ public class Registry<T extends Registrable> {
*/
@NotNull
public T register(@NotNull final T element) {
if (this.isLocked) {
throw new IllegalStateException("Cannot add to locked registry! (ID: " + element.getID() + ")");
}
Validate.isTrue(ID_PATTERN.matcher(element.getID()).matches(), "ID must match pattern: " + ID_PATTERN.pattern() + " (was " + element.getID() + ")");
registry.put(element.getID(), element);
@@ -56,6 +73,10 @@ public class Registry<T extends Registrable> {
* @return The element.
*/
public T remove(@NotNull final T element) {
if (this.isLocked) {
throw new IllegalStateException("Cannot remove from locked registry! (ID: " + element.getID() + ")");
}
element.onRemove();
registry.remove(element.getID());
@@ -71,6 +92,10 @@ public class Registry<T extends Registrable> {
*/
@Nullable
public T remove(@NotNull final String id) {
if (this.isLocked) {
throw new IllegalStateException("Cannot remove from locked registry! (ID: " + id + ")");
}
T element = registry.get(id);
if (element != null) {
@@ -109,6 +134,67 @@ public class Registry<T extends Registrable> {
return Set.copyOf(registry.values());
}
/**
* Get if the registry is locked.
*
* @return If the registry is locked.
*/
public boolean isLocked() {
return isLocked;
}
/**
* Lock the registry.
*
* @param locker The locker.
*/
public void lock(@Nullable final Object locker) {
if (this.isLocked && this.locker != locker) {
throw new IllegalArgumentException("Registry is already locked with a different locker!");
}
this.locker = locker;
isLocked = true;
}
/**
* Unlock the registry.
*
* @param locker The locker.
*/
public void unlock(@Nullable final Object locker) {
if (this.locker != locker) {
throw new IllegalArgumentException("Cannot unlock registry!");
}
this.locker = null;
isLocked = false;
}
/**
* Get if the registry is empty.
*
* @return If the registry is empty.
*/
public boolean isEmpty() {
return registry.isEmpty();
}
/**
* Get if the registry is not empty.
*
* @return If the registry is not empty.
*/
public boolean isNotEmpty() {
return !isEmpty();
}
@NotNull
@Override
public Iterator<T> iterator() {
return values().iterator();
}
/**
* Try to fit a string to the ID pattern.
*
@@ -120,6 +206,6 @@ public class Registry<T extends Registrable> {
return string.replace(" ", "_")
.replace(".", "_")
.replace("-", "_")
.toLowerCase();
.toLowerCase(Locale.ENGLISH);
}
}

View File

@@ -0,0 +1,17 @@
package com.willfp.eco.core.version;
import org.jetbrains.annotations.NotNull;
/**
* An error thrown when eco is outdated.
*/
public class OutdatedEcoVersionError extends Error {
/**
* Create a new OutdatedEcoVersionError.
*
* @param message The message.
*/
public OutdatedEcoVersionError(@NotNull final String message) {
super(message);
}
}

View File

@@ -0,0 +1,53 @@
package com.willfp.eco.core.version;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.jetbrains.annotations.NotNull;
/**
* A minimal class to represent a version, uses DefaultArtifactVersion under the hood.
* <p>
* This class exists to resolve issues where 1.17 doesn't include DefaultArtifactVersion.
*/
public class Version implements Comparable<Version> {
/**
* The version.
*/
private final DefaultArtifactVersion version;
/**
* Create a new version.
*
* @param version The version.
*/
public Version(@NotNull final String version) {
this.version = new DefaultArtifactVersion(version);
}
@Override
public int compareTo(@NotNull final Version o) {
return this.version.compareTo(o.version);
}
@Override
public String toString() {
return this.version.toString();
}
@Override
public boolean equals(@NotNull final Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof Version other)) {
return false;
}
return this.version.equals(other.version);
}
@Override
public int hashCode() {
return this.version.hashCode();
}
}

View File

@@ -8,42 +8,15 @@ import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Set;
/**
* Utilities / API methods for blocks.
*/
public final class BlockUtils {
/**
* Max blocks to mine (yes, this is to prevent a stack overflow).
*/
private static final int MAX_BLOCKS = 2500;
private static Set<Block> getNearbyBlocks(@NotNull final Block start,
@NotNull final List<Material> allowedMaterials,
@NotNull final Set<Block> blocks,
final int limit) {
for (BlockFace face : BlockFace.values()) {
Block block = start.getRelative(face);
if (blocks.contains(block)) {
continue;
}
if (allowedMaterials.contains(block.getType())) {
blocks.add(block);
if (blocks.size() > limit || blocks.size() > MAX_BLOCKS) {
return blocks;
}
blocks.addAll(getNearbyBlocks(block, allowedMaterials, blocks, limit));
}
}
return blocks;
}
/**
* Get a set of all blocks in contact with each other of a specific type.
*
@@ -56,7 +29,32 @@ public final class BlockUtils {
public static Set<Block> getVein(@NotNull final Block start,
@NotNull final List<Material> allowedMaterials,
final int limit) {
return getNearbyBlocks(start, allowedMaterials, new HashSet<>(), limit);
Set<Block> blocks = new HashSet<>();
Queue<Block> toProcess = new LinkedList<>();
if (allowedMaterials.contains(start.getType())) {
toProcess.add(start);
}
while (!toProcess.isEmpty() && blocks.size() < limit) {
Block currentBlock = toProcess.poll();
if (blocks.contains(currentBlock)) {
continue;
}
blocks.add(currentBlock);
for (BlockFace face : BlockFace.values()) {
Block adjacentBlock = currentBlock.getRelative(face);
if (!blocks.contains(adjacentBlock) && allowedMaterials.contains(adjacentBlock.getType())) {
toProcess.add(adjacentBlock);
}
}
}
return blocks;
}
/**

View File

@@ -1,10 +1,9 @@
package com.willfp.eco.util;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager;
import com.willfp.eco.core.math.MathContext;
import com.willfp.eco.core.placeholder.AdditionalPlayer;
import com.willfp.eco.core.placeholder.PlaceholderInjectable;
import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -210,7 +209,7 @@ public final class NumberUtils {
* @return The value of the expression, or zero if invalid.
*/
public static double evaluateExpression(@NotNull final String expression) {
return evaluateExpression(expression, MathContext.EMPTY);
return evaluateExpression(expression, PlaceholderContext.EMPTY);
}
/**
@@ -222,7 +221,7 @@ public final class NumberUtils {
*/
public static double evaluateExpression(@NotNull final String expression,
@Nullable final Player player) {
return evaluateExpression(expression, player, PlaceholderManager.EMPTY_INJECTABLE);
return evaluateExpression(expression, player, null);
}
/**
@@ -230,12 +229,12 @@ public final class NumberUtils {
*
* @param expression The expression.
* @param player The player.
* @param context The injectable placeholders.
* @param context The injectableContext 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 PlaceholderInjectable context) {
@Nullable final PlaceholderInjectable context) {
return evaluateExpression(expression, player, context, new ArrayList<>());
}
@@ -244,30 +243,46 @@ public final class NumberUtils {
*
* @param expression The expression.
* @param player The player.
* @param context The injectable placeholders.
* @param context The injectableContext placeholders.
* @param additionalPlayers Additional players to parse placeholders for.
* @return The value of the expression, or zero if invalid.
*/
public static double evaluateExpression(@NotNull final String expression,
@Nullable final Player player,
@NotNull final PlaceholderInjectable context,
@Nullable final PlaceholderInjectable context,
@NotNull final Collection<AdditionalPlayer> additionalPlayers) {
return Eco.get().evaluate(expression, new MathContext(
context,
return Eco.get().evaluate(expression, new PlaceholderContext(
player,
null,
context,
additionalPlayers
));
}
/**
* Evaluate an expression with respect to a player (for placeholders).
* Evaluate an expression in a context.
*
* @param expression The expression.
* @param context The math context.
* @param context The context.
* @return The value of the expression, or zero if invalid.
* @deprecated Use {@link #evaluateExpression(String, PlaceholderContext)} instead.
*/
@Deprecated(since = "6.56.0", forRemoval = true)
@SuppressWarnings({"removal", "DeprecatedIsStillUsed"})
public static double evaluateExpression(@NotNull final String expression,
@NotNull final com.willfp.eco.core.math.MathContext context) {
return evaluateExpression(expression, context.toPlaceholderContext());
}
/**
* Evaluate an expression in a context.
*
* @param expression The expression.
* @param context The context.
* @return The value of the expression, or zero if invalid.
*/
public static double evaluateExpression(@NotNull final String expression,
@NotNull final MathContext context) {
@NotNull final PlaceholderContext context) {
return Eco.get().evaluate(expression, context);
}

View File

@@ -8,7 +8,10 @@ import com.google.common.collect.ImmutableMap;
import com.google.gson.JsonSyntaxException;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager;
import com.willfp.eco.core.placeholder.context.PlaceholderContext;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.JoinConfiguration;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.TextDecoration;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
@@ -200,6 +203,26 @@ public final class StringUtils {
return translated;
}
/**
* Format a list of strings.
* <p>
* Coverts color codes and placeholders.
*
* @param list The messages to format.
* @param context The context.
* @return The message, format.
*/
@NotNull
public static List<String> formatList(@NotNull final List<String> list,
@NotNull PlaceholderContext context) {
List<String> translated = new ArrayList<>();
for (String string : list) {
translated.add(format(string, context));
}
return translated;
}
/**
* Format a string.
* <p>
@@ -242,7 +265,7 @@ public final class StringUtils {
@NotNull
public static String format(@NotNull final String message,
@NotNull final FormatOption option) {
return format(message, null, option);
return format(message, (Player) null, option);
}
/**
@@ -287,7 +310,7 @@ public final class StringUtils {
@NotNull
public static Component formatToComponent(@NotNull final String message,
@NotNull final FormatOption option) {
return formatToComponent(message, null, option);
return formatToComponent(message, (Player) null, option);
}
/**
@@ -321,10 +344,49 @@ public final class StringUtils {
public static String format(@NotNull final String message,
@Nullable final Player player,
@NotNull final FormatOption option) {
String processedMessage = message;
if (option == FormatOption.WITH_PLACEHOLDERS) {
processedMessage = PlaceholderManager.translatePlaceholders(processedMessage, player);
return format(
message,
new PlaceholderContext(player)
);
}
return STRING_FORMAT_CACHE.get(message);
}
/**
* Format a string to a component.
* <p>
* Converts color codes and placeholders if specified.
*
* @param message The message to translate.
* @param context The placeholder context.
* @return The message, formatted, as a component.
* @see StringUtils#format(String, Player)
*/
@NotNull
public static Component formatToComponent(@NotNull final String message,
@NotNull final PlaceholderContext context) {
return toComponent(format(message, context));
}
/**
* Format a string.
* <p>
* Coverts color codes and placeholders if specified.
*
* @param message The message to format.
* @param context The context to translate placeholders with respect to.
* @return The message, formatted.
*/
@NotNull
public static String format(@NotNull final String message,
@NotNull final PlaceholderContext context) {
String processedMessage = message;
processedMessage = PlaceholderManager.translatePlaceholders(
processedMessage,
context
);
return STRING_FORMAT_CACHE.get(processedMessage);
}
@@ -674,6 +736,177 @@ public final class StringUtils {
return builder.toString();
}
/**
* Fast implementation of {@link String#replace(CharSequence, CharSequence)}
*
* @param input The input string.
* @param target The target string.
* @param replacement The replacement string.
* @return The replaced string.
*/
@NotNull
public static String replaceQuickly(@NotNull final String input,
@NotNull final String target,
@NotNull final String replacement) {
int targetLength = target.length();
// Count the number of original occurrences
int count = 0;
for (
int index = input.indexOf(target);
index != -1;
index = input.indexOf(target, index + targetLength)
) {
count++;
}
if (count == 0) {
return input;
}
int replacementLength = replacement.length();
int inputLength = input.length();
// Pre-calculate the final size of the StringBuilder
int newSize = inputLength + (replacementLength - targetLength) * count;
StringBuilder result = new StringBuilder(newSize);
int start = 0;
for (
int index = input.indexOf(target);
index != -1;
index = input.indexOf(target, start)
) {
result.append(input, start, index);
result.append(replacement);
start = index + targetLength;
}
result.append(input, start, inputLength);
return result.toString();
}
/**
* Line wrap a list of strings while preserving formatting.
*
* @param input The input list.
* @param lineLength The length of each line.
* @return The wrapped list.
*/
@NotNull
public static List<String> lineWrap(@NotNull final List<String> input,
final int lineLength) {
return lineWrap(input, lineLength, true);
}
/**
* Line wrap a list of strings while preserving formatting.
*
* @param input The input list.
* @param lineLength The length of each line.
* @param preserveMargin If the string has a margin, add it to the next line.
* @return The wrapped list.
*/
@NotNull
public static List<String> lineWrap(@NotNull final List<String> input,
final int lineLength,
final boolean preserveMargin) {
return input.stream()
.flatMap(line -> lineWrap(line, lineLength, preserveMargin).stream())
.toList();
}
/**
* Line wrap a string while preserving formatting.
*
* @param input The input list.
* @param lineLength The length of each line.
* @return The wrapped list.
*/
@NotNull
public static List<String> lineWrap(@NotNull final String input,
final int lineLength) {
return lineWrap(input, lineLength, true);
}
/**
* Line wrap a string while preserving formatting.
*
* @param input The input string.
* @param lineLength The length of each line.
* @param preserveMargin If the string has a margin, add it to the start of each line.
* @return The wrapped string.
*/
@NotNull
public static List<String> lineWrap(@NotNull final String input,
final int lineLength,
final boolean preserveMargin) {
int margin = preserveMargin ? getMargin(input) : 0;
TextComponent space = Component.text(" ");
Component asComponent = toComponent(input);
// The component contains the text as its children, so the child components
// are accessed like this:
List<TextComponent> children = new ArrayList<>();
if (asComponent instanceof TextComponent) {
children.add((TextComponent) asComponent);
}
for (Component child : asComponent.children()) {
children.add((TextComponent) child);
}
// Start by splitting the component into individual characters.
List<TextComponent> letters = new ArrayList<>();
for (TextComponent child : children) {
for (char c : child.content().toCharArray()) {
letters.add(Component.text(c).mergeStyle(child));
}
}
List<Component> lines = new ArrayList<>();
List<TextComponent> currentLine = new ArrayList<>();
boolean isFirstLine = true;
for (TextComponent letter : letters) {
if (currentLine.size() > lineLength && letter.content().isBlank()) {
lines.add(Component.join(JoinConfiguration.noSeparators(), currentLine));
currentLine.clear();
isFirstLine = false;
} else {
// Add margin if starting a new line.
if (currentLine.isEmpty() && !isFirstLine) {
if (preserveMargin) {
for (int i = 0; i < margin; i++) {
currentLine.add(space);
}
}
}
currentLine.add(letter);
}
}
// Push last line.
lines.add(Component.join(JoinConfiguration.noSeparators(), currentLine));
// Convert back to legacy strings.
return lines.stream().map(StringUtils::toLegacy)
.collect(Collectors.toList());
}
/**
* Get a string's margin.
*
* @param input The input string.
* @return The margin.
*/
public static int getMargin(@NotNull final String input) {
return input.indexOf(input.trim());
}
/**
* Options for formatting.
*/

View File

@@ -8,7 +8,7 @@ package com.willfp.eco.core.map
* @see ListMap
*/
@Suppress("RedundantOverride")
class MutableListMap<K : Any, V : Any> : ListMap<K, V>() {
class MutableListMap<K : Any, V> : ListMap<K, V>() {
/**
* Override with enforced MutableList type.
*/
@@ -18,7 +18,7 @@ class MutableListMap<K : Any, V : Any> : ListMap<K, V>() {
/**
* Override with enforced MutableList type.
*/
override fun getOrDefault(key: K, defaultValue: MutableList<V>?): MutableList<V> {
override fun getOrDefault(key: K, defaultValue: MutableList<V>): MutableList<V> {
return super.getOrDefault(key, defaultValue)
}
}
@@ -29,6 +29,12 @@ class MutableListMap<K : Any, V : Any> : ListMap<K, V>() {
fun <K : Any, V : Any> defaultMap(defaultValue: V) =
DefaultMap<K, V>(defaultValue)
/**
* @see DefaultMap
*/
fun <K : Any, V : Any> defaultMap(defaultValue: () -> V) =
DefaultMap<K, V>(defaultValue())
/**
* @see ListMap
*/
@@ -38,11 +44,13 @@ fun <K : Any, V : Any> listMap() =
/**
* @see DefaultMap.createNestedMap
*/
fun <K : Any, K1 : Any, V : Any> nestedMap() =
fun <K : Any, K1 : Any, V> nestedMap() =
DefaultMap.createNestedMap<K, K1, V>()
/**
* @see DefaultMap.createNestedListMap
*/
fun <K : Any, K1 : Any, V : Any> nestedListMap() =
DefaultMap<K, MutableListMap<K1, V>>(MutableListMap())
fun <K : Any, K1 : Any, V> nestedListMap() =
DefaultMap<K, MutableListMap<K1, V>>() {
MutableListMap()
}

View File

@@ -0,0 +1,25 @@
@file:JvmName("PlaceholderContextExtensions")
package com.willfp.eco.core.placeholder.context
import com.willfp.eco.core.placeholder.AdditionalPlayer
import com.willfp.eco.core.placeholder.PlaceholderInjectable
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
/** @see PlaceholderContext */
@JvmOverloads
fun placeholderContext(
player: Player? = null,
item: ItemStack? = null,
injectable: PlaceholderInjectable? = null,
additionalPlayers: Collection<AdditionalPlayer> = emptyList()
): PlaceholderContext = PlaceholderContext(player, item, injectable, additionalPlayers)
/** @see PlaceholderContext */
fun PlaceholderContext.copy(
player: Player? = this.player,
item: ItemStack? = this.itemStack,
injectable: PlaceholderInjectable? = this.injectableContext,
additionalPlayers: Collection<AdditionalPlayer> = this.additionalPlayers
): PlaceholderContext = PlaceholderContext(player, item, injectable, additionalPlayers)

View File

@@ -0,0 +1,13 @@
package com.willfp.eco.core.registry
/**
* A registrable that has a string ID, for use with Kotlin.
*/
interface KRegistrable : Registrable {
/**
* The ID of the registrable.
*/
val id: String
override fun getID() = id
}

View File

@@ -15,5 +15,5 @@ fun <T> create2DList(rows: Int, columns: Int): MutableList<MutableList<T>> =
ListUtils.create2DList(rows, columns)
/** @see ListUtils.toSingletonList */
fun <T> T.toSingletonList(): List<T> =
fun <T> T?.toSingletonList(): List<T> =
ListUtils.toSingletonList(this)

View File

@@ -2,6 +2,32 @@
package com.willfp.eco.util
import com.willfp.eco.core.placeholder.context.PlaceholderContext
/** @see NumberUtils.toNumeral */
fun Number.toNumeral(): String =
NumberUtils.toNumeral(this.toInt())
/** @see NumberUtils.fromNumeral */
fun String.parseNumeral(): Int =
NumberUtils.fromNumeral(this)
/** @see NumberUtils.randInt */
fun randInt(min: Int, max: Int) =
NumberUtils.randInt(min, max)
/** @see NumberUtils.randFloat */
fun randDouble(min: Double, max: Double) =
NumberUtils.randFloat(min, max)
/** @see NumberUtils.randFloat */
fun randFloat(min: Float, max: Float) =
NumberUtils.randFloat(min.toDouble(), max.toDouble()).toFloat()
/** @see NumberUtils.evaluateExpression */
fun evaluateExpression(expression: String) =
NumberUtils.evaluateExpression(expression)
/** @see NumberUtils.evaluateExpression */
fun evaluateExpression(expression: String, context: PlaceholderContext) =
NumberUtils.evaluateExpression(expression, context)

View File

@@ -2,6 +2,7 @@
package com.willfp.eco.util
import com.willfp.eco.core.placeholder.context.PlaceholderContext
import net.kyori.adventure.text.Component
import org.bukkit.entity.Player
@@ -31,6 +32,14 @@ fun String.formatEco(
if (formatPlaceholders) StringUtils.FormatOption.WITH_PLACEHOLDERS else StringUtils.FormatOption.WITHOUT_PLACEHOLDERS
)
/** @see StringUtils.format */
fun String.formatEco(
context: PlaceholderContext
) = StringUtils.format(
this,
context
)
/** @see StringUtils.formatList */
fun List<String>.formatEco(
player: Player? = null,
@@ -41,6 +50,14 @@ fun List<String>.formatEco(
if (formatPlaceholders) StringUtils.FormatOption.WITH_PLACEHOLDERS else StringUtils.FormatOption.WITHOUT_PLACEHOLDERS
)
/** @see StringUtils.formatList */
fun List<String>.formatEco(
context: PlaceholderContext
): List<String> = StringUtils.formatList(
this,
context
)
/** @see StringUtils.splitAround */
fun String.splitAround(separator: String): Array<String> =
StringUtils.splitAround(this, separator)
@@ -48,3 +65,19 @@ fun String.splitAround(separator: String): Array<String> =
/** @see StringUtils.toNiceString */
fun Any?.toNiceString(): String =
StringUtils.toNiceString(this)
/** @see StringUtils.replaceQuickly */
fun String.replaceQuickly(target: String, replacement: String): String =
StringUtils.replaceQuickly(this, target, replacement)
/** @see StringUtils.lineWrap */
fun String.lineWrap(width: Int, preserveMargin: Boolean = true): List<String> =
StringUtils.lineWrap(this, width, preserveMargin)
/** @see StringUtils.lineWrap */
fun List<String>.lineWrap(width: Int, preserveMargin: Boolean = true): List<String> =
StringUtils.lineWrap(this, width, preserveMargin)
/** @see StringUtils.getMargin */
val String.margin: Int
get() = StringUtils.getMargin(this)

View File

@@ -8,10 +8,12 @@ import org.bukkit.command.TabCompleter
class DelegatedBukkitCommand(
private val delegate: EcoPluginCommand
) : Command(delegate.name), TabCompleter, PluginIdentifiableCommand {
private var _aliases: List<String>? = null
private var _description: String? = null
) : Command(
delegate.name,
delegate.description ?: "",
"/${delegate.name}",
delegate.aliases
), TabCompleter, PluginIdentifiableCommand {
override fun execute(sender: CommandSender, label: String, args: Array<out String>?): Boolean {
return delegate.onCommand(sender, this, label, args)
}
@@ -36,16 +38,4 @@ class DelegatedBukkitCommand(
override fun getPlugin() = delegate.plugin
override fun getPermission() = delegate.permission
override fun getDescription() = _description ?: delegate.description ?: ""
override fun getAliases(): List<String> = _aliases ?: delegate.aliases
override fun setDescription(description: String): Command {
this._description = description
return this
}
override fun setAliases(aliases: List<String>): Command {
this._aliases = aliases
return this
}
}

View File

@@ -7,7 +7,7 @@ import com.willfp.eco.core.command.PluginCommandBase
import org.bukkit.Bukkit
class EcoPluginCommand(
parentDelegate: CommandBase,
private val parentDelegate: PluginCommandBase,
plugin: EcoPlugin,
name: String,
permission: String,
@@ -39,6 +39,9 @@ class EcoPluginCommand(
Eco.get().syncCommands()
}
override fun getAliases(): List<String> = parentDelegate.aliases
override fun getDescription(): String? = parentDelegate.description
}
class EcoSubcommand(

View File

@@ -3,7 +3,8 @@ 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.InjectablePlaceholder
import com.willfp.eco.core.placeholder.StaticPlaceholder
import com.willfp.eco.core.placeholder.context.PlaceholderContext
import com.willfp.eco.internal.fast.listView
import com.willfp.eco.util.StringUtils
import org.bukkit.configuration.file.YamlConfiguration
import java.util.concurrent.ConcurrentHashMap
@@ -15,7 +16,7 @@ open class EcoConfig(
private val values = ConcurrentHashMap<String, Any?>()
@Transient
var injections = mutableListOf<InjectablePlaceholder>()
var injections = ConcurrentHashMap<String, InjectablePlaceholder>()
fun init(values: Map<String, Any?>) {
this.values.clear()
@@ -104,12 +105,12 @@ open class EcoConfig(
}
override fun getSubsectionOrNull(path: String): Config? {
return (get(path) as? Config)?.apply { this.addInjectablePlaceholder(injections) }
return (get(path) as? Config)?.apply { this.addInjectablePlaceholder(injections.values) }
}
override fun getSubsectionsOrNull(path: String): List<Config>? {
return getList<Config>(path)
?.map { it.apply { this.addInjectablePlaceholder(injections) } }
?.map { it.apply { this.addInjectablePlaceholder(injections.values) } }
?.toList()
}
@@ -141,9 +142,7 @@ open class EcoConfig(
var string = get(path)?.toString() ?: return null
if (format && option == StringUtils.FormatOption.WITH_PLACEHOLDERS) {
for (injection in placeholderInjections) {
if (injection is StaticPlaceholder) {
string = string.replace("%${injection.identifier}%", injection.value)
}
string = injection.tryTranslateQuickly(string, PlaceholderContext.EMPTY)
}
}
return if (format) StringUtils.format(string, option) else string
@@ -161,9 +160,7 @@ open class EcoConfig(
strings.replaceAll {
var string = it
for (injection in placeholderInjections) {
if (injection is StaticPlaceholder) {
string = string.replace("%${injection.identifier}%", injection.value)
}
string = injection.tryTranslateQuickly(string, PlaceholderContext.EMPTY)
}
string
}
@@ -180,12 +177,13 @@ open class EcoConfig(
}
override fun addInjectablePlaceholder(placeholders: Iterable<InjectablePlaceholder>) {
injections.removeIf { placeholders.any { placeholder -> it.identifier == placeholder.identifier } }
injections.addAll(placeholders)
for (placeholder in placeholders) {
injections[placeholder.pattern.pattern()] = placeholder
}
}
override fun getPlaceholderInjections(): List<InjectablePlaceholder> {
return injections.toList()
return injections.values.listView() // Faster than .toList()
}
override fun clearInjectedPlaceholders() {
@@ -208,14 +206,6 @@ open class EcoConfig(
return bukkit
}
override fun clone(): Config {
return EcoConfigSection(type, this.values.toMutableMap(), injections)
}
override fun toString(): String {
return this.toPlaintext()
}
private inline fun <reified T> getList(path: String): List<T>? {
val asIterable = get(path) as? Iterable<*> ?: return null
val asList = asIterable.toList()
@@ -226,4 +216,44 @@ open class EcoConfig(
return asList as List<T>
}
override fun clone(): Config {
return EcoConfigSection(type, this.values.toMutableMap(), injections)
}
override fun toString(): String {
return this.toPlaintext()
}
override fun equals(other: Any?): Boolean {
if (this === other) {
return true
}
if (other !is EcoConfig) {
return false
}
// Hey! Don't care. This works.
return this.hashCode() == other.hashCode()
}
override fun hashCode(): Int {
/*
The keys are completely redundant, as they are only used to prevent
duplicate keys in the map. Therefore, we can ignore them and just
hash the actual placeholder values.
*/
var injectionHash = 0
injections.forEachValue(5) {
injectionHash = injectionHash xor (it.hashCode() shl 5)
}
// hashCode() has to compute extremely quickly, so we're using bitwise, because why not?
// Fucking filthy to use identityHashCode here, but it should be extremely fast
val identityHash = System.identityHashCode(this)
return (identityHash shl 5) - (identityHash xor configType.hashCode()) + injectionHash
}
}

View File

@@ -2,14 +2,15 @@ package com.willfp.eco.internal.config
import com.willfp.eco.core.config.ConfigType
import com.willfp.eco.core.placeholder.InjectablePlaceholder
import java.util.concurrent.ConcurrentHashMap
class EcoConfigSection(
type: ConfigType,
values: Map<String, Any?> = emptyMap(),
injections: Collection<InjectablePlaceholder> = emptyList()
injections: Map<String, InjectablePlaceholder> = emptyMap()
) : EcoConfig(type) {
init {
this.init(values)
this.injections = injections.toMutableList()
this.injections = ConcurrentHashMap(injections)
}
}

View File

@@ -0,0 +1,27 @@
package com.willfp.eco.internal.data
import com.willfp.eco.core.data.ExternalDataStoreObjectAdapter
import com.willfp.eco.core.version.Version
object VersionToStringAdapter: ExternalDataStoreObjectAdapter<Version, String>(
Version::class.java,
String::class.java
) {
override fun toAccessedObject(obj: String): Version = Version(obj)
override fun toStoredObject(obj: Version): String = obj.toString()
}
class MavenVersionToStringAdapter(
className: String
): ExternalDataStoreObjectAdapter<Any, String>(
Class.forName(className),
String::class.java
) {
private val constructor = Class.forName(className)
.getConstructor(String::class.java)
override fun toAccessedObject(obj: String): Any = constructor.newInstance(obj)
override fun toStoredObject(obj: Any): String = obj.toString()
}

View File

@@ -5,9 +5,11 @@ import com.willfp.eco.core.Eco
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.config.toConfig
import com.willfp.eco.core.extensions.Extension
import com.willfp.eco.core.extensions.ExtensionLoadException
import com.willfp.eco.core.extensions.ExtensionLoader
import com.willfp.eco.core.extensions.ExtensionMetadata
import com.willfp.eco.core.extensions.MalformedExtensionException
import com.willfp.eco.core.version.Version
import org.bukkit.configuration.file.YamlConfiguration
import java.io.File
import java.io.InputStreamReader
@@ -32,7 +34,7 @@ class EcoExtensionLoader(
}
runCatching { loadExtension(extensionJar) }.onFailure {
this.plugin.logger.warning(extensionJar.name + " caused an error!")
this.plugin.logger.warning(extensionJar.name + " caused an error: ${it.message ?: "Unknown error"}")
if (Eco.get().ecoPlugin.configYml.getBool("log-full-extension-errors")) {
it.printStackTrace()
}
@@ -54,7 +56,12 @@ class EcoExtensionLoader(
var name = extensionYml.getStringOrNull("name")
var version = extensionYml.getStringOrNull("version")
var author = extensionYml.getStringOrNull("author")
val pluginVersion = Version(extensionYml.getStringOrNull("plugin-version") ?: "0.0.0")
val pluginName = extensionYml.getStringOrNull("plugin")
if (pluginName != null && !pluginName.equals(this.plugin.description.name, ignoreCase = true)) {
throw ExtensionLoadException("${extensionJar.name} is only compatible with $pluginName!")
}
if (mainClass == null) {
throw MalformedExtensionException("Invalid extension.yml found in " + extensionJar.name)
@@ -75,7 +82,11 @@ class EcoExtensionLoader(
author = "Unnamed Author"
}
val metadata = ExtensionMetadata(version, name, author)
if (Version(this.plugin.description.version) < pluginVersion) {
throw ExtensionLoadException("Plugin version is too low for ${extensionJar.name}!")
}
val metadata = ExtensionMetadata(version, name, author, extensionJar, pluginVersion)
val cls: Class<*> = classLoader.loadClass(mainClass)
val extension: Extension = cls.getConstructor(EcoPlugin::class.java).newInstance(this.plugin) as Extension
@@ -85,7 +96,7 @@ class EcoExtensionLoader(
extensions[extension] = classLoader
}
override fun getLoadedExtensions(): MutableSet<Extension> {
override fun getLoadedExtensions(): Set<Extension> {
return ImmutableSet.copyOf(extensions.keys)
}
@@ -97,4 +108,4 @@ class EcoExtensionLoader(
extensions.clear()
}
}
}

View File

@@ -0,0 +1,48 @@
package com.willfp.eco.internal.fast
class ListViewOfCollection<T>(private val collection: Collection<T>) : List<T> {
/*
The backing list is lazy-loaded because it provides a performance hit
to copy the contents of the contents of the collection into a list.
Since the only required operator for most use-cases is .iterator(),
we can just use the collection's iterator.
*/
private val backingList: List<T> by lazy { collection.toList() }
override val size: Int
get() = collection.size
override fun containsAll(elements: Collection<T>) =
collection.containsAll(elements)
override fun get(index: Int): T =
backingList[index]
override fun indexOf(element: T): Int = backingList.indexOf(element)
override fun contains(element: T): Boolean = collection.contains(element)
override fun isEmpty() =
collection.isEmpty()
override fun iterator() =
collection.iterator()
override fun listIterator() =
backingList.listIterator()
override fun listIterator(index: Int) =
backingList.listIterator(index)
override fun subList(fromIndex: Int, toIndex: Int) =
backingList.subList(fromIndex, toIndex)
override fun lastIndexOf(element: T) =
backingList.lastIndexOf(element)
}
inline fun <reified T> Collection<T>.listView(): List<T> {
return ListViewOfCollection(this)
}

View File

@@ -34,4 +34,6 @@ open class EcoSlot(
}
override fun getActionableSlot(player: Player, menu: Menu): EcoSlot = this
override fun shouldRenderOnClick() = handlers.values.any { it.isNotEmpty() }
}

View File

@@ -6,6 +6,7 @@ import com.willfp.eco.core.gui.slot.functional.CaptiveFilter
import com.willfp.eco.core.gui.slot.functional.SlotHandler
import com.willfp.eco.core.gui.slot.functional.SlotProvider
import com.willfp.eco.core.gui.slot.functional.SlotUpdater
import com.willfp.eco.core.map.listMap
import org.bukkit.entity.Player
import org.bukkit.event.inventory.ClickType
import java.util.function.Predicate
@@ -15,14 +16,14 @@ class EcoSlotBuilder(private val provider: SlotProvider) : SlotBuilder {
private var captiveFromEmpty = false
private var updater: SlotUpdater = SlotUpdater { player, menu, _ -> provider.provide(player, menu) }
private val handlers = mutableMapOf<ClickType, MutableList<SlotHandler>>()
private val handlers = listMap<ClickType, SlotHandler>()
private var captiveFilter =
CaptiveFilter { _, _, _ -> true }
private var notCaptiveFor: (Player) -> Boolean = { _ -> false}
override fun onClick(type: ClickType, action: SlotHandler): SlotBuilder {
handlers.computeIfAbsent(type) { mutableListOf() } += action
handlers[type] += action
return this
}

View File

@@ -2,6 +2,7 @@ package com.willfp.eco.internal.integrations
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager
import com.willfp.eco.core.placeholder.context.placeholderContext
import me.clip.placeholderapi.expansion.PlaceholderExpansion
import org.bukkit.entity.Player
@@ -33,7 +34,19 @@ class PAPIExpansion(private val plugin: EcoPlugin) : PlaceholderExpansion() {
override fun onPlaceholderRequest(
player: Player?,
identifier: String
): String {
return PlaceholderManager.getResult(player, identifier, plugin)
): String? {
return PlaceholderManager.getResult(
plugin,
identifier,
placeholderContext(
player = player,
item = player?.inventory?.itemInMainHand
)
)
}
override fun getPlaceholders(): List<String> {
return PlaceholderManager.getRegisteredPlaceholders(plugin)
.map { "%${this.plugin.name.lowercase()}_${it.pattern.pattern()}%" }
}
}

View File

@@ -0,0 +1,10 @@
package com.willfp.eco.internal.logging
import java.util.logging.LogRecord
import java.util.logging.Logger
object NOOPLogger : Logger("eco_noop", null as String?) {
override fun log(record: LogRecord?) {
return
}
}

View File

@@ -0,0 +1,44 @@
package com.willfp.eco.internal.placeholder
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager
import com.willfp.eco.core.placeholder.InjectablePlaceholder
import com.willfp.eco.core.placeholder.Placeholder
import java.util.regex.Pattern
class PlaceholderLookup(
val args: String,
val plugin: EcoPlugin?,
private val injections: Collection<InjectablePlaceholder>?
) {
fun findMatchingPlaceholder(): Placeholder? {
if (plugin != null) {
val pluginPlaceholders = PlaceholderManager.getRegisteredPlaceholders(plugin)
for (placeholder in pluginPlaceholders) {
if (placeholder.matches(this)) {
return placeholder
}
}
}
if (injections != null) {
for (placeholder in injections) {
if (placeholder.matches(this)) {
return placeholder
}
}
}
return null
}
private fun Placeholder.matches(lookup: PlaceholderLookup): Boolean {
val pattern = this.pattern
val patternString = pattern.pattern()
val patternFlags = pattern.flags()
val isLiteral = Pattern.LITERAL and patternFlags != 0
return if (isLiteral) lookup.args == patternString else pattern.matcher(lookup.args).matches()
}
}

View File

@@ -0,0 +1,208 @@
package com.willfp.eco.internal.placeholder
import com.github.benmanes.caffeine.cache.Caffeine
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager
import com.willfp.eco.core.placeholder.InjectablePlaceholder
import com.willfp.eco.core.placeholder.Placeholder
import com.willfp.eco.core.placeholder.context.PlaceholderContext
import com.willfp.eco.util.StringUtils
import java.util.Optional
import java.util.concurrent.TimeUnit
/*
A lot of methods here are centered around minimising calls to getPlaceholderInjections,
which tends to be slow for things like configs. This was optimised with ListViewOfCollection,
but it's still best to minimise the memory overhead.
*/
class PlaceholderParser {
private val placeholderRegex = Regex("%([^% ]+)%")
private val placeholderLookupCache = Caffeine.newBuilder()
.expireAfterWrite(1, TimeUnit.SECONDS)
.build<PlaceholderLookup, Optional<Placeholder>>()
fun translatePlacholders(text: String, context: PlaceholderContext): String {
return translatePlacholders(text, context, context.injectableContext.placeholderInjections)
}
private fun translatePlacholders(
text: String,
context: PlaceholderContext,
injections: Collection<InjectablePlaceholder>,
translateEcoPlaceholders: Boolean = true
): String {
/*
Why am I doing injections at the start, and again at the end?
Additional players let you use something like victim as a player to parse in relation to,
for example doing %victim_player_health%, which would parse the health of the victim.
However, something like libreforge will also inject %victim_max_health%, which is unrelated
to additional players, and instead holds a constant value. So, eco saw this, smartly thought
"ah, it's an additional player, let's parse it", and then tried to parse %max_health% with
relation to the victim, which resolved to zero. So, we have to parse statics and player statics
that might include a prefix first, then additional players, then player statics with the support
of additional players.
This was a massive headache and took so many reports before I clocked what was going on.
Oh well, at least it's fixed now.
*/
// Apply injections first
var processed = injections.fold(text) { acc, injection ->
injection.tryTranslateQuickly(acc, context)
}
// Prevent running 2 scans if there are no additional players.
if (context.additionalPlayers.isNotEmpty()) {
val found = PlaceholderManager.findPlaceholdersIn(text)
for (additionalPlayer in context.additionalPlayers) {
val prefix = "%${additionalPlayer.identifier}_"
processed = found.fold(processed) { acc, placeholder ->
if (placeholder.startsWith(prefix)) {
val newPlaceholder = "%${StringUtils.removePrefix(prefix, placeholder)}"
val translation = translatePlacholders(
newPlaceholder,
context.copyWithPlayer(additionalPlayer.player),
injections
)
acc.replace(placeholder, translation)
} else {
acc
}
}
}
}
// Translate eco placeholders
if (translateEcoPlaceholders) {
processed = translateEcoPlaceholdersIn(processed, context, injections)
}
// Apply registered integrations
processed = PlaceholderManager.getRegisteredIntegrations().fold(processed) { acc, integration ->
integration.translate(acc, context.player)
}
// Apply injections again
return injections.fold(processed) { acc, injection ->
injection.tryTranslateQuickly(acc, context)
}
}
fun getPlaceholderResult(
plugin: EcoPlugin?,
args: String,
context: PlaceholderContext
): String? {
// Only scan for injections if plugin is null.
val injections = if (plugin == null) context.injectableContext.placeholderInjections else null
return doGetResult(plugin, args, injections, context)
}
// Injections are sent separately here to prevent multiple calls to getPlaceholderInjections
private fun doGetResult(
plugin: EcoPlugin?,
args: String?,
injections: Collection<InjectablePlaceholder>?,
context: PlaceholderContext
): String? {
if (args == null) {
return null
}
val lookup = PlaceholderLookup(args, plugin, injections)
val placeholder = placeholderLookupCache.get(lookup) {
Optional.ofNullable(it.findMatchingPlaceholder())
}.orElse(null) ?: return null
return placeholder.getValue(args, context)
}
private fun translateEcoPlaceholdersIn(
text: String,
context: PlaceholderContext,
injections: Collection<InjectablePlaceholder>
): String {
val output = StringBuilder()
var lastAppendPosition = 0
for (matchResult in placeholderRegex.findAll(text)) {
val placeholder = matchResult.groups[1]?.value ?: ""
val injectableResult = doGetResult(null, placeholder, injections, context)
val parts = placeholder.split("_", limit = 2)
var result: String? = null
if (injectableResult != null) {
result = injectableResult
} else if (parts.size == 2) {
val plugin = EcoPlugin.getPlugin(parts[0])
if (plugin != null) {
result = doGetResult(plugin, parts[1], null, context)
}
}
output.append(text.substring(lastAppendPosition, matchResult.range.first))
output.append(result ?: matchResult.value)
lastAppendPosition = matchResult.range.last + 1
}
output.append(text.substring(lastAppendPosition))
return output.toString()
}
fun parseIndividualPlaceholders(strings: Collection<String>, context: PlaceholderContext): Collection<String> {
val injections = context.injectableContext.placeholderInjections
return strings.map {
parseIndividualEcoPlaceholder(it, context, injections)
?: translatePlacholders(
it,
context,
injections,
translateEcoPlaceholders = false
) // Default to slower translation
}
}
private fun parseIndividualEcoPlaceholder(
string: String,
context: PlaceholderContext,
injections: Collection<InjectablePlaceholder>
): String? {
val placeholder = string.substring(1, string.length - 1)
val injectableResult = doGetResult(null, placeholder, injections, context)
if (injectableResult != null) {
return injectableResult
}
val parts = placeholder.split("_", limit = 2)
if (parts.size == 2) {
val plugin = EcoPlugin.getPlugin(parts[0])
if (plugin != null) {
return doGetResult(plugin, parts[1], null, context)
}
}
return null
}
}

View File

@@ -1,10 +1,10 @@
package com.willfp.eco.internal.price
import com.willfp.eco.core.math.MathContext
import com.willfp.eco.core.placeholder.context.PlaceholderContext
import com.willfp.eco.core.placeholder.context.PlaceholderContextSupplier
import com.willfp.eco.core.price.Price
import com.willfp.eco.core.price.PriceFactory
import com.willfp.eco.core.price.impl.PriceEconomy
import java.util.function.Function
object PriceFactoryEconomy : PriceFactory {
override fun getNames() = listOf(
@@ -12,7 +12,7 @@ object PriceFactoryEconomy : PriceFactory {
"$"
)
override fun create(baseContext: MathContext, function: Function<MathContext, Double>): Price {
override fun create(baseContext: PlaceholderContext, function: PlaceholderContextSupplier<Double>): Price {
return PriceEconomy(baseContext, function)
}
}

View File

@@ -1,11 +1,11 @@
package com.willfp.eco.internal.price
import com.willfp.eco.core.math.MathContext
import com.willfp.eco.core.placeholder.context.PlaceholderContext
import com.willfp.eco.core.placeholder.context.PlaceholderContextSupplier
import com.willfp.eco.core.price.Price
import com.willfp.eco.core.price.PriceFactory
import org.bukkit.entity.Player
import java.util.UUID
import java.util.function.Function
import kotlin.math.roundToInt
object PriceFactoryXP : PriceFactory {
@@ -15,13 +15,13 @@ object PriceFactoryXP : PriceFactory {
"experience"
)
override fun create(baseContext: MathContext, function: Function<MathContext, Double>): Price {
return PriceXP(baseContext) { function.apply(it).roundToInt() }
override fun create(baseContext: PlaceholderContext, function: PlaceholderContextSupplier<Double>): Price {
return PriceXP(baseContext) { function.get(it).roundToInt() }
}
private class PriceXP(
private val baseContext: MathContext,
private val xp: (MathContext) -> Int
private val baseContext: PlaceholderContext,
private val xp: (PlaceholderContext) -> Int
) : Price {
private val multipliers = mutableMapOf<UUID, Double>()
@@ -37,7 +37,7 @@ object PriceFactoryXP : PriceFactory {
}
override fun getValue(player: Player, multiplier: Double): Double {
return xp(MathContext.copyWithPlayer(baseContext, player)) * getMultiplier(player) * multiplier
return xp(baseContext.copyWithPlayer(player)) * getMultiplier(player) * multiplier
}
override fun getMultiplier(player: Player): Double {

View File

@@ -1,11 +1,11 @@
package com.willfp.eco.internal.price
import com.willfp.eco.core.math.MathContext
import com.willfp.eco.core.placeholder.context.PlaceholderContext
import com.willfp.eco.core.placeholder.context.PlaceholderContextSupplier
import com.willfp.eco.core.price.Price
import com.willfp.eco.core.price.PriceFactory
import org.bukkit.entity.Player
import java.util.UUID
import java.util.function.Function
import kotlin.math.roundToInt
object PriceFactoryXPLevels : PriceFactory {
@@ -16,13 +16,13 @@ object PriceFactoryXPLevels : PriceFactory {
"explevels",
)
override fun create(baseContext: MathContext, function: Function<MathContext, Double>): Price {
return PriceXPLevel(baseContext) { function.apply(it).roundToInt() }
override fun create(baseContext: PlaceholderContext, function: PlaceholderContextSupplier<Double>): Price {
return PriceXPLevel(baseContext) { function.get(it).roundToInt() }
}
private class PriceXPLevel(
private val baseContext: MathContext,
private val level: (MathContext) -> Int
private val baseContext: PlaceholderContext,
private val level: (PlaceholderContext) -> Int
) : Price {
private val multipliers = mutableMapOf<UUID, Double>()
@@ -37,7 +37,7 @@ object PriceFactoryXPLevels : PriceFactory {
}
override fun getValue(player: Player, multiplier: Double): Double {
return level(MathContext.copyWithPlayer(baseContext, player)) * getMultiplier(player) * multiplier
return level(baseContext.copyWithPlayer(player)) * getMultiplier(player) * multiplier
}
override fun getMultiplier(player: Player): Double {

View File

@@ -45,7 +45,7 @@ class SNBTConverter : SNBTConverterProxy {
val nms = CraftItemStack.asNMSCopy(itemStack)
val nmsTag = nms.save(CompoundTag())
nmsTag.remove("Count")
return tag.copy().merge(nmsTag) == nmsTag
return tag.copy().merge(nmsTag) == nmsTag && itemStack.type == item.type
}
override fun getItem(): ItemStack = item

View File

@@ -45,7 +45,7 @@ class SNBTConverter : SNBTConverterProxy {
val nms = CraftItemStack.asNMSCopy(itemStack)
val nmsTag = nms.save(CompoundTag())
nmsTag.remove("Count")
return tag.copy().merge(nmsTag) == nmsTag
return tag.copy().merge(nmsTag) == nmsTag && itemStack.type == item.type
}
override fun getItem(): ItemStack = item

View File

@@ -45,7 +45,7 @@ class SNBTConverter : SNBTConverterProxy {
val nms = CraftItemStack.asNMSCopy(itemStack)
val nmsTag = nms.save(CompoundTag())
nmsTag.remove("Count")
return tag.copy().merge(nmsTag) == nmsTag
return tag.copy().merge(nmsTag) == nmsTag && itemStack.type == item.type
}
override fun getItem(): ItemStack = item

View File

@@ -3,6 +3,7 @@ package com.willfp.eco.internal.spigot.proxy.v1_19_R1
import com.willfp.eco.core.items.TestableItem
import com.willfp.eco.core.recipe.parts.EmptyTestableItem
import com.willfp.eco.internal.spigot.proxy.SNBTConverterProxy
import com.willfp.eco.internal.spigot.proxy.common.toMaterial
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.SnbtPrinterTagVisitor
import net.minecraft.nbt.TagParser
@@ -44,7 +45,7 @@ class SNBTConverter : SNBTConverterProxy {
val nms = CraftItemStack.asNMSCopy(itemStack)
val nmsTag = nms.save(CompoundTag())
nmsTag.remove("Count")
return tag.copy().merge(nmsTag) == nmsTag
return tag.copy().merge(nmsTag) == nmsTag && itemStack.type == item.type
}
override fun getItem(): ItemStack = item

View File

@@ -44,7 +44,7 @@ class SNBTConverter : SNBTConverterProxy {
val nms = CraftItemStack.asNMSCopy(itemStack)
val nmsTag = nms.save(CompoundTag())
nmsTag.remove("Count")
return tag.copy().merge(nmsTag) == nmsTag
return tag.copy().merge(nmsTag) == nmsTag && itemStack.type == item.type
}
override fun getItem(): ItemStack = item

View File

@@ -44,7 +44,7 @@ class SNBTConverter : SNBTConverterProxy {
val nms = CraftItemStack.asNMSCopy(itemStack)
val nmsTag = nms.save(CompoundTag())
nmsTag.remove("Count")
return tag.copy().merge(nmsTag) == nmsTag
return tag.copy().merge(nmsTag) == nmsTag && itemStack.type == item.type
}
override fun getItem(): ItemStack = item

View File

@@ -37,7 +37,7 @@ dependencies {
compileOnly("com.github.jiangdashao:matrix-api-repo:317d4635fd")
compileOnly("com.gmail.nossr50.mcMMO:mcMMO:2.1.202")
compileOnly("me.clip:placeholderapi:2.10.10")
compileOnly("com.github.oraxen:oraxen:bea381fb82")
compileOnly("com.github.oraxen:oraxen:1.155.0")
compileOnly("com.github.brcdev-minecraft:shopgui-api:3.0.0")
compileOnly("com.github.LoneDev6:API-ItemsAdder:2.4.7")
compileOnly("com.arcaniax:HeadDatabase-API:1.3.0")
@@ -52,12 +52,16 @@ dependencies {
compileOnly("com.github.N0RSKA:ScytherAPI:55a")
compileOnly("com.ticxo.modelengine:api:R3.0.1")
compileOnly("me.TechsCode:UltraEconomyAPI:1.0.0")
compileOnly("org.black_ixx:playerpoints:3.2.5")
compileOnly("com.github.Ssomar-Developement:SCore:3.4.7")
compileOnly("io.lumine:Mythic:5.2.1")
compileOnly("io.lumine:LumineUtils:1.19-SNAPSHOT")
compileOnly("com.SirBlobman.combatlogx:CombatLogX-API:10.0.0.0-SNAPSHOT")
compileOnly("com.github.sirblobman.combatlogx:api:11.0.0.0-SNAPSHOT")
compileOnly("LibsDisguises:LibsDisguises:10.0.26")
compileOnly("com.denizenscript:denizen:1.2.7-SNAPSHOT") {
exclude(group = "*", module = "*")
}
compileOnly(fileTree("../../lib") {
include("*.jar")

View File

@@ -13,8 +13,8 @@ import com.willfp.eco.core.gui.menu.Menu
import com.willfp.eco.core.gui.menu.MenuType
import com.willfp.eco.core.gui.slot.functional.SlotProvider
import com.willfp.eco.core.items.Items
import com.willfp.eco.core.math.MathContext
import com.willfp.eco.core.packet.Packet
import com.willfp.eco.core.placeholder.context.PlaceholderContext
import com.willfp.eco.internal.EcoPropsParser
import com.willfp.eco.internal.command.EcoPluginCommand
import com.willfp.eco.internal.command.EcoSubcommand
@@ -37,6 +37,8 @@ import com.willfp.eco.internal.gui.menu.renderedInventory
import com.willfp.eco.internal.gui.slot.EcoSlotBuilder
import com.willfp.eco.internal.integrations.PAPIExpansion
import com.willfp.eco.internal.logging.EcoLogger
import com.willfp.eco.internal.logging.NOOPLogger
import com.willfp.eco.internal.placeholder.PlaceholderParser
import com.willfp.eco.internal.proxy.EcoProxyFactory
import com.willfp.eco.internal.scheduling.EcoScheduler
import com.willfp.eco.internal.spigot.data.DataYml
@@ -44,7 +46,9 @@ import com.willfp.eco.internal.spigot.data.KeyRegistry
import com.willfp.eco.internal.spigot.data.ProfileHandler
import com.willfp.eco.internal.spigot.data.storage.HandlerType
import com.willfp.eco.internal.spigot.integrations.bstats.MetricHandler
import com.willfp.eco.internal.spigot.math.evaluateExpression
import com.willfp.eco.internal.spigot.math.DelegatedExpressionHandler
import com.willfp.eco.internal.spigot.math.ImmediatePlaceholderTranslationExpressionHandler
import com.willfp.eco.internal.spigot.math.LazyPlaceholderTranslationExpressionHandler
import com.willfp.eco.internal.spigot.proxy.BukkitCommandsProxy
import com.willfp.eco.internal.spigot.proxy.CommonsInitializerProxy
import com.willfp.eco.internal.spigot.proxy.DummyEntityFactoryProxy
@@ -88,6 +92,15 @@ class EcoImpl : EcoSpigotPlugin(), Eco {
if (this.configYml.getBool("use-safer-namespacedkey-creation"))
SafeInternalNamespacedKeyFactory() else FastInternalNamespacedKeyFactory()
private val placeholderParser = PlaceholderParser()
private val crunchHandler = DelegatedExpressionHandler(
this,
if (this.configYml.getBool("use-immediate-placeholder-translation-for-math"))
ImmediatePlaceholderTranslationExpressionHandler(placeholderParser)
else LazyPlaceholderTranslationExpressionHandler(placeholderParser),
)
override fun createScheduler(plugin: EcoPlugin) =
EcoScheduler(plugin)
@@ -113,6 +126,9 @@ class EcoImpl : EcoSpigotPlugin(), Eco {
override fun createLogger(plugin: EcoPlugin) =
EcoLogger(plugin)
override fun getNOOPLogger() =
NOOPLogger
override fun createPAPIIntegration(plugin: EcoPlugin) {
PAPIExpansion(plugin)
}
@@ -172,7 +188,7 @@ class EcoImpl : EcoSpigotPlugin(), Eco {
}
override fun createPluginCommand(
parentDelegate: CommandBase,
parentDelegate: PluginCommandBase,
plugin: EcoPlugin,
name: String,
permission: String,
@@ -246,6 +262,7 @@ class EcoImpl : EcoSpigotPlugin(), Eco {
override fun addNewPlugin(plugin: EcoPlugin) {
loadedEcoPlugins[plugin.name.lowercase()] = plugin
loadedEcoPlugins[plugin.id] = plugin
}
override fun getLoadedPlugins(): List<String> =
@@ -269,9 +286,6 @@ class EcoImpl : EcoSpigotPlugin(), Eco {
override fun loadPlayerProfile(uuid: UUID) =
profileHandler.load(uuid)
override fun unloadPlayerProfile(uuid: UUID) =
profileHandler.unloadPlayer(uuid)
override fun createDummyEntity(location: Location): Entity =
getProxy(DummyEntityFactoryProxy::class.java).createDummyEntity(location)
@@ -312,8 +326,8 @@ class EcoImpl : EcoSpigotPlugin(), Eco {
override fun getTPS() =
getProxy(TPSProxy::class.java).getTPS()
override fun evaluate(expression: String, context: MathContext) =
evaluateExpression(expression, context)
override fun evaluate(expression: String, context: PlaceholderContext) =
crunchHandler.evaluate(expression, context)
override fun getOpenMenu(player: Player) =
player.renderedInventory?.menu
@@ -326,4 +340,10 @@ class EcoImpl : EcoSpigotPlugin(), Eco {
override fun sendPacket(player: Player, packet: Packet) =
this.getProxy(PacketHandlerProxy::class.java).sendPacket(player, packet)
override fun translatePlaceholders(text: String, context: PlaceholderContext) =
placeholderParser.translatePlacholders(text, context)
override fun getPlaceholderValue(plugin: EcoPlugin?, args: String, context: PlaceholderContext) =
placeholderParser.getPlaceholderResult(plugin, args, context)
}

View File

@@ -3,6 +3,7 @@ package com.willfp.eco.internal.spigot
import com.willfp.eco.core.Eco
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.Prerequisite
import com.willfp.eco.core.data.ExternalDataStore
import com.willfp.eco.core.entities.Entities
import com.willfp.eco.core.integrations.IntegrationLoader
import com.willfp.eco.core.integrations.afk.AFKManager
@@ -19,6 +20,8 @@ import com.willfp.eco.core.items.Items
import com.willfp.eco.core.packet.PacketListener
import com.willfp.eco.core.particle.Particles
import com.willfp.eco.core.price.Prices
import com.willfp.eco.internal.data.MavenVersionToStringAdapter
import com.willfp.eco.internal.data.VersionToStringAdapter
import com.willfp.eco.internal.entities.EntityArgParserAdult
import com.willfp.eco.internal.entities.EntityArgParserAttackDamage
import com.willfp.eco.internal.entities.EntityArgParserAttackSpeed
@@ -92,6 +95,7 @@ import com.willfp.eco.internal.spigot.integrations.antigrief.AntigriefTowny
import com.willfp.eco.internal.spigot.integrations.antigrief.AntigriefWorldGuard
import com.willfp.eco.internal.spigot.integrations.customentities.CustomEntitiesMythicMobs
import com.willfp.eco.internal.spigot.integrations.customitems.CustomItemsCustomCrafting
import com.willfp.eco.internal.spigot.integrations.customitems.CustomItemsDenizen
import com.willfp.eco.internal.spigot.integrations.customitems.CustomItemsExecutableItems
import com.willfp.eco.internal.spigot.integrations.customitems.CustomItemsHeadDatabase
import com.willfp.eco.internal.spigot.integrations.customitems.CustomItemsItemsAdder
@@ -107,6 +111,8 @@ import com.willfp.eco.internal.spigot.integrations.hologram.HologramHolographicD
import com.willfp.eco.internal.spigot.integrations.mcmmo.McmmoIntegrationImpl
import com.willfp.eco.internal.spigot.integrations.multiverseinventories.MultiverseInventoriesIntegration
import com.willfp.eco.internal.spigot.integrations.placeholder.PlaceholderIntegrationPAPI
import com.willfp.eco.internal.spigot.integrations.price.PriceFactoryPlayerPoints
import com.willfp.eco.internal.spigot.integrations.price.PriceFactoryRoyaleEconomy
import com.willfp.eco.internal.spigot.integrations.price.PriceFactoryUltraEconomy
import com.willfp.eco.internal.spigot.integrations.shop.ShopDeluxeSellwands
import com.willfp.eco.internal.spigot.integrations.shop.ShopEconomyShopGUI
@@ -120,7 +126,9 @@ import com.willfp.eco.internal.spigot.recipes.listeners.ComplexInComplex
import com.willfp.eco.internal.spigot.recipes.listeners.ComplexInVanilla
import com.willfp.eco.internal.spigot.recipes.stackhandlers.ShapedCraftingRecipeStackHandler
import com.willfp.eco.internal.spigot.recipes.stackhandlers.ShapelessCraftingRecipeStackHandler
import com.willfp.eco.util.ClassUtils
import me.TechsCode.UltraEconomy.UltraEconomy
import me.qKing12.RoyaleEconomy.MultiCurrency.MultiCurrencyHandler
import net.kyori.adventure.platform.bukkit.BukkitAudiences
import net.milkbowl.vault.economy.Economy
import org.bukkit.Bukkit
@@ -178,6 +186,20 @@ abstract class EcoSpigotPlugin : EcoPlugin() {
SegmentParserUseIfPresent.register()
CustomItemsManager.registerProviders()
ExternalDataStore.registerAdapter(VersionToStringAdapter)
// Handle with shadow.
val className = listOf(
"org",
"apache",
"maven",
"artifact",
"versioning",
"DefaultArtifactVersion"
).joinToString(".")
if (ClassUtils.exists(className)) {
ExternalDataStore.registerAdapter(MavenVersionToStringAdapter(className))
}
}
override fun handleEnable() {
@@ -199,8 +221,6 @@ abstract class EcoSpigotPlugin : EcoPlugin() {
this.logger.info("No conflicts found!")
}
CollatedRunnable(this)
CustomItemsManager.registerProviders() // Do it again here
// Register events for ShopSellEvent
@@ -231,14 +251,15 @@ abstract class EcoSpigotPlugin : EcoPlugin() {
Eco.get().adventure?.close()
}
override fun handleReload() {
override fun createTasks() {
CollatedRunnable(this)
this.scheduler.runLater(3) {
profileHandler.migrateIfNeeded()
}
ProfileSaver(this, profileHandler)
ProfileSaver(this, profileHandler).startTicking()
this.scheduler.runTimer(
{ getProxy(PacketHandlerProxy::class.java).clearDisplayFrames() },
this.configYml.getInt("display-frame-ttl").toLong(),
@@ -270,6 +291,7 @@ abstract class EcoSpigotPlugin : EcoPlugin() {
IntegrationLoader("CombatLogX") {
val pluginManager = Bukkit.getPluginManager()
val combatLogXPlugin = pluginManager.getPlugin("CombatLogX") ?: return@IntegrationLoader
@Suppress("DEPRECATION")
val pluginVersion = combatLogXPlugin.description.version
if (pluginVersion.startsWith("10")) {
@@ -294,7 +316,7 @@ abstract class EcoSpigotPlugin : EcoPlugin() {
IntegrationLoader("MythicMobs") { CustomEntitiesManager.register(CustomEntitiesMythicMobs()) },
// Custom Items
IntegrationLoader("Oraxen") { CustomItemsManager.register(CustomItemsOraxen()) },
IntegrationLoader("Oraxen") { CustomItemsManager.register(CustomItemsOraxen(this)) },
IntegrationLoader("ItemsAdder") { CustomItemsManager.register(CustomItemsItemsAdder()) },
IntegrationLoader("HeadDatabase") { CustomItemsManager.register(CustomItemsHeadDatabase(this)) },
IntegrationLoader("ExecutableItems") { CustomItemsManager.register(CustomItemsExecutableItems()) },
@@ -304,6 +326,7 @@ abstract class EcoSpigotPlugin : EcoPlugin() {
},
IntegrationLoader("MythicMobs") { CustomItemsManager.register(CustomItemsMythicMobs(this)) },
IntegrationLoader("Scyther") { CustomItemsManager.register(CustomItemsScyther()) },
IntegrationLoader("Denizen") { CustomItemsManager.register(CustomItemsDenizen()) },
// Shop
IntegrationLoader("ShopGUIPlus") { ShopManager.register(ShopShopGuiPlus()) },
@@ -336,6 +359,12 @@ abstract class EcoSpigotPlugin : EcoPlugin() {
Prices.registerPriceFactory(PriceFactoryUltraEconomy(currency))
}
},
IntegrationLoader("PlayerPoints") { Prices.registerPriceFactory(PriceFactoryPlayerPoints()) },
IntegrationLoader("RoyaleEconomy") {
for (currency in MultiCurrencyHandler.getCurrencies()) {
Prices.registerPriceFactory(PriceFactoryRoyaleEconomy(currency))
}
},
// Placeholder
IntegrationLoader("PlaceholderAPI") { PlaceholderManager.addIntegration(PlaceholderIntegrationPAPI()) },
@@ -360,7 +389,7 @@ abstract class EcoSpigotPlugin : EcoPlugin() {
GUIListener(this),
ArrowDataListener(this),
ArmorChangeEventListeners(this),
DataListener(this),
DataListener(this, profileHandler),
PlayerBlockListener(this),
ServerLocking
)

View File

@@ -1,6 +1,5 @@
package com.willfp.eco.internal.spigot.data
import com.willfp.eco.core.Eco
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.util.PlayerUtils
import org.bukkit.event.EventHandler
@@ -11,11 +10,14 @@ import org.bukkit.event.player.PlayerLoginEvent
import org.bukkit.event.player.PlayerQuitEvent
class DataListener(
private val plugin: EcoPlugin
private val plugin: EcoPlugin,
private val handler: ProfileHandler
) : Listener {
@EventHandler(priority = EventPriority.HIGHEST)
fun onLeave(event: PlayerQuitEvent) {
Eco.get().unloadPlayerProfile(event.player.uniqueId)
val profile = handler.accessLoadedProfile(event.player.uniqueId) ?: return
handler.saveKeysFor(event.player.uniqueId, profile.data.keys)
handler.unloadPlayer(event.player.uniqueId)
}
@EventHandler
@@ -27,6 +29,6 @@ class DataListener(
@EventHandler(priority = EventPriority.LOWEST)
fun onLogin(event: PlayerLoginEvent) {
Eco.get().unloadPlayerProfile(event.player.uniqueId)
handler.unloadPlayer(event.player.uniqueId)
}
}

View File

@@ -1,5 +1,6 @@
package com.willfp.eco.internal.spigot.data
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.data.PlayerProfile
import com.willfp.eco.core.data.Profile
import com.willfp.eco.core.data.ServerProfile
@@ -11,14 +12,13 @@ import java.util.concurrent.ConcurrentHashMap
abstract class EcoProfile(
val data: MutableMap<PersistentDataKey<*>, Any>,
val uuid: UUID,
private val handler: DataHandler
private val handler: DataHandler,
private val localHandler: DataHandler
) : Profile {
override fun <T : Any> write(key: PersistentDataKey<T>, value: T) {
this.data[key] = value
val changedKeys = CHANGE_MAP[uuid] ?: mutableSetOf()
changedKeys.add(key)
CHANGE_MAP[uuid] = changedKeys
CHANGE_MAP.add(uuid)
}
override fun <T : Any> read(key: PersistentDataKey<T>): T {
@@ -27,7 +27,12 @@ abstract class EcoProfile(
return this.data[key] as T
}
this.data[key] = handler.read(uuid, key) ?: key.defaultValue
this.data[key] = if (key.isLocal) {
localHandler.read(uuid, key)
} else {
handler.read(uuid, key)
} ?: key.defaultValue
return read(key)
}
@@ -44,15 +49,16 @@ abstract class EcoProfile(
}
companion object {
val CHANGE_MAP: MutableMap<UUID, MutableSet<PersistentDataKey<*>>> = ConcurrentHashMap()
val CHANGE_MAP: MutableSet<UUID> = ConcurrentHashMap.newKeySet()
}
}
class EcoPlayerProfile(
data: MutableMap<PersistentDataKey<*>, Any>,
uuid: UUID,
handler: DataHandler
) : EcoProfile(data, uuid, handler), PlayerProfile {
handler: DataHandler,
localHandler: DataHandler
) : EcoProfile(data, uuid, handler, localHandler), PlayerProfile {
override fun toString(): String {
return "EcoPlayerProfile{uuid=$uuid}"
}
@@ -60,9 +66,13 @@ class EcoPlayerProfile(
class EcoServerProfile(
data: MutableMap<PersistentDataKey<*>, Any>,
handler: DataHandler
) : EcoProfile(data, serverProfileUUID, handler), ServerProfile {
handler: DataHandler,
localHandler: DataHandler
) : EcoProfile(data, serverProfileUUID, handler, localHandler), ServerProfile {
override fun toString(): String {
return "EcoServerProfile"
}
}
private val PersistentDataKey<*>.isLocal: Boolean
get() = EcoPlugin.getPlugin(this.key.namespace)?.isUsingLocalStorage == true

View File

@@ -4,6 +4,7 @@ import com.willfp.eco.core.config.interfaces.Config
import com.willfp.eco.core.data.keys.PersistentDataKey
import com.willfp.eco.core.data.keys.PersistentDataKeyType
import org.bukkit.NamespacedKey
import java.math.BigDecimal
object KeyRegistry {
private val registry = mutableMapOf<NamespacedKey, PersistentDataKey<*>>()
@@ -44,6 +45,9 @@ object KeyRegistry {
PersistentDataKeyType.CONFIG -> if (default !is Config) {
throw IllegalArgumentException("Invalid Data Type! Should be Config")
}
PersistentDataKeyType.BIG_DECIMAL -> if (default !is BigDecimal) {
throw IllegalArgumentException("Invalid Data Type! Should be BigDecimal")
}
else -> throw NullPointerException("Null value found!")
}

View File

@@ -9,7 +9,6 @@ import com.willfp.eco.internal.spigot.EcoSpigotPlugin
import com.willfp.eco.internal.spigot.ServerLocking
import com.willfp.eco.internal.spigot.data.storage.DataHandler
import com.willfp.eco.internal.spigot.data.storage.HandlerType
import com.willfp.eco.internal.spigot.data.storage.LegacyMySQLDataHandler
import com.willfp.eco.internal.spigot.data.storage.MongoDataHandler
import com.willfp.eco.internal.spigot.data.storage.MySQLDataHandler
import com.willfp.eco.internal.spigot.data.storage.YamlDataHandler
@@ -22,24 +21,18 @@ class ProfileHandler(
private val type: HandlerType,
private val plugin: EcoSpigotPlugin
) {
private val loaded = mutableMapOf<UUID, Profile>()
private val loaded = mutableMapOf<UUID, EcoProfile>()
private val localHandler = YamlDataHandler(plugin, this)
val handler: DataHandler = when (type) {
HandlerType.YAML -> YamlDataHandler(plugin, this)
HandlerType.YAML -> localHandler
HandlerType.MYSQL -> MySQLDataHandler(plugin, this)
HandlerType.MONGO -> MongoDataHandler(plugin, this)
HandlerType.LEGACY_MYSQL -> LegacyMySQLDataHandler(plugin, this)
}
init {
if (handler.type == HandlerType.LEGACY_MYSQL) {
plugin.logger.warning("You're using the legacy MySQL handler!")
plugin.logger.warning("Some features will not work and you may get unfixable errors.")
plugin.logger.warning("Support cannot be given to data issues related to legacy MySQL.")
plugin.logger.warning("Change your data handler to mysql, mongo, or yaml to fix this!")
plugin.logger.warning("This can be done in /plugins/eco/config.yml")
}
}
fun accessLoadedProfile(uuid: UUID): EcoProfile? =
loaded[uuid]
fun loadGenericProfile(uuid: UUID): Profile {
val found = loaded[uuid]
@@ -50,7 +43,7 @@ class ProfileHandler(
val data = mutableMapOf<PersistentDataKey<*>, Any>()
val profile = if (uuid == serverProfileUUID)
EcoServerProfile(data, handler) else EcoPlayerProfile(data, uuid, handler)
EcoServerProfile(data, handler, localHandler) else EcoPlayerProfile(data, uuid, handler, localHandler)
loaded[uuid] = profile
return profile
@@ -65,7 +58,19 @@ class ProfileHandler(
}
fun saveKeysFor(uuid: UUID, keys: Set<PersistentDataKey<*>>) {
handler.saveKeysFor(uuid, keys)
val profile = accessLoadedProfile(uuid) ?: return
val map = mutableMapOf<PersistentDataKey<*>, Any>()
for (key in keys) {
map[key] = profile.data[key] ?: continue
}
handler.saveKeysFor(uuid, map)
// Don't save to local handler if it's the same handler.
if (localHandler != handler) {
localHandler.saveKeysFor(uuid, map)
}
}
fun unloadPlayer(uuid: UUID) {
@@ -74,6 +79,10 @@ class ProfileHandler(
fun save() {
handler.save()
if (localHandler != handler) {
localHandler.save()
}
}
fun migrateIfNeeded() {
@@ -87,11 +96,7 @@ class ProfileHandler(
}
var previousHandlerType = HandlerType.valueOf(plugin.dataYml.getString("previous-handler"))
if (previousHandlerType == HandlerType.MYSQL && !plugin.dataYml.has("new-mysql")) {
previousHandlerType = HandlerType.LEGACY_MYSQL
}
val previousHandlerType = HandlerType.valueOf(plugin.dataYml.getString("previous-handler"))
if (previousHandlerType == type) {
return
@@ -101,7 +106,6 @@ class ProfileHandler(
HandlerType.YAML -> YamlDataHandler(plugin, this)
HandlerType.MYSQL -> MySQLDataHandler(plugin, this)
HandlerType.MONGO -> MongoDataHandler(plugin, this)
HandlerType.LEGACY_MYSQL -> LegacyMySQLDataHandler(plugin, this)
}
ServerLocking.lock("Migrating player data! Check console for more information.")
@@ -161,5 +165,8 @@ class ProfileHandler(
fun initialize() {
handler.initialize()
if (localHandler != handler) {
localHandler.initialize()
}
}
}

View File

@@ -19,7 +19,7 @@ abstract class DataHandler(
/**
* Save a set of keys for a given UUID.
*/
abstract fun saveKeysFor(uuid: UUID, keys: Set<PersistentDataKey<*>>)
abstract fun saveKeysFor(uuid: UUID, keys: Map<PersistentDataKey<*>, Any>)
// Everything below this are methods that are only needed for certain implementations.

View File

@@ -3,6 +3,5 @@ package com.willfp.eco.internal.spigot.data.storage
enum class HandlerType {
YAML,
MYSQL,
MONGO,
LEGACY_MYSQL
MONGO
}

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