Compare commits

..

121 Commits

Author SHA1 Message Date
Auxilor
d9a8d26990 Fixed 1.19.3 build 2022-12-11 17:07:33 +00:00
Auxilor
4d3eeaaefc Added 1.19.3 support 2022-12-11 16:58:58 +00:00
Auxilor
610110efde Improved CombinedDisplayPrice 2022-12-07 16:38:32 +00:00
Auxilor
7f9052c64d Updated to 6.48.0 2022-12-05 12:34:53 +00:00
Auxilor
55a841b3f5 Added CombinedDisplayPrice 2022-12-05 12:34:45 +00:00
Auxilor
c57c824027 Cleaned up UltraEconomy integration 2022-11-29 15:56:30 +00:00
Auxilor
7cb905e65a Merge remote-tracking branch 'origin/develop' into develop
# Conflicts:
#	eco-api/src/main/java/com/willfp/eco/core/gui/slot/ConfigSlot.java
2022-11-29 15:47:52 +00:00
Auxilor
31a2c7e338 Added lore to ConfigSlot 2022-11-29 15:47:41 +00:00
Auxilor
1759b52f82 Added lore to ConfigSlot 2022-11-29 15:37:36 +00:00
Auxilor
ccf93e3a4d Fixed 2x2 crafting bug 2022-11-28 23:55:45 +00:00
Auxilor
abd07389ab Fixed dynamic command registration 2022-11-28 17:28:29 +00:00
Auxilor
80ad738bb2 Removed AbstractItemStackBuilder correctifying things 2022-11-28 15:10:28 +00:00
Auxilor
b01105819a Updated EconomyShopGUI and ShopGUI+ integrations 2022-11-28 14:46:38 +00:00
Auxilor
a7c08b0731 Updated shop API (again) 2022-11-28 14:40:42 +00:00
Auxilor
7e4c071698 Fixed javadoc 2022-11-28 14:28:38 +00:00
Auxilor
f94f7ead08 Reworked price/shop API to have call-site multipliers 2022-11-28 14:26:46 +00:00
Auxilor
b21c5bf3a9 Fixed captive filter and GUI drag bug 2022-11-28 13:57:41 +00:00
Auxilor
7a9e8c5c10 Fixed bug with multiple stacked paginated menus 2022-11-25 16:51:17 +00:00
Auxilor
a6ddbc46ab Fixes 2022-11-25 16:28:24 +00:00
Auxilor
ffaee137d8 Overhauled ShopSellEvent API 2022-11-24 23:19:03 +00:00
Auxilor
18d882dac6 Reworked Shop API 2022-11-24 23:12:25 +00:00
Auxilor
52841f7f04 Fixed bug with price copying 2022-11-23 22:31:37 +00:00
Auxilor
47b72e9243 Javadoc fix 2022-11-23 22:03:44 +00:00
Auxilor
854a10e8fd Added Price#withMultiplier 2022-11-23 22:00:07 +00:00
Auxilor
823ef6477b Refactoring for naming, updated to 6.47.0 2022-11-23 21:56:07 +00:00
Auxilor
eccb146852 Reworked slot changes into canCaptivateItem 2022-11-18 18:08:21 +00:00
Auxilor
d877b707d6 Improved lang.yml/config.yml warning messages 2022-11-17 15:12:28 +00:00
Auxilor
bcb7401c74 Fixed captive slot changes PR 2022-11-17 15:10:05 +00:00
Auxilor
f05c5f3cd6 Fixed UltraEconomy integration 2022-11-17 15:02:40 +00:00
_OfTeN_
3bd8bccb81 Added UltraEconomy support to Price Lookup 2022-11-17 02:58:53 +03:00
_OfTeN_
6f55787c84 Added ability to filter items for captive slots 2022-11-13 02:50:23 +03:00
_OfTeN_
eb4dc168fc Added proper error displaying when missing config or lang ymls 2022-11-11 02:08:19 +03:00
Auxilor
c5085de5d1 Updated to 6.46.1 2022-11-09 12:59:44 +00:00
Auxilor
019cdbf9c8 Fixed tab completion bug 2022-11-09 12:59:37 +00:00
Auxilor
d5ddcaea4b Cleaned up Menu API 2022-11-08 18:11:27 +00:00
Auxilor
d3a7ef72e8 Thread naming 2022-11-08 16:39:28 +00:00
Auxilor
a311ce1227 Added save-interval option 2022-11-08 16:01:46 +00:00
Auxilor
5c0d4540a8 Merge branch 'pvpmanager-support' into develop 2022-11-08 15:55:03 +00:00
Auxilor
7e66ee8071 Merge branch 'fix/exposed' into develop 2022-11-08 15:54:06 +00:00
Auxilor
fd233df736 Added relevant kt extension for onBuild 2022-11-07 16:43:32 +00:00
Auxilor
6baf636e6a Added MenuBuilder#onBuild 2022-11-07 16:42:40 +00:00
Auxilor
9ee579f2c4 Updated to 6.46.0 2022-11-07 16:40:14 +00:00
Auxilor
3b5ea87353 Fixes to ReactiveSlot 2022-11-07 15:42:46 +00:00
Auxilor
00d32ed218 Added CaptiveItemChangeEvent 2022-11-07 15:32:01 +00:00
Auxilor
5ce9a1c04e Added ItemStack#modify and TestableItem#modify 2022-11-07 15:13:19 +00:00
Auxilor
966549065d Added PlayableSound 2022-11-07 15:08:26 +00:00
Auxilor
ee2911b57c Revert "Added "undyable" arg parser"
This reverts commit a9c32000d8.
2022-11-07 15:00:47 +00:00
_OfTeN_
f15ec5eec0 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/integrations/customitems/CustomItemsScyther.kt
2022-11-07 15:54:25 +03:00
_OfTeN_
a9c32000d8 Added "undyable" arg parser 2022-11-07 15:54:02 +03:00
_OfTeN_
b68459951f Fixed scyther integration 2022-11-07 14:51:03 +03:00
Will FP
b520c76169 Update README.md 2022-11-06 20:03:25 +00:00
Auxilor
d081afbd8e Improved command sync 2022-11-06 19:23:11 +00:00
Auxilor
46fd0439a5 Fixed dynamic command reg 2022-11-06 19:17:21 +00:00
Auxilor
ad58ce4a74 GUI Fixes + Improvements 2022-11-06 14:21:49 +00:00
Auxilor
b5cd8f42e0 Fixed illusioner goals 2022-11-05 21:25:52 +00:00
Auxilor
d0baf50709 Fixed illusioner goals 2022-11-04 15:07:34 +00:00
Auxilor
2496f318fa Removed health-fixer 2022-11-04 13:57:16 +00:00
Auxilor
048c200c95 Changed ConfiguredPrice to be a delegate 2022-11-01 22:05:25 +00:00
Auxilor
17446acb2e Fixed javadoc 2022-11-01 21:55:56 +00:00
Auxilor
7929113c91 Improved PriceItem 2022-11-01 21:53:27 +00:00
Auxilor
6acc5864bd Reworked Price API (again) 2022-11-01 21:36:09 +00:00
Kees Monshouwer
fa64950d28 add support for PvPManager 2022-10-31 09:39:17 +01:00
Auxilor
730c20dbc0 Added Testable Extensions 2022-10-30 16:53:29 +00:00
Auxilor
0c5ae54c3a Codestyle 2022-10-30 16:34:08 +00:00
Auxilor
b48e80837d Fixed javadoc 2022-10-30 16:32:30 +00:00
Auxilor
17eb4cf5f8 Added alias and description support to PluginCommand 2022-10-30 16:02:44 +00:00
Auxilor
890f85fa56 Added Player versions of onExecute and tabComplete 2022-10-30 15:58:14 +00:00
Auxilor
86b427c95e Improved commands 2022-10-30 15:21:21 +00:00
Auxilor
8c1fde57b0 Improved DelegatedBukkitCommand 2022-10-30 15:03:17 +00:00
Auxilor
2efc040a8e Added dynamic command registration 2022-10-29 19:06:15 +01:00
Auxilor
54b2b42512 Added price multipliers 2022-10-29 16:53:24 +01:00
Auxilor
e67d9d634c Fixed javadoc 2022-10-29 16:34:18 +01:00
Auxilor
39fb676b9a Updated to 6.45.0 2022-10-29 16:28:57 +01:00
Auxilor
ec8936b765 Updated price API 2022-10-29 16:28:48 +01:00
Auxilor
cf347de4b8 Updated to 6.44.1 2022-10-27 16:20:51 +01:00
Auxilor
2b7122c5c2 Fixed particles 2022-10-27 16:20:42 +01:00
Auxilor
062b5c9b92 Fixed ProtocolLib bug with TemporaryPlayer 2022-10-27 15:13:20 +01:00
DaRacci
052cd74756 fix: explicit database for transactions 2022-10-28 01:06:35 +11:00
Auxilor
082b39a2e4 Fixed config bug with list getters 2022-10-25 16:27:35 +01:00
Auxilor
616fa032d9 Removed Prices#lookup due to poor usage 2022-10-24 15:59:41 +01:00
Auxilor
ea997239fc Fix 2022-10-24 15:59:06 +01:00
Auxilor
ad04abab73 Reworked prices 2022-10-24 15:29:03 +01:00
Auxilor
f440ef922b Updated prices, removed display text 2022-10-24 14:57:27 +01:00
Auxilor
933271fb4a Imrpoved Economy Price 2022-10-24 13:41:41 +01:00
Auxilor
9679d3100f Imrpoved Economy Price 2022-10-24 13:38:39 +01:00
Auxilor
6e20763522 Refactor 2022-10-24 13:20:30 +01:00
Auxilor
18dea2c20c Fixed javadoc 2022-10-24 13:19:38 +01:00
Auxilor
62b666559c Added MathContext, added value to price 2022-10-24 13:17:09 +01:00
Auxilor
465563523b Fixed javadco 2022-10-24 12:40:14 +01:00
Auxilor
f0f014ed89 Added particle lookup system 2022-10-24 12:23:41 +01:00
Auxilor
58811c5d77 Updated PriceFree 2022-10-24 11:53:01 +01:00
Auxilor
81d495e76e Added display text to prices 2022-10-23 21:43:43 +01:00
Auxilor
c78e397959 Renamed addState to setState 2022-10-23 17:32:28 +01:00
Auxilor
95740f155e Imports 2022-10-23 17:08:46 +01:00
Auxilor
5638d5e152 Added GenericConfig to cover last remaining use of TransientConfig 2022-10-23 16:56:10 +01:00
Auxilor
4d3712057c Added plurals to vanilla item names 2022-10-23 16:48:06 +01:00
Auxilor
458fcd78b3 Updated to 6.44.0 2022-10-23 16:41:49 +01:00
Auxilor
ee13de31f4 Added price system 2022-10-23 16:40:52 +01:00
Auxilor
9588d49788 Added functional pattern to plugin lifecycle hooks 2022-10-23 15:55:23 +01:00
Auxilor
8e7ce298b0 Improved Config API, minor cleanup 2022-10-23 15:48:50 +01:00
Auxilor
32a11ce5b8 Added friendly material names 2022-10-23 15:08:33 +01:00
Auxilor
960f62cc8b Updated to 6.43.7 2022-10-16 21:48:14 +01:00
Auxilor
28ceb83eb5 Fixed ExtendedPersistentDataContainerFactory.kt 2022-10-16 21:47:46 +01:00
Auxilor
6f748b6b8a Updated to 6.43.6 2022-10-07 17:28:39 +01:00
Auxilor
190ea5d49f Refactor + Fix 2022-10-07 17:28:28 +01:00
Auxilor
c0ed083a5c Fixed PAPI 2022-10-04 11:58:15 +01:00
Auxilor
04f04bb7a6 Fixed custom model data parser 2022-10-04 11:48:06 +01:00
Auxilor
b8a3806ff9 Updated to 6.43.5 2022-10-04 11:44:17 +01:00
Auxilor
ae49d41542 Fixes to placeholders and integrations 2022-10-04 11:43:10 +01:00
Auxilor
5f2255a3bc Fixed initial render 2022-10-03 23:33:39 +01:00
Auxilor
065ccfbe67 Updated to 6.43.4 2022-10-03 22:30:27 +01:00
Auxilor
17727c9015 Optimized second render for captive changes 2022-10-03 22:30:20 +01:00
Auxilor
ea64e69b4d Fixed GUI bug 2022-10-03 22:25:03 +01:00
Auxilor
07ca6c2359 Fixed 2 more GUI bugs 2022-10-03 21:58:53 +01:00
Auxilor
162558b1c2 Updated to 6.43.3 2022-10-03 13:44:28 +01:00
Auxilor
10f9e8dce0 Fixed Skull 2022-10-03 13:44:17 +01:00
Will FP
b02943d7ff Merge pull request #204
Fix: Skull texture out of bounds error
2022-10-03 13:42:39 +01:00
Auxilor
40ad970ffa Updated to 6.43.2 2022-10-03 13:00:02 +01:00
Auxilor
aefdfa786d Fixed menu rendering bugs 2022-10-03 12:59:54 +01:00
MillionthOdin16
1cf08955a0 Additional check for bounds error
explanation in comment
2022-10-02 19:33:37 -04:00
_OfTeN_
7767e48e51 Fixed scyther integration 2022-09-14 23:03:20 +03:00
128 changed files with 4038 additions and 723 deletions

View File

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

View File

@@ -77,6 +77,8 @@ allprojects {
// LibsDisguises
maven("https://repo.md-5.net/content/groups/public/")
maven("https://repo.techscode.com/repository/maven-releases/")
}
dependencies {

View File

@@ -1,5 +1,6 @@
package com.willfp.eco.core;
import com.willfp.eco.core.command.impl.PluginCommand;
import com.willfp.eco.core.config.ConfigType;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.config.interfaces.LoadableConfig;
@@ -21,16 +22,15 @@ import com.willfp.eco.core.gui.menu.MenuBuilder;
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.integrations.placeholder.PlaceholderIntegration;
import com.willfp.eco.core.items.TestableItem;
import com.willfp.eco.core.placeholder.AdditionalPlayer;
import com.willfp.eco.core.placeholder.PlaceholderInjectable;
import com.willfp.eco.core.math.MathContext;
import com.willfp.eco.core.proxy.ProxyFactory;
import com.willfp.eco.core.scheduling.Scheduler;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import org.apache.commons.lang.Validate;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.command.CommandMap;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Mob;
@@ -42,7 +42,6 @@ import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -74,7 +73,7 @@ public interface Eco {
/**
* Create an event manager.
*
* @param plugin The plugin.
* @param plugin The plugin.F
* @return The event manager.
*/
@NotNull
@@ -138,10 +137,8 @@ public interface Eco {
* Create a PAPI integration.
*
* @param plugin The plugin.
* @return The integration.
*/
@NotNull
PlaceholderIntegration createPAPIIntegration(@NotNull EcoPlugin plugin);
void createPAPIIntegration(@NotNull EcoPlugin plugin);
/**
* Create a proxy factory.
@@ -173,6 +170,7 @@ public interface Eco {
* @param requiresChangesToSave If the config must be changed in order to save the config.
* @return The config implementation.
*/
@NotNull
LoadableConfig createUpdatableConfig(@NotNull String configName,
@NotNull PluginLike plugin,
@NotNull String subDirectoryPath,
@@ -193,6 +191,7 @@ public interface Eco {
* @param requiresChangesToSave If the config must be changed in order to save the config.
* @return The config implementation.
*/
@NotNull
LoadableConfig createLoadableConfig(@NotNull String configName,
@NotNull PluginLike plugin,
@NotNull String subDirectoryPath,
@@ -206,6 +205,7 @@ public interface Eco {
* @param config The handle.
* @return The config implementation.
*/
@NotNull
Config wrapConfigurationSection(@NotNull ConfigurationSection config);
/**
@@ -215,6 +215,7 @@ public interface Eco {
* @param type The config type.
* @return The config implementation.
*/
@NotNull
Config createConfig(@NotNull Map<String, Object> values,
@NotNull ConfigType type);
@@ -225,6 +226,7 @@ public interface Eco {
* @param type The type.
* @return The config implementation.
*/
@NotNull
Config createConfig(@NotNull String contents,
@NotNull ConfigType type);
@@ -335,23 +337,16 @@ public interface Eco {
*
* @return The keys.
*/
@NotNull
Set<PersistentDataKey<?>> getRegisteredPersistentDataKeys();
/**
* Get persistent data key from namespaced key.
*
* @param namespacedKey The key.
* @return The key, or null if not found.
*/
@Nullable
PersistentDataKey<?> getPersistentDataKeyFrom(@NotNull NamespacedKey namespacedKey);
/**
* Load a player profile.
*
* @param uuid The UUID.
* @return The profile.
*/
@NotNull
PlayerProfile loadPlayerProfile(@NotNull UUID uuid);
/**
@@ -359,6 +354,7 @@ public interface Eco {
*
* @return The profile.
*/
@NotNull
ServerProfile getServerProfile();
/**
@@ -370,24 +366,6 @@ public interface Eco {
*/
void unloadPlayerProfile(@NotNull UUID uuid);
/**
* Save keys for a player.
* <p>
* Can run async if using MySQL.
*
* @param uuid The uuid.
* @param keys The keys.
*/
void savePersistentDataKeysFor(@NotNull UUID uuid,
@NotNull Set<PersistentDataKey<?>> keys);
/**
* Commit all changes to the file.
* <p>
* Does nothing if using MySQL.
*/
void saveAllProfiles();
/**
* Create dummy entity - never spawned, exists purely in code.
*
@@ -513,16 +491,12 @@ public interface Eco {
/**
* Evaluate an expression.
*
* @param expression The expression.
* @param player The player.
* @param injectable The injectable placeholders.
* @param additionalPlayers The additional players.
* @param expression The expression.
* @param context The context.
* @return The value of the expression, or zero if invalid.
*/
double evaluate(@NotNull String expression,
@Nullable Player player,
@NotNull PlaceholderInjectable injectable,
@NotNull Collection<AdditionalPlayer> additionalPlayers);
@NotNull MathContext context);
/**
* Get the menu a player currently has open.
@@ -533,6 +507,25 @@ public interface Eco {
@Nullable
Menu getOpenMenu(@NotNull Player player);
/**
* Sync commands.
*/
void syncCommands();
/**
* Get the command map.
*
* @return The command map.
*/
@NotNull CommandMap getCommandMap();
/**
* Unregister a command.
*
* @param command The command.
*/
void unregisterCommand(@NotNull final PluginCommand command);
/**
* Get the instance of eco; the bridge between the api frontend
* and the implementation backend.

View File

@@ -13,7 +13,6 @@ import com.willfp.eco.core.factory.MetadataValueFactory;
import com.willfp.eco.core.factory.NamespacedKeyFactory;
import com.willfp.eco.core.factory.RunnableFactory;
import com.willfp.eco.core.integrations.IntegrationLoader;
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager;
import com.willfp.eco.core.proxy.ProxyFactory;
import com.willfp.eco.core.scheduling.Scheduler;
import com.willfp.eco.core.web.UpdateChecker;
@@ -153,6 +152,31 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
@Nullable
private final ProxyFactory proxyFactory;
/**
* The tasks to run on enable.
*/
private final List<Runnable> onEnable = new ArrayList<>();
/**
* The tasks to run on disable.
*/
private final List<Runnable> onDisable = new ArrayList<>();
/**
* The tasks to run on reload.
*/
private final List<Runnable> onReload = new ArrayList<>();
/**
* The tasks to run on load.
*/
private final List<Runnable> onLoad = new ArrayList<>();
/**
* The tasks to run after load.
*/
private final List<Runnable> afterLoad = new ArrayList<>();
/**
* Create a new plugin.
* <p>
@@ -375,8 +399,7 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
.collect(Collectors.toSet());
if (enabledPlugins.contains("PlaceholderAPI".toLowerCase())) {
this.loadedIntegrations.add("PlaceholderAPI");
PlaceholderManager.addIntegration(Eco.get().createPAPIIntegration(this));
Eco.get().createPAPIIntegration(this);
}
this.loadIntegrationLoaders().forEach(integrationLoader -> {
@@ -386,6 +409,8 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
}
});
this.loadedIntegrations.removeIf(pl -> pl.equalsIgnoreCase(this.getName()));
this.getLogger().info("Loaded integrations: " + String.join(", ", this.getLoadedIntegrations()));
Prerequisite.update();
@@ -414,10 +439,20 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
}
this.handleEnable();
this.onEnable.forEach(Runnable::run);
this.getLogger().info("");
}
/**
* Add new task to run on enable.
*
* @param task The task.
*/
public final void onEnable(@NotNull final Runnable task) {
this.onEnable.add(task);
}
/**
* Default code to be executed on plugin disable.
*/
@@ -429,6 +464,7 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
this.getScheduler().cancelAll();
this.handleDisable();
this.onDisable.forEach(Runnable::run);
if (this.isSupportingExtensions()) {
this.getExtensionLoader().unloadExtensions();
@@ -438,6 +474,15 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
Eco.get().clean(this);
}
/**
* Add new task to run on disable.
*
* @param task The task.
*/
public final void onDisable(@NotNull final Runnable task) {
this.onDisable.add(task);
}
/**
* Default code to be executed on plugin load.
*/
@@ -446,6 +491,16 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
super.onLoad();
this.handleLoad();
this.onLoad.forEach(Runnable::run);
}
/**
* Add new task to run on load.
*
* @param task The task.
*/
public final void onLoad(@NotNull final Runnable task) {
this.onLoad.add(task);
}
/**
@@ -478,6 +533,7 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
}
this.handleAfterLoad();
this.afterLoad.forEach(Runnable::run);
this.reload();
@@ -488,6 +544,15 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
this.getLogger().info("Loaded " + this.color + this.getName());
}
/**
* Add new task to run after load.
*
* @param task The task.
*/
public final void afterLoad(@NotNull final Runnable task) {
this.afterLoad.add(task);
}
/**
* Reload the plugin.
*/
@@ -499,12 +564,22 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
this.getConfigHandler().callUpdate(); // Call twice to fix issues
this.handleReload();
this.onReload.forEach(Runnable::run);
for (Extension extension : this.extensionLoader.getLoadedExtensions()) {
extension.handleReload();
}
}
/**
* Add new task to run on enable.
*
* @param task The task.
*/
public final void onReload(@NotNull final Runnable task) {
this.onReload.add(task);
}
/**
* Reload the plugin and return the time taken to reload.
*
@@ -624,7 +699,15 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
* @return lang.yml.
*/
protected LangYml createLangYml() {
return new LangYml(this);
try {
return new LangYml(this);
} catch (NullPointerException e) {
this.getLogger().severe("Failed to load lang.yml!");
this.getLogger().severe("For the developer of this plugin: make sure you have a lang.yml");
e.printStackTrace();
Bukkit.getPluginManager().disablePlugin(this);
return null;
}
}
/**
@@ -635,7 +718,15 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
* @return config.yml.
*/
protected ConfigYml createConfigYml() {
return new ConfigYml(this);
try {
return new ConfigYml(this);
} catch (NullPointerException e) {
this.getLogger().severe("Failed to load config.yml!");
this.getLogger().severe("For the developer of this plugin: make sure you have a config.yml");
e.printStackTrace();
Bukkit.getPluginManager().disablePlugin(this);
return null;
}
}
/**

View File

@@ -1,7 +1,9 @@
package com.willfp.eco.core.command;
import com.google.common.collect.ImmutableList;
import com.willfp.eco.core.EcoPlugin;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
@@ -43,8 +45,6 @@ public interface CommandBase {
/**
* Handle command execution.
* <p>
* Marked as default void with no implementation for backwards compatibility.
*
* @param sender The sender.
* @param args The args.
@@ -54,20 +54,43 @@ public interface CommandBase {
// Do nothing.
}
/**
* Handle command execution from players.
*
* @param sender The sender.
* @param args The args.
*/
default void onExecute(@NotNull Player sender,
@NotNull List<String> args) {
// Do nothing.
}
/**
* Handle tab completion.
* <p>
* Marked as default void with no implementation for backwards compatibility.
*
* @param sender The sender.
* @param args The args.
* @return The results.
*/
@NotNull
default List<String> tabComplete(@NotNull CommandSender sender,
@NotNull List<String> args) {
return new ArrayList<>();
}
/**
* Handle tab completion.
*
* @param sender The sender.
* @param args The args.
* @return The results.
*/
@NotNull
default List<String> tabComplete(@NotNull Player sender,
@NotNull List<String> args) {
return new ArrayList<>();
}
/**
* Get the plugin.
*
@@ -83,7 +106,11 @@ public interface CommandBase {
* @deprecated Use {@link CommandBase#onExecute(CommandSender, List)} instead.
*/
@Deprecated(forRemoval = true)
CommandHandler getHandler();
default CommandHandler getHandler() {
return (a, b) -> {
};
}
/**
* Set the handler.
@@ -93,7 +120,9 @@ public interface CommandBase {
* @deprecated Handlers have been deprecated.
*/
@Deprecated(forRemoval = true)
void setHandler(@NotNull CommandHandler handler);
default void setHandler(@NotNull final CommandHandler handler) {
// Do nothing.
}
/**
* Get the tab completer.
@@ -103,7 +132,9 @@ public interface CommandBase {
* @deprecated Use {@link CommandBase#tabComplete(CommandSender, List)} instead.
*/
@Deprecated(forRemoval = true)
TabCompleteHandler getTabCompleter();
default TabCompleteHandler getTabCompleter() {
return (a, b) -> ImmutableList.of();
}
/**
* Set the tab completer.
@@ -113,5 +144,7 @@ public interface CommandBase {
* @deprecated Handlers have been deprecated.
*/
@Deprecated(forRemoval = true)
void setTabCompleter(@NotNull TabCompleteHandler handler);
default void setTabCompleter(@NotNull final TabCompleteHandler handler) {
// Do nothing.
}
}

View File

@@ -0,0 +1,71 @@
package com.willfp.eco.core.command.impl;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.PluginIdentifiableCommand;
import org.bukkit.command.TabCompleter;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* Delegates a bukkit command to an eco command (for registrations).
*/
public final class DelegatedBukkitCommand extends Command implements TabCompleter, PluginIdentifiableCommand {
/**
* The delegate command.
*/
private final PluginCommand delegate;
/**
* Create a new delegated command.
*
* @param delegate The delegate.
*/
public DelegatedBukkitCommand(@NotNull final PluginCommand delegate) {
super(delegate.getName());
this.delegate = delegate;
}
@Override
public boolean execute(@NotNull final CommandSender commandSender,
@NotNull final String label,
@NotNull final String[] args) {
return delegate.onCommand(commandSender, this, label, args);
}
@Override
public List<String> onTabComplete(@NotNull final CommandSender commandSender,
@NotNull final Command command,
@NotNull final String label,
@NotNull final String[] args) {
return delegate.onTabComplete(commandSender, command, label, args);
}
@NotNull
@Override
public Plugin getPlugin() {
return this.delegate.getPlugin();
}
@Nullable
@Override
public String getPermission() {
return this.delegate.getPermission();
}
@NotNull
@Override
public String getDescription() {
return this.delegate.getDescription() == null ? "" : this.delegate.getDescription();
}
@NotNull
@Override
public List<String> getAliases() {
return this.delegate.getAliases();
}
}

View File

@@ -145,6 +145,9 @@ abstract class HandledCommand implements CommandBase {
this.getHandler().onExecute(sender, Arrays.asList(args));
} else {
this.onExecute(sender, Arrays.asList(args));
if (sender instanceof Player player) {
this.onExecute(player, Arrays.asList(args));
}
}
}
@@ -202,7 +205,11 @@ abstract class HandledCommand implements CommandBase {
if (this.getTabCompleter() != null) {
return this.getTabCompleter().tabComplete(sender, Arrays.asList(args));
} else {
return this.tabComplete(sender, Arrays.asList(args));
List<String> completions = new ArrayList<>(this.tabComplete(sender, Arrays.asList(args)));
if (sender instanceof Player player) {
completions.addAll(this.tabComplete(player, Arrays.asList(args)));
}
return completions;
}
}

View File

@@ -1,14 +1,18 @@
package com.willfp.eco.core.command.impl;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.EcoPlugin;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandMap;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
/**
@@ -38,14 +42,60 @@ public abstract class PluginCommand extends HandledCommand implements CommandExe
/**
* Registers the command with the server,
* <p>
* Requires the command name to exist, defined in plugin.yml.
*/
public final void register() {
org.bukkit.command.PluginCommand command = Bukkit.getPluginCommand(this.getName());
assert command != null;
command.setExecutor(this);
command.setTabCompleter(this);
if (command != null) {
command.setExecutor(this);
command.setTabCompleter(this);
if (this.getDescription() != null) {
command.setDescription(this.getDescription());
}
List<String> aliases = new ArrayList<>(command.getAliases());
aliases.addAll(this.getAliases());
command.setAliases(aliases);
} else {
this.unregister();
CommandMap commandMap = getCommandMap();
commandMap.register(this.getPlugin().getName().toLowerCase(), new DelegatedBukkitCommand(this));
}
Eco.get().syncCommands();
}
/**
* Unregisters the command from the server.
*/
public final void unregister() {
CommandMap commandMap = getCommandMap();
Eco.get().unregisterCommand(this);
Eco.get().syncCommands();
}
/**
* Get aliases. Leave null if this command is from plugin.yml.
*
* @return The aliases.
*/
@NotNull
public List<String> getAliases() {
return new ArrayList<>();
}
/**
* Get description.
*
* @return The description.
*/
@Nullable
public String getDescription() {
return null;
}
/**
@@ -93,4 +143,19 @@ public abstract class PluginCommand extends HandledCommand implements CommandExe
return this.handleTabCompletion(sender, args);
}
/**
* Get the internal server CommandMap.
*
* @return The CommandMap.
*/
public static CommandMap getCommandMap() {
try {
Field field = Bukkit.getServer().getClass().getDeclaredField("commandMap");
field.setAccessible(true);
return (CommandMap) field.get(Bukkit.getServer());
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new NullPointerException("Command map wasn't found!");
}
}
}

View File

@@ -6,12 +6,12 @@ import org.jetbrains.annotations.Nullable;
/**
* Builder for configs to create them programmatically.
*/
public class BuildableConfig extends TransientConfig {
public class BuildableConfig extends GenericConfig {
/**
* Create a new empty config builder.
*/
public BuildableConfig() {
super();
}
/**

View File

@@ -0,0 +1,157 @@
package com.willfp.eco.core.config;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.config.interfaces.Config;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.Map;
/**
* Utilities / API methods for configs.
*/
public final class Configs {
/**
* Load a Config from a bukkit {@link ConfigurationSection}.
*
* @param config The ConfigurationSection.
* @return The config.
*/
@NotNull
public static Config fromBukkit(@Nullable final ConfigurationSection config) {
return config == null ? empty() : Eco.get().wrapConfigurationSection(config);
}
/**
* Load a config from an {@link InputStream}.
* <p>
* Only for yaml configs.
*
* @param stream The InputStream.
* @return The config.
*/
@NotNull
public static Config fromStream(@Nullable final InputStream stream) {
return stream != null ? fromBukkit(YamlConfiguration.loadConfiguration(
new InputStreamReader(stream)
)) : empty();
}
/**
* Load a config from a file.
*
* @param file The file.
* @return The config.
*/
@NotNull
public static Config fromFile(@Nullable final File file) {
if (file == null) {
return empty();
}
int lastIndex = file.getName().lastIndexOf(".");
if (lastIndex < 0) {
return empty();
}
for (ConfigType type : ConfigType.values()) {
if (file.getName().substring(lastIndex + 1).equalsIgnoreCase(type.getExtension())) {
return fromFile(file, type);
}
}
return empty();
}
/**
* Load a config from a file.
*
* @param file The file.
* @param type The type.
* @return The config.
*/
@NotNull
public static Config fromFile(@Nullable final File file,
@NotNull final ConfigType type) {
if (file == null) {
return empty();
}
try {
return Eco.get().createConfig(Files.readString(file.toPath()), type);
} catch (IOException e) {
return empty();
}
}
/**
* Load config from map (uses {@link ConfigType#JSON}).
*
* @param values The values.
* @return The config.
*/
@NotNull
public static Config fromMap(@NotNull final Map<String, Object> values) {
return fromMap(values, ConfigType.JSON);
}
/**
* Load config from map.
*
* @param values The values.
* @param type The type.
* @return The config.
*/
@NotNull
public static Config fromMap(@NotNull final Map<String, Object> values,
@NotNull final ConfigType type) {
return Eco.get().createConfig(values, type);
}
/**
* Create empty config (uses {@link ConfigType#JSON}).
*
* @return An empty config.
*/
@NotNull
public static Config empty() {
return fromMap(new HashMap<>(), ConfigType.JSON);
}
/**
* Create empty config.
*
* @param type The type.
* @return An empty config.
*/
@NotNull
public static Config empty(@NotNull final ConfigType type) {
return fromMap(new HashMap<>(), type);
}
/**
* Load config from string.
*
* @param contents The contents of the config.
* @param type The config type.
* @return The config.
*/
@NotNull
public static Config fromString(@NotNull final String contents,
@NotNull final ConfigType type) {
return Eco.get().createConfig(contents, type);
}
private Configs() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
}

View File

@@ -0,0 +1,27 @@
package com.willfp.eco.core.config;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.config.wrapper.ConfigWrapper;
import org.jetbrains.annotations.NotNull;
/**
* Generic config to simplify creating custom configs without having
* to meddle with delegation.
*/
public abstract class GenericConfig extends ConfigWrapper<Config> {
/**
* Create a new generic config.
*/
protected GenericConfig() {
super(Configs.empty());
}
/**
* Create a new generic config.
*
* @param type The config type.
*/
protected GenericConfig(@NotNull final ConfigType type) {
super(Configs.empty(type));
}
}

View File

@@ -20,7 +20,10 @@ import java.util.Map;
* Config that exists purely in the code, not linked to any file.
* <p>
* Use for inline configs to move data around or to add subsections to other configs.
*
* @deprecated Poorly named class, makes the config system seem needlessly complicated.
*/
@Deprecated(since = "6.44.0", forRemoval = true)
public class TransientConfig extends ConfigWrapper<Config> {
/**
* @param config The ConfigurationSection handle.

View File

@@ -2,7 +2,7 @@ package com.willfp.eco.core.config.interfaces;
import com.willfp.eco.core.config.BuildableConfig;
import com.willfp.eco.core.config.ConfigType;
import com.willfp.eco.core.config.TransientConfig;
import com.willfp.eco.core.config.Configs;
import com.willfp.eco.core.placeholder.AdditionalPlayer;
import com.willfp.eco.core.placeholder.InjectablePlaceholder;
import com.willfp.eco.core.placeholder.PlaceholderInjectable;
@@ -103,7 +103,7 @@ public interface Config extends Cloneable, PlaceholderInjectable {
*/
@NotNull
default Config getSubsection(@NotNull String path) {
return Objects.requireNonNullElse(getSubsectionOrNull(path), new TransientConfig());
return Objects.requireNonNullElse(getSubsectionOrNull(path), Configs.empty());
}
/**

View File

@@ -7,7 +7,6 @@ import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

View File

@@ -63,11 +63,29 @@ public interface Menu {
* @param player The player
* @param menu The menu.
* @return The slot.
* @deprecated Menu shouldn't be a parameter.
*/
default Slot getSlot(int row,
int column,
@NotNull Player player,
@NotNull Menu menu) {
@Deprecated(since = "6.46.0", forRemoval = true)
default Slot getSlot(final int row,
final int column,
@NotNull final Player player,
@NotNull final Menu menu) {
return this.getSlot(row, column, player);
}
/**
* Get a slot at a given row and column.
* <p>
* Defaults to static slot if no reactive slot exists.
*
* @param row The row.
* @param column The column.
* @param player The player
* @return The slot.
*/
default Slot getSlot(final int row,
final int column,
@NotNull final Player player) {
return this.getSlot(row, column);
}
@@ -110,15 +128,32 @@ public interface Menu {
}
/**
* Add state for a player.
* Set state for a player.
*
* @param player The player.
* @param key The key.
* @param value The state.
*/
void addState(@NotNull Player player,
@NotNull String key,
@Nullable Object value);
default void setState(@NotNull Player player,
@NotNull String key,
@Nullable Object value) {
// Blank method for backwards compatibility.
}
/**
* Add state for a player.
*
* @param player The player.
* @param key The key.
* @param value The state.
* @deprecated Poorly named, use setState instead.
*/
@Deprecated(since = "6.44.0", forRemoval = true)
default void addState(@NotNull Player player,
@NotNull String key,
@Nullable Object value) {
this.setState(player, key, value);
}
/**
* Remove state for a player.
@@ -221,7 +256,7 @@ public interface Menu {
@NotNull final NamespacedKey key,
@NotNull final PersistentDataType<T, Z> type,
@NotNull final Z value) {
this.addState(player, key.toString(), value);
this.setState(player, key.toString(), value);
}
/**

View File

@@ -107,7 +107,7 @@ public interface MenuBuilder extends PageBuilder {
* @return The builder.
*/
default MenuBuilder addPage(@NotNull final Page page) {
return this.addComponent(MenuLayer.TOP, 1, 1, page);
return this.addComponent(MenuLayer.UPPER, 1, 1, page);
}
/**
@@ -139,7 +139,7 @@ public interface MenuBuilder extends PageBuilder {
* @return The builder.
*/
default MenuBuilder maxPages(@NotNull final Function<Player, Integer> pages) {
return onOpen((player, menu) -> menu.addState(player, Page.MAX_PAGE_KEY, pages.apply(player)));
return this.onRender((player, menu) -> menu.setState(player, Page.MAX_PAGE_KEY, pages.apply(player)));
}
/**
@@ -195,6 +195,16 @@ public interface MenuBuilder extends PageBuilder {
return this;
}
/**
* Add an action to run on build.
*
* @param action The action.
* @return The builder.
*/
default MenuBuilder onBuild(@NotNull Consumer<Menu> action) {
return this;
}
/**
* Build the menu.
*

View File

@@ -0,0 +1,22 @@
package com.willfp.eco.core.gui.menu.events;
import com.willfp.eco.core.gui.menu.MenuEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
/**
* Represents a captive item change.
*
* @param row The row.
* @param column The column.
* @param before The previous item in the slot.
* @param after The new item in the slot.
*/
public record CaptiveItemChangeEvent(
int row,
int column,
@Nullable ItemStack before,
@Nullable ItemStack after
) implements MenuEvent {
}

View File

@@ -9,8 +9,6 @@ import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
/**
* A page is a component representing another menu.
* This allows full component support in pagination.
@@ -85,7 +83,7 @@ public final class Page implements GUIComponent {
delegate = Eco.get().blendMenuState(page, menu);
}
return page.getSlot(row, column, player, delegate);
return page.getSlot(row, column, player);
}
@Override

View File

@@ -48,7 +48,7 @@ public final class PageChanger implements GUIComponent {
return;
}
menu.addState(player, Page.PAGE_KEY, newPage);
menu.setState(player, Page.PAGE_KEY, newPage);
menu.callEvent(player, new PageChangeEvent(
newPage,
page
@@ -79,7 +79,7 @@ public final class PageChanger implements GUIComponent {
return null;
}
if (page >= maxPage - 1 && this.direction == Direction.FORWARDS) {
if (page >= maxPage && this.direction == Direction.FORWARDS) {
return null;
}

View File

@@ -1,12 +1,14 @@
package com.willfp.eco.core.gui.slot;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.fast.FastItemStack;
import com.willfp.eco.core.gui.slot.functional.SlotHandler;
import com.willfp.eco.core.items.Items;
import com.willfp.eco.util.StringUtils;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
@@ -37,7 +39,25 @@ public class ConfigSlot extends CustomSlot {
public ConfigSlot(@NotNull final Config config) {
this.config = config;
SlotBuilder builder = Slot.builder(Items.lookup(config.getString("item")));
ItemStack item = Items.lookup(config.getString("item")).getItem();
SlotBuilder builder = Slot.builder((player, menu) -> {
if (!config.has("lore")) {
return item;
} else {
FastItemStack fast = FastItemStack.wrap(item.clone());
List<String> newLore = new ArrayList<>(fast.getLore());
newLore.addAll(
StringUtils.formatList(
config.getStrings("lore"),
player,
StringUtils.FormatOption.WITH_PLACEHOLDERS
)
);
fast.setLore(newLore);
return fast.unwrap();
}
});
for (ClickType clickType : ClickType.values()) {
builder.onClick(

View File

@@ -4,6 +4,7 @@ import com.willfp.eco.core.gui.menu.Menu;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Base class for custom slot implementations.
@@ -31,7 +32,7 @@ public abstract class CustomSlot implements Slot {
}
@Override
public ItemStack getItemStack(@NotNull final Player player) {
public final @NotNull ItemStack getItemStack(@NotNull final Player player) {
if (delegate == null) {
throw new IllegalStateException("Custom Slot was not initialized!");
}
@@ -40,8 +41,8 @@ public abstract class CustomSlot implements Slot {
}
@Override
public boolean isCaptive(@NotNull final Player player,
@NotNull final Menu menu) {
public final boolean isCaptive(@NotNull final Player player,
@NotNull final Menu menu) {
if (delegate == null) {
throw new IllegalStateException("Custom Slot was not initialized!");
}
@@ -50,7 +51,18 @@ public abstract class CustomSlot implements Slot {
}
@Override
public boolean isCaptiveFromEmpty() {
public final boolean isAllowedCaptive(@NotNull final Player player,
@NotNull final Menu menu,
@Nullable final ItemStack itemStack) {
if (delegate == null) {
throw new IllegalStateException("Custom Slot was not initialized!");
}
return delegate.isAllowedCaptive(player, menu, itemStack);
}
@Override
public final boolean isCaptiveFromEmpty() {
if (delegate == null) {
throw new IllegalStateException("Custom Slot was not initialized!");
}
@@ -59,8 +71,8 @@ public abstract class CustomSlot implements Slot {
}
@Override
public final Slot getActionableSlot(@NotNull final Player player,
@NotNull final Menu menu) {
public final @NotNull Slot getActionableSlot(@NotNull final Player player,
@NotNull final Menu menu) {
return delegate;
}

View File

@@ -5,6 +5,7 @@ import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Base class for custom slot implementations.
@@ -29,19 +30,26 @@ public abstract class ReactiveSlot implements Slot {
@NotNull final Menu menu);
@Override
public ItemStack getItemStack(@NotNull final Player player) {
return new ItemStack(Material.STONE);
public @NotNull ItemStack getItemStack(@NotNull final Player player) {
return new ItemStack(Material.AIR);
}
@Override
public boolean isCaptive(@NotNull final Player player,
@NotNull final Menu menu) {
public final boolean isCaptive(@NotNull final Player player,
@NotNull final Menu menu) {
return getSlot(player, menu).isCaptive(player, menu);
}
@Override
public final Slot getActionableSlot(@NotNull final Player player,
@NotNull final Menu menu) {
public final boolean isAllowedCaptive(@NotNull final Player player,
@NotNull final Menu menu,
@Nullable final ItemStack itemStack) {
return getSlot(player, menu).isAllowedCaptive(player, menu, itemStack);
}
@Override
public final @NotNull Slot getActionableSlot(@NotNull final Player player,
@NotNull final Menu menu) {
return getSlot(player, menu);
}

View File

@@ -9,6 +9,7 @@ import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.function.Function;
@@ -30,6 +31,7 @@ public interface Slot extends GUIComponent {
* @param player The player.
* @return The ItemStack.
*/
@NotNull
ItemStack getItemStack(@NotNull Player player);
/**
@@ -44,6 +46,20 @@ public interface Slot extends GUIComponent {
return false;
}
/**
* If the slot allows a certain item to be placed in it.
*
* @param player The player.
* @param menu The menu.
* @param itemStack The item; use null if the item is unknown.
* @return If captive.
*/
default boolean isAllowedCaptive(@NotNull final Player player,
@NotNull final Menu menu,
@Nullable final ItemStack itemStack) {
return this.isCaptive(player, menu);
}
/**
* Get the actionable slot to be shown.
* <p>
@@ -60,6 +76,7 @@ public interface Slot extends GUIComponent {
* @param menu The menu.
* @return The slot.
*/
@NotNull
default Slot getActionableSlot(@NotNull final Player player,
@NotNull final Menu menu) {
return this;
@@ -125,7 +142,9 @@ public interface Slot extends GUIComponent {
*
* @param provider The provider.
* @return The builder.
* @deprecated This method was written incorrectly, should have been a Player + Menu function.
*/
@Deprecated(since = "6.45.0", forRemoval = true)
static SlotBuilder builder(@NotNull final Function<Player, ItemStack> provider) {
return Eco.get().createSlotBuilder((player, menu) -> provider.apply(player));
}

View File

@@ -1,5 +1,6 @@
package com.willfp.eco.core.gui.slot;
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.SlotModifier;
import com.willfp.eco.core.gui.slot.functional.SlotUpdater;
@@ -143,7 +144,17 @@ public interface SlotBuilder {
* @param predicate The predicate. Returns true when the slot should not be captive.
* @return The builder.
*/
SlotBuilder notCaptiveFor(@NotNull Predicate<Player> predicate);
SlotBuilder notCaptiveFor(@NotNull final Predicate<Player> predicate);
/**
* Set a whitelist for allowed captive items.
*
* @param filter The filter.
* @return The builder.
*/
default SlotBuilder setCaptiveFilter(@NotNull final CaptiveFilter filter) {
return this;
}
/**
* Set the ItemStack updater.

View File

@@ -0,0 +1,25 @@
package com.willfp.eco.core.gui.slot.functional;
import com.willfp.eco.core.gui.menu.Menu;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Interface to test if a captive slot is allowed to contain an item given a player and a menu.
*/
@FunctionalInterface
public interface CaptiveFilter {
/**
* Get if allowed.
*
* @param player The player.
* @param menu The menu.
* @param itemStack The item.
* @return If captive.
*/
boolean isAllowed(@NotNull Player player,
@NotNull Menu menu,
@Nullable ItemStack itemStack);
}

View File

@@ -51,10 +51,15 @@ public final class PlaceholderManager {
.expireAfterWrite(50, TimeUnit.MILLISECONDS)
.build(key -> key.entry.getValue(key.player));
/**
* The default PlaceholderAPI pattern; brought in for compatibility.
*/
private static final Pattern PATTERN = Pattern.compile("[%]([^% ]+)[%]");
/**
* Empty injectable object.
*/
private static final PlaceholderInjectable EMPTY_INJECTABLE = new PlaceholderInjectable() {
public static final PlaceholderInjectable EMPTY_INJECTABLE = new PlaceholderInjectable() {
@Override
public void clearInjectedPlaceholders() {
// Do nothing.
@@ -67,11 +72,6 @@ public final class PlaceholderManager {
}
};
/**
* The default PlaceholderAPI pattern; brought in for compatibility.
*/
private static final Pattern PATTERN = Pattern.compile("[%]([^%]+)[%]");
/**
* Register a new placeholder integration.
*

View File

@@ -1,6 +1,8 @@
package com.willfp.eco.core.integrations.shop;
import com.willfp.eco.core.integrations.Integration;
import com.willfp.eco.core.price.Price;
import com.willfp.eco.core.price.impl.PriceFree;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.inventory.ItemStack;
@@ -29,12 +31,41 @@ public interface ShopIntegration extends Integration {
return null;
}
/**
* Get if an item is sellable for a player.
*
* @param itemStack The item.
* @param player The player.
* @return If sellable.
*/
default boolean isSellable(@NotNull final ItemStack itemStack,
@NotNull final Player player) {
return false;
}
/**
* Get the value of one of an item for a player.
* <p>
* For example, if you pass in a stack, it will only return the value of <b>one</b> item, not the full stack.
*
* @param itemStack The item.
* @param player The player.
* @return The price.
*/
@NotNull
default Price getUnitValue(@NotNull final ItemStack itemStack,
@NotNull final Player player) {
return new PriceFree();
}
/**
* Get the price of an item.
*
* @param itemStack The item.
* @return The price.
* @deprecated Use getValue instead.
*/
@Deprecated(since = "6.47.0", forRemoval = true)
default double getPrice(@NotNull final ItemStack itemStack) {
// Do nothing unless overridden.
return 0.0;
@@ -46,9 +77,11 @@ public interface ShopIntegration extends Integration {
* @param itemStack The item.
* @param player The player.
* @return The price.
* @deprecated Use getValue instead.
*/
@Deprecated(since = "6.47.0", forRemoval = true)
default double getPrice(@NotNull final ItemStack itemStack,
@NotNull final Player player) {
return getPrice(itemStack);
return getUnitValue(itemStack, player).getValue(player);
}
}

View File

@@ -1,10 +1,9 @@
package com.willfp.eco.core.integrations.shop;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.price.Price;
import com.willfp.eco.core.price.impl.PriceFree;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -39,12 +38,57 @@ public final class ShopManager {
}
}
/**
* Get if an item is sellable for a player.
*
* @param itemStack The item.
* @param player The player.
* @return If sellable.
*/
public static boolean isSellable(@Nullable final ItemStack itemStack,
@NotNull final Player player) {
if (itemStack == null) {
return false;
}
for (ShopIntegration integration : REGISTERED) {
return integration.isSellable(itemStack, player);
}
return false;
}
/**
* Get the value of one of an item for a player.
* <p>
* For example, if you pass in a stack, it will only return the value of <b>one</b> item, not the full stack.
*
* @param itemStack The item.
* @param player The player.
* @return The price.
*/
@NotNull
public static Price getUnitValue(@Nullable final ItemStack itemStack,
@NotNull final Player player) {
if (itemStack == null) {
return new PriceFree();
}
for (ShopIntegration integration : REGISTERED) {
return integration.getUnitValue(itemStack, player);
}
return new PriceFree();
}
/**
* Get the price of an item.
*
* @param itemStack The item.
* @return The price.
* @deprecated Use getValue instead. This will always return 0 as prices depend on players.
*/
@Deprecated(since = "6.47.0", forRemoval = true)
public static double getItemPrice(@Nullable final ItemStack itemStack) {
return getItemPrice(itemStack, null);
}
@@ -55,19 +99,17 @@ public final class ShopManager {
* @param itemStack The item.
* @param player The player.
* @return The price.
* @deprecated Use getValue instead. Null players / null items will always return 0.
*/
@Deprecated(since = "6.47.0", forRemoval = true)
public static double getItemPrice(@Nullable final ItemStack itemStack,
@Nullable final Player player) {
if (itemStack == null) {
if (itemStack == null || player == null) {
return 0.0;
}
for (ShopIntegration shopIntegration : REGISTERED) {
if (player == null) {
return shopIntegration.getPrice(itemStack);
} else {
return shopIntegration.getPrice(itemStack, player);
}
return shopIntegration.getUnitValue(itemStack, player).getValue(player, itemStack.getAmount());
}
return 0.0;

View File

@@ -1,5 +1,7 @@
package com.willfp.eco.core.integrations.shop;
import com.willfp.eco.core.price.Price;
import com.willfp.eco.core.price.impl.PriceEconomy;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerEvent;
@@ -19,7 +21,12 @@ public class ShopSellEvent extends PlayerEvent {
/**
* The sell price.
*/
private double price;
private Price price;
/**
* The price multiplier.
*/
private double multiplier;
/**
* The item to be sold.
@@ -33,31 +40,64 @@ public class ShopSellEvent extends PlayerEvent {
* @param who The player.
* @param price The price.
* @param item The item.
* @deprecated Use the price system instead.
*/
@Deprecated(since = "6.47.0", forRemoval = true)
public ShopSellEvent(@NotNull final Player who,
final double price,
@Nullable final ItemStack item) {
this(who, new PriceEconomy(price), item);
}
/**
* Create new shop sell event.
*
* @param who The player.
* @param price The price.
* @param item The item.
*/
public ShopSellEvent(@NotNull final Player who,
@NotNull final Price price,
@Nullable final ItemStack item) {
this(who, price, item, 1.0);
}
/**
* Create new shop sell event.
*
* @param who The player.
* @param price The price.
* @param item The item.
* @param multiplier The multiplier.
*/
public ShopSellEvent(@NotNull final Player who,
@NotNull final Price price,
@Nullable final ItemStack item,
final double multiplier) {
super(who);
this.price = price;
this.item = item;
this.multiplier = multiplier;
}
/**
* Get the price.
* Get the value.
*
* @return The price.
* @return The value.
*/
public double getPrice() {
@NotNull
public Price getValue() {
return this.price;
}
/**
* Set the price.
* Set the value.
*
* @param price The price.
* @param price The value.
*/
public void setPrice(final double price) {
public void setValue(@NotNull final Price price) {
this.price = price;
}
@@ -81,6 +121,46 @@ public class ShopSellEvent extends PlayerEvent {
return item != null;
}
/**
* Get the price multiplier.
*
* @return The multiplier.
*/
public double getMultiplier() {
return multiplier;
}
/**
* Set the price multiplier.
*
* @param multiplier The multiplier.
*/
public void setMultiplier(final double multiplier) {
this.multiplier = multiplier;
}
/**
* Get the price.
*
* @return The price.
* @deprecated Use the price system instead.
*/
@Deprecated(since = "6.47.0", forRemoval = true)
public double getPrice() {
return this.getValue().getValue(player);
}
/**
* Set the price.
*
* @param price The price.
* @deprecated Use the price system instead.
*/
@Deprecated(since = "6.47.0", forRemoval = true)
public void setPrice(final double price) {
this.setValue(new PriceEconomy(price));
}
/**
* Bukkit parity.
*

View File

@@ -25,6 +25,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -86,6 +87,11 @@ public final class Items {
*/
private static final TestableItem EMPTY_TESTABLE_ITEM = new EmptyTestableItem();
/**
* Friendly material names (without underscores, etc.)
*/
private static final Map<String, Material> FRIENDLY_MATERIAL_NAMES = new HashMap<>();
/**
* Register a new custom item.
*
@@ -216,7 +222,7 @@ public final class Items {
if (isWildcard) {
itemType = itemType.substring(1);
}
Material material = Material.getMaterial(itemType.toUpperCase());
Material material = FRIENDLY_MATERIAL_NAMES.get(itemType.toLowerCase());
if (material == null || material == Material.AIR) {
return new EmptyTestableItem();
}
@@ -565,4 +571,25 @@ public final class Items {
private Items() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
static {
for (Material material : Material.values()) {
FRIENDLY_MATERIAL_NAMES.put(material.name().toLowerCase(), material);
String oneWord = material.name().toLowerCase().replace("_", "");
if (!FRIENDLY_MATERIAL_NAMES.containsKey(oneWord)) {
FRIENDLY_MATERIAL_NAMES.put(oneWord, material);
}
String plural = material.name().toLowerCase() + "s";
if (!FRIENDLY_MATERIAL_NAMES.containsKey(plural)) {
FRIENDLY_MATERIAL_NAMES.put(plural, material);
}
String oneWordPlural = oneWord + "s";
if (!FRIENDLY_MATERIAL_NAMES.containsKey(oneWordPlural)) {
FRIENDLY_MATERIAL_NAMES.put(oneWordPlural, material);
}
}
}
}

View File

@@ -14,7 +14,6 @@ import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
@@ -71,7 +70,7 @@ public abstract class AbstractItemStackBuilder<T extends ItemMeta, U extends Abs
@Override
public U setAmount(final int amount) {
Validate.isTrue(amount >= 1 && amount <= base.getMaxStackSize());
Validate.isTrue(amount >= 1);
base.setAmount(amount);
return (U) this;
}

View File

@@ -0,0 +1,63 @@
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 org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
/**
* Represents a context to do math in.
*
* @param injectableContext The PlaceholderInjectable context.
* @param player The player.
* @param additionalPlayers The additional players.
*/
public record MathContext(
@NotNull PlaceholderInjectable injectableContext,
@Nullable Player player,
@NotNull Collection<AdditionalPlayer> additionalPlayers
) {
/**
* Empty math context.
*/
public static final MathContext EMPTY = new MathContext(
PlaceholderManager.EMPTY_INJECTABLE,
null,
Collections.emptyList()
);
/**
* Create MathContext of a PlaceholderInjectable context.
*
* @param injectableContext The PlaceholderInjectable context.
* @return The MathContext.
*/
public static MathContext of(@NotNull final PlaceholderInjectable injectableContext) {
return new MathContext(
injectableContext,
null,
Collections.emptyList()
);
}
/**
* Copy a MathContext with a player.
*
* @param context The context.
* @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(),
player,
context.additionalPlayers()
);
}
}

View File

@@ -0,0 +1,28 @@
package com.willfp.eco.core.particle;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* Create particles.
*/
public interface ParticleFactory {
/**
* Get the names (how the particle looks in lookup strings).
* <p>
* For example, for RGB particles this would be 'rgb', 'color', etc.
*
* @return The allowed names.
*/
@NotNull List<String> getNames();
/**
* Create the particle
*
* @param key The key.
* @return The particle.
*/
@Nullable SpawnableParticle create(@NotNull String key);
}

View File

@@ -0,0 +1,83 @@
package com.willfp.eco.core.particle;
import com.willfp.eco.core.particle.impl.EmptyParticle;
import com.willfp.eco.core.particle.impl.SimpleParticle;
import com.willfp.eco.util.StringUtils;
import org.bukkit.Particle;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Class to manage particles.
*/
public final class Particles {
/**
* All factories.
*/
private static final Map<String, ParticleFactory> FACTORIES = new ConcurrentHashMap<>();
/**
* Register a new particle factory.
*
* @param factory The factory.
*/
public static void registerParticleFactory(@NotNull final ParticleFactory factory) {
for (String name : factory.getNames()) {
FACTORIES.put(name.toLowerCase(), factory);
}
}
/**
* Lookup a particle from a string.
* <p>
* A particle string should look like {@code magic}, {@code rgb:00ff00}
*
* @param key The key.
* @return The particle, or an {@link EmptyParticle} if invalid.
*/
@NotNull
public static SpawnableParticle lookup(@NotNull final String key) {
String[] args = StringUtils.parseTokens(key.toLowerCase());
if (args.length == 0) {
return new EmptyParticle();
}
SpawnableParticle spawnableParticle;
String[] split = args[0].split(":");
if (split.length == 1) {
try {
Particle particle = Particle.valueOf(args[0].toUpperCase());
spawnableParticle = new SimpleParticle(particle);
} catch (IllegalArgumentException e) {
spawnableParticle = new EmptyParticle();
}
} else if (split.length == 2) {
String name = split[0];
String factoryKey = split[1];
ParticleFactory factory = FACTORIES.get(name);
if (factory == null) {
spawnableParticle = new EmptyParticle();
} else {
spawnableParticle = factory.create(factoryKey);
}
} else {
return new EmptyParticle();
}
if (spawnableParticle == null || spawnableParticle instanceof EmptyParticle) {
return new EmptyParticle();
}
return spawnableParticle;
}
private Particles() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
}

View File

@@ -0,0 +1,27 @@
package com.willfp.eco.core.particle;
import org.bukkit.Location;
import org.jetbrains.annotations.NotNull;
/**
* A particle that can be spawned.
*/
public interface SpawnableParticle {
/**
* Spawn the particle at a location.
*
* @param location The location.
* @param amount The amount to spawn.
*/
void spawn(@NotNull Location location,
int amount);
/**
* Spawn the particle at a location.
*
* @param location The location.
*/
default void spawn(@NotNull Location location) {
spawn(location, 1);
}
}

View File

@@ -0,0 +1,16 @@
package com.willfp.eco.core.particle.impl;
import com.willfp.eco.core.particle.SpawnableParticle;
import org.bukkit.Location;
import org.jetbrains.annotations.NotNull;
/**
* Empty (invalid) particle that is spawned when an invalid key is provided.
*/
public final class EmptyParticle implements SpawnableParticle {
@Override
public void spawn(@NotNull final Location location,
final int amount) {
// Do nothing.
}
}

View File

@@ -0,0 +1,38 @@
package com.willfp.eco.core.particle.impl;
import com.willfp.eco.core.particle.SpawnableParticle;
import org.bukkit.Location;
import org.bukkit.Particle;
import org.bukkit.World;
import org.jetbrains.annotations.NotNull;
/**
* Empty (invalid) particle that is spawned when an invalid key is provided.
*/
public final class SimpleParticle implements SpawnableParticle {
/**
* The particle to be spawned.
*/
private final Particle particle;
/**
* Create a new spawnable particle.
*
* @param particle The particle.
*/
public SimpleParticle(@NotNull final Particle particle) {
this.particle = particle;
}
@Override
public void spawn(@NotNull final Location location,
final int amount) {
World world = location.getWorld();
if (world == null) {
return;
}
world.spawnParticle(particle, location, amount, 0, 0, 0, 0, null);
}
}

View File

@@ -0,0 +1,156 @@
package com.willfp.eco.core.price;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* A group of {@link ConfiguredPrice}s in order to show them
* to players in one go.
*/
public final class CombinedDisplayPrice {
/**
* Maps configured prices to multipliers.
*/
private final Map<ConfiguredPrice, Double> prices;
/**
* The player to format for.
*/
private final Player player;
/**
* Initialize a new combined price mapping formatters to multipliers.
*
* @param player The player.
* @param prices The prices.
*/
private CombinedDisplayPrice(@NotNull final Player player,
@NotNull final Map<ConfiguredPrice, Double> prices) {
this.player = player;
this.prices = prices;
}
/**
* Get the display strings.
*
* @return The display strings.
*/
@NotNull
public String[] getDisplayStrings() {
List<String> displayStrings = new ArrayList<>();
for (Map.Entry<ConfiguredPrice, Double> entry : prices.entrySet()) {
displayStrings.add(entry.getKey().getDisplay(player, entry.getValue()));
}
return displayStrings.toArray(new String[0]);
}
/**
* The builder.
*/
public static class Builder {
/**
* All multiplied prices.
*/
private final List<MultipliedPrice> prices = new ArrayList<>();
/**
* The player.
*/
private final Player player;
/**
* Create a new builder.
*
* @param player The player.
*/
Builder(@NotNull final Player player) {
this.player = player;
}
/**
* Add a new price with a certain multiplier.
*
* @param price The price.
* @param multiplier The multiplier.
* @return The builder.
*/
@NotNull
public Builder add(@NotNull final ConfiguredPrice price,
final double multiplier) {
prices.add(new MultipliedPrice(price, multiplier));
return this;
}
/**
* Add a new price.
*
* @param price The price.
* @return The builder.
*/
@NotNull
public Builder add(@NotNull final ConfiguredPrice price) {
return this.add(price, 1D);
}
/**
* Build into a {@link CombinedDisplayPrice}.
*
* @return The combined price.
*/
@NotNull
public CombinedDisplayPrice build() {
Map<ConfiguredPrice, Double> unitPrices = new HashMap<>();
// Take first configured price at each ID as the format for all prices with that ID.
for (MultipliedPrice price : prices) {
// Find the base price.
ConfiguredPrice base = unitPrices.keySet()
.stream()
.filter(it -> it.getIdentifier().equals(price.price().getIdentifier()))
.findFirst()
.orElse(price.price());
// Find the multiplier for a value of 1, e.g. a price that's worth 20 will be 0.05.
double unitMultiplier = 1 / base.getValue(player);
double currentMultiplier = unitPrices.getOrDefault(base, 0D);
currentMultiplier += unitMultiplier * price.price().getValue(player, price.multiplier());
unitPrices.put(base, currentMultiplier);
}
return new CombinedDisplayPrice(player, unitPrices);
}
/**
* A price with a multiplier.
*
* @param price The price.
* @param multiplier The multiplier.
*/
private record MultipliedPrice(
@NotNull ConfiguredPrice price,
double multiplier
) {
}
}
/**
* Create a new builder for a player.
*
* @param player The player.
* @return The builder.
*/
@NotNull
public static Builder builder(@NotNull final Player player) {
return new Builder(player);
}
}

View File

@@ -0,0 +1,177 @@
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.price.impl.PriceFree;
import com.willfp.eco.core.serialization.ConfigDeserializer;
import com.willfp.eco.util.NumberUtils;
import com.willfp.eco.util.StringUtils;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
/**
* A price that can be shown to a player.
*/
public final class ConfiguredPrice implements Price {
/**
* The deserializer.
*/
private static final ConfigDeserializer<ConfiguredPrice> DESERIALIZER = new Deserializer();
/**
* Free.
*/
public static final ConfiguredPrice FREE = new ConfiguredPrice(
new PriceFree(),
"Free"
);
/**
* The price.
*/
private final Price price;
/**
* The format string.
*/
private final String formatString;
/**
* Create a new Configured Price.
*
* @param price The price.
* @param formatString The format string.
*/
public ConfiguredPrice(@NotNull final Price price,
@NotNull final String formatString) {
this.price = price;
this.formatString = formatString;
}
@Override
public boolean canAfford(@NotNull final Player player,
final double multiplier) {
return this.price.canAfford(player, multiplier);
}
@Override
public void pay(@NotNull final Player player,
final double multiplier) {
this.price.pay(player, multiplier);
}
@Override
public void giveTo(@NotNull final Player player,
final double multiplier) {
this.price.giveTo(player, multiplier);
}
@Override
public double getValue(@NotNull final Player player,
final double multiplier) {
return this.price.getValue(player, multiplier);
}
@Override
public double getMultiplier(@NotNull final Player player) {
return this.price.getMultiplier(player);
}
@Override
public void setMultiplier(@NotNull final Player player,
final double multiplier) {
this.price.setMultiplier(player, multiplier);
}
@Override
public String getIdentifier() {
return this.price.getIdentifier();
}
/**
* Get the price that this delegates to.
*
* @return The price.
*/
public Price getPrice() {
return price;
}
/**
* Get the display string for a player.
*
* @param player The player.
* @return The display string.
*/
public String getDisplay(@NotNull final Player player) {
return this.getDisplay(player, 1.0);
}
/**
* Get the display string for a player.
*
* @param player The player.
* @param multiplier The multiplier.
* @return The display string.
*/
public String getDisplay(@NotNull final Player player,
final double multiplier) {
return StringUtils.format(
formatString.replace("%value%", NumberUtils.format(this.getPrice().getValue(player, multiplier))),
player,
StringUtils.FormatOption.WITH_PLACEHOLDERS
);
}
/**
* Parse a configured price from config.
*
* @param config The config.
* @return The price, or null if it's invalid.
*/
@Nullable
public static ConfiguredPrice create(@NotNull final Config config) {
return DESERIALIZER.deserialize(config);
}
/**
* Parse a configured price from config.
*
* @param config The config.
* @return The price, or free if invalid.
*/
@NotNull
public static ConfiguredPrice createOrFree(@NotNull final Config config) {
return Objects.requireNonNullElse(create(config), FREE);
}
/**
* The deserializer for {@link ConfiguredPrice}.
*/
private static final class Deserializer implements ConfigDeserializer<ConfiguredPrice> {
@Override
@Nullable
public ConfiguredPrice deserialize(@NotNull final Config config) {
if (!(
config.has("value")
&& config.has("type")
&& config.has("display")
)) {
return null;
}
String formatString = config.getString("display");
Price price = Prices.create(
config.getString("value"),
config.getString("type"),
MathContext.of(config)
);
return new ConfiguredPrice(price, formatString);
}
}
}

View File

@@ -0,0 +1,171 @@
package com.willfp.eco.core.price;
import org.apache.commons.lang.NotImplementedException;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
/**
* A price that a player should pay.
* <p>
* There are important implementation details:
* <p>
* For backwards compatibility, all methods are default, however you must override the following:
* <ul>
* <li><code>canAfford(Player, double)</code></li>
* <li><code>pay(Player, double)</code></li>
* <li><code>giveTo(Player, double)</code></li>
* <li><code>getValue(Player, double)</code></li>
* <li><code>getMultiplier(Player)</code></li>
* <li><code>setMultiplier(Player, double)</code></li>
* </ul>
* Otherwise, your implementation will throw {@link NotImplementedException}.
* <p>
* Also, getValue() should always return the value with player multipliers applied.
*/
public interface Price {
/**
* Get if a player can afford to pay the price.
*
* @param player The player.
* @return If the player can afford.
*/
default boolean canAfford(@NotNull final Player player) {
return this.canAfford(player, 1);
}
/**
* Get if a player can afford to pay x times the price.
*
* @param player The player.
* @param multiplier The multiplier.
* @return If the player can afford.
*/
default boolean canAfford(@NotNull final Player player,
final double multiplier) {
throw new NotImplementedException("Override canAfford(Player, double) in your Price implementation!");
}
/**
* Make the player pay the price.
* <p>
* Check canAfford first.
*
* @param player The player.
*/
default void pay(@NotNull final Player player) {
this.pay(player, 1);
}
/**
* Make the player pay the price x times.
* <p>
* Check canAfford first.
*
* @param player The player.
* @param multiplier The multiplier.
*/
default void pay(@NotNull final Player player,
final double multiplier) {
throw new NotImplementedException("Override pay(Player, double) in your Price implementation!");
}
/**
* Give the price to the player.
*
* @param player The player.
*/
default void giveTo(@NotNull final Player player) {
this.giveTo(player, 1);
}
/**
* Give the price to the player x times.
*
* @param player The player.
* @param multiplier The multiplier.
*/
default void giveTo(@NotNull final Player player,
final double multiplier) {
throw new NotImplementedException("Override giveTo(Player, double) in your Price implementation!");
}
/**
* Get the numerical value that backs this price.
*
* @param player The player.
* @return The value.
*/
default double getValue(@NotNull final Player player) {
return getValue(player, 1);
}
/**
* Get the numeral value that backs this price multiplied x times.
*
* @param player The player.
* @param multiplier The multiplier.
* @return The value.
*/
default double getValue(@NotNull final Player player,
final double multiplier) {
throw new NotImplementedException("Override getValue(Player, double) in your Price implementation!");
}
/**
* Get the value multiplier for the player.
*
* @param player The player.
* @return The multiplier.
*/
default double getMultiplier(@NotNull final Player player) {
return 1;
}
/**
* Set the value multiplier for the player.
*
* @param player The player.
* @param multiplier The multiplier.
*/
default void setMultiplier(@NotNull final Player player,
final double multiplier) {
throw new NotImplementedException("Override setMultiplier(Player, double) in your Price implementation!");
}
/**
* Get the identifier of this price (as type/instance checks break with delegation,
* this is used for combining prices, etc.)
* <p>
* By default, this uses the class name, but it's good practice to override this.
* <p>
* It's also good practice to prefix your identifiers with some kind of namespace or
* internal ID, in order to prevent conflicts.
*
* @return The identifier.
*/
default String getIdentifier() {
return this.getClass().getName();
}
/**
* If the price is backed by a value, get it here.
*
* @return The value.
* @deprecated Use getValue(Player) instead.
*/
@Deprecated(since = "6.45.0", forRemoval = true)
default double getValue() {
return 0;
}
/**
* If the price is backed by a value, set it here.
*
* @param value The value.
* @deprecated Values shouldn't be fixed. This method should never work.
*/
@Deprecated(since = "6.45.0", forRemoval = true)
default void setValue(final double value) {
// Override when needed.
}
}

View File

@@ -0,0 +1,46 @@
package com.willfp.eco.core.price;
import com.willfp.eco.core.math.MathContext;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.function.Function;
/**
* Create prices.
* <p>
* You must override one of the create methods.
*/
public interface PriceFactory {
/**
* Get the names (how the price looks in lookup strings).
* <p>
* For example, for XP Levels this would be 'l', 'xpl', 'levels', etc.
*
* @return The allowed names.
*/
@NotNull List<String> getNames();
/**
* Create the price.
*
* @param value The value.
* @return The price.
*/
default @NotNull Price create(final double value) {
return create(MathContext.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.
* @return The price.
*/
default @NotNull Price create(@NotNull final MathContext baseContext,
@NotNull final Function<MathContext, Double> function) {
return create(function.apply(baseContext));
}
}

View File

@@ -0,0 +1,103 @@
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.price.impl.PriceEconomy;
import com.willfp.eco.core.price.impl.PriceFree;
import com.willfp.eco.core.price.impl.PriceItem;
import com.willfp.eco.core.recipe.parts.EmptyTestableItem;
import com.willfp.eco.util.NumberUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
/**
* Class to manage prices.
*/
public final class Prices {
/**
* All factories.
*/
private static final Map<String, PriceFactory> FACTORIES = new ConcurrentHashMap<>();
/**
* Register a new price factory.
*
* @param factory The factory.
*/
public static void registerPriceFactory(@NotNull final PriceFactory factory) {
for (String name : factory.getNames()) {
FACTORIES.put(name.toLowerCase(), factory);
}
}
/**
* Create price from an expression (representing the value),
* and a price name.
* <p>
* Supports items as price names.
*
* @param expression The expression for the value.
* @param priceName The price name.
* @return The price, or free if invalid.
*/
@NotNull
public static Price create(@NotNull final String expression,
@Nullable final String priceName) {
return create(expression, priceName, MathContext.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.
*/
@NotNull
public static Price create(@NotNull final String expression,
@Nullable final String priceName,
@NotNull final MathContext context) {
Function<MathContext, Double> function = (ctx) -> NumberUtils.evaluateExpression(
expression,
ctx
);
if (function.apply(context) <= 0) {
return new PriceFree();
}
// Default to economy
if (priceName == null) {
return new PriceEconomy(context, function);
}
// Find price factory
PriceFactory factory = FACTORIES.get(priceName);
// If no price factory, default to item price
if (factory == null) {
TestableItem item = Items.lookup(priceName);
if (item instanceof EmptyTestableItem) {
return new PriceFree();
}
return new PriceItem(context, function, item);
} else {
return factory.create(context, function);
}
}
private Prices() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
}

View File

@@ -0,0 +1,93 @@
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.price.Price;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.function.Function;
/**
* Economy-based price (for Vault, Treasury, etc.)
*/
public final class PriceEconomy implements Price {
/**
* The value of the price.
*/
private final Function<MathContext, Double> function;
/**
* The base math context.
*/
private final MathContext baseContext;
/**
* The multipliers.
*/
private final Map<UUID, Double> multipliers = new HashMap<>();
/**
* Create a new economy-based price.
*
* @param value The value.
*/
public PriceEconomy(final double value) {
this(MathContext.EMPTY, ctx -> value);
}
/**
* Create a new economy-based price.
*
* @param baseContext The base context.
* @param function The function.
*/
public PriceEconomy(@NotNull final MathContext baseContext,
@NotNull final Function<MathContext, Double> function) {
this.baseContext = baseContext;
this.function = function;
}
@Override
public boolean canAfford(@NotNull final Player player,
final double multiplier) {
return EconomyManager.getBalance(player) >= getValue(player, multiplier);
}
@Override
public void pay(@NotNull final Player player,
final double multiplier) {
EconomyManager.removeMoney(player, getValue(player, multiplier));
}
@Override
public void giveTo(@NotNull final Player player,
final double multiplier) {
EconomyManager.giveMoney(player, getValue(player, multiplier));
}
@Override
public double getValue(@NotNull final Player player,
final double multiplier) {
return this.function.apply(MathContext.copyWithPlayer(baseContext, player)) * getMultiplier(player) * multiplier;
}
@Override
public double getMultiplier(@NotNull final Player player) {
return this.multipliers.getOrDefault(player.getUniqueId(), 1.0);
}
@Override
public void setMultiplier(@NotNull final Player player,
final double multiplier) {
this.multipliers.put(player.getUniqueId(), multiplier);
}
@Override
public String getIdentifier() {
return "eco:economy";
}
}

View File

@@ -0,0 +1,57 @@
package com.willfp.eco.core.price.impl;
import com.willfp.eco.core.price.Price;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
/**
* Free (default) price.
*/
public final class PriceFree implements Price {
/**
* Create a new free price.
*/
public PriceFree() {
}
@Override
public boolean canAfford(@NotNull final Player player,
final double multiplier) {
return true;
}
@Override
public void pay(@NotNull final Player player,
final double multiplier) {
// Nothing.
}
@Override
public void giveTo(@NotNull final Player player,
final double multiplier) {
// Nothing.
}
@Override
public double getMultiplier(@NotNull final Player player) {
return 1.0;
}
@Override
public void setMultiplier(@NotNull final Player player,
final double multiplier) {
// Nothing.
}
@Override
public double getValue(@NotNull final Player player,
final double multiplier) {
return 0;
}
@Override
public String getIdentifier() {
return "eco:free";
}
}

View File

@@ -0,0 +1,160 @@
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.price.Price;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.function.Function;
/**
* Item-based price.
*/
public final class PriceItem implements Price {
/**
* The base MathContext.
*/
private final MathContext baseContext;
/**
* The amount of items.
*/
private final Function<MathContext, Double> function;
/**
* The item.
*/
private final TestableItem item;
/**
* The multipliers.
*/
private final Map<UUID, Double> multipliers = new HashMap<>();
/**
* Create a new item-based price.
*
* @param amount The amount.
* @param item The item.
*/
public PriceItem(final int amount,
@NotNull final TestableItem item) {
this(MathContext.EMPTY, 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.
*/
public PriceItem(@NotNull final MathContext baseContext,
@NotNull final Function<MathContext, Double> function,
@NotNull final TestableItem item) {
this.baseContext = baseContext;
this.function = function;
this.item = item;
}
/**
* Get the item.
*
* @return The item.
*/
public TestableItem getItem() {
return item;
}
@Override
public boolean canAfford(@NotNull final Player player,
final double multiplier) {
int toRemove = (int) getValue(player, multiplier);
if (toRemove <= 0) {
return true;
}
int count = 0;
for (ItemStack itemStack : player.getInventory().getContents()) {
if (item.matches(itemStack)) {
count += itemStack.getAmount();
}
}
return count >= toRemove;
}
@Override
public void pay(@NotNull final Player player,
final double multiplier) {
int toRemove = (int) getValue(player, multiplier);
int count = 0;
for (ItemStack itemStack : player.getInventory().getContents()) {
if (count >= toRemove) {
break;
}
if (item.matches(itemStack)) {
int itemAmount = itemStack.getAmount();
if (itemAmount > toRemove) {
itemStack.setAmount(itemAmount - toRemove);
}
if (itemAmount <= toRemove) {
itemStack.setAmount(0);
itemStack.setType(Material.AIR);
}
count += itemAmount;
}
}
}
@Override
public void giveTo(@NotNull final Player player,
final double multiplier) {
ItemStack itemStack = item.getItem().clone();
itemStack.setAmount((int) getValue(player, multiplier));
new DropQueue(player)
.addItem(itemStack)
.forceTelekinesis()
.push();
}
@Override
public double getValue(@NotNull final Player player,
final double multiplier) {
return Math.toIntExact(Math.round(
this.function.apply(MathContext.copyWithPlayer(baseContext, player))
* getMultiplier(player) * multiplier
));
}
@Override
public double getMultiplier(@NotNull final Player player) {
return this.multipliers.getOrDefault(player.getUniqueId(), 1.0);
}
@Override
public void setMultiplier(@NotNull final Player player,
final double multiplier) {
this.multipliers.put(player.getUniqueId(), multiplier);
}
@Override
public String getIdentifier() {
return "eco:item-" + HashedItem.of(this.item.getItem()).getHash();
}
}

View File

@@ -21,7 +21,8 @@ public final class ProxyConstants {
"v1_17_R1",
"v1_18_R1",
"v1_18_R2",
"v1_19_R1"
"v1_19_R1",
"v1_19_R2"
);
private ProxyConstants() {

View File

@@ -0,0 +1,101 @@
package com.willfp.eco.core.sound;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.serialization.ConfigDeserializer;
import org.bukkit.Location;
import org.bukkit.Sound;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
/**
* A sound that can be played at a location.
*
* @param sound The sound.
* @param pitch The pitch.
* @param volume The volume.
*/
public record PlayableSound(@NotNull Sound sound,
double pitch,
double volume) {
/**
* The deserializer.
*/
private static final ConfigDeserializer<PlayableSound> DESERIALIZER = new Deserializer();
/**
* Create a sound with a default volume.
*
* @param sound The sound.
* @param pitch The pitch.
*/
public PlayableSound(@NotNull final Sound sound,
final double pitch) {
this(sound, pitch, 1.0);
}
/**
* Play the sound to a player.
*
* @param player The player.
*/
public void playTo(@NotNull final Player player) {
player.playSound(player.getLocation(), sound, (float) volume, (float) pitch);
}
/**
* Play the sound at a location.
*
* @param location The location.
*/
public void playAt(@NotNull final Location location) {
World world = location.getWorld();
if (world == null) {
return;
}
world.playSound(location, sound, (float) volume, (float) pitch);
}
/**
* Parse a playable sound from config.
*
* @param config The config.
* @return The sound, or null if it's invalid.
*/
@Nullable
public static PlayableSound create(@NotNull final Config config) {
return DESERIALIZER.deserialize(config);
}
/**
* The deserializer for {@link PlayableSound}.
*/
private static final class Deserializer implements ConfigDeserializer<PlayableSound> {
@Override
public @Nullable PlayableSound deserialize(@NotNull final Config config) {
if (!config.has("sound")) {
return null;
}
try {
Sound sound = Sound.valueOf(config.getString("sound").toUpperCase());
double pitch = Objects.requireNonNullElse(config.getDouble("pitch"), 1.0);
double volume = Objects.requireNonNullElse(config.getDouble("volume"), 1.0);
return new PlayableSound(
sound,
pitch,
volume
);
} catch (IllegalArgumentException e) {
return null;
}
}
}
}

View File

@@ -1,8 +1,10 @@
package com.willfp.eco.util;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager;
import com.willfp.eco.core.placeholder.AdditionalPlayer;
import com.willfp.eco.core.placeholder.InjectablePlaceholder;
import com.willfp.eco.core.math.MathContext;
import com.willfp.eco.core.placeholder.PlaceholderInjectable;
import com.willfp.eco.core.placeholder.StaticPlaceholder;
import org.bukkit.entity.Player;
@@ -12,7 +14,6 @@ import org.jetbrains.annotations.Nullable;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
@@ -240,7 +241,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, null);
return evaluateExpression(expression, MathContext.EMPTY);
}
/**
@@ -252,18 +253,7 @@ public final class NumberUtils {
*/
public static double evaluateExpression(@NotNull final String expression,
@Nullable final Player player) {
return evaluateExpression(expression, player, new PlaceholderInjectable() {
@Override
public void clearInjectedPlaceholders() {
// Nothing.
}
@Override
public @NotNull
List<InjectablePlaceholder> getPlaceholderInjections() {
return Collections.emptyList();
}
});
return evaluateExpression(expression, player, PlaceholderManager.EMPTY_INJECTABLE);
}
/**
@@ -323,7 +313,23 @@ public final class NumberUtils {
@Nullable final Player player,
@NotNull final PlaceholderInjectable context,
@NotNull final Collection<AdditionalPlayer> additionalPlayers) {
return Eco.get().evaluate(expression, player, context, additionalPlayers);
return Eco.get().evaluate(expression, new MathContext(
context,
player,
additionalPlayers
));
}
/**
* Evaluate an expression with respect to a player (for placeholders).
*
* @param expression The expression.
* @param context The math context.
* @return The value of the expression, or zero if invalid.
*/
public static double evaluateExpression(@NotNull final String expression,
@NotNull final MathContext context) {
return Eco.get().evaluate(expression, context);
}
private NumberUtils() {

View File

@@ -3,9 +3,12 @@
package com.willfp.eco.core.config
import com.willfp.eco.core.config.interfaces.Config
import org.bukkit.configuration.ConfigurationSection
import java.io.File
import java.io.InputStream
/** Helper class to create configs with a kotlin DSL. */
class DSLConfig internal constructor(type: ConfigType) : TransientConfig(emptyMap(), type) {
class DSLConfig internal constructor(type: ConfigType) : GenericConfig(type) {
/**
* Map a key to a value.
*
@@ -34,3 +37,30 @@ class DSLConfig internal constructor(type: ConfigType) : TransientConfig(emptyMa
*/
fun config(type: ConfigType = ConfigType.YAML, builder: DSLConfig.() -> Unit): Config =
DSLConfig(type).apply(builder)
/** @see Configs.empty */
fun emptyConfig() = Configs.empty()
/** @see Configs.empty */
fun emptyConfig(type: ConfigType) = Configs.empty(type)
/** @see Configs.fromBukkit */
fun ConfigurationSection?.toConfig() = Configs.fromBukkit(this)
/** @see Configs.fromStream */
fun InputStream?.readConfig() = Configs.fromStream(this)
/** @see Configs.fromFile */
fun File?.readConfig() = Configs.fromFile(this)
/** @see Configs.fromFile */
fun File?.readConfig(type: ConfigType) = Configs.fromFile(this, type)
/** @see Configs.fromMap */
fun Map<String?, Any?>.toConfig() = Configs.fromMap(this)
/** @see Configs.fromMap */
fun Map<String?, Any?>.toConfig(type: ConfigType) = Configs.fromMap(this, type)
/** @see Configs.fromString */
fun readConfig(contents: String, type: ConfigType) = Configs.fromString(contents, type)

View File

@@ -4,9 +4,9 @@ package com.willfp.eco.core.gui
import com.willfp.eco.core.gui.menu.Menu
import com.willfp.eco.core.gui.menu.MenuBuilder
import com.willfp.eco.core.gui.menu.MenuType
import com.willfp.eco.core.gui.menu.MenuEvent
import com.willfp.eco.core.gui.menu.MenuEventHandler
import com.willfp.eco.core.gui.menu.MenuType
import com.willfp.eco.core.gui.page.Page
import com.willfp.eco.core.gui.page.PageBuilder
import com.willfp.eco.core.gui.slot.Slot
@@ -74,6 +74,10 @@ fun SlotBuilder.onClick(clickType: ClickType, action: (Player, InventoryClickEve
fun SlotBuilder.notCaptiveFor(test: (Player) -> Boolean): SlotBuilder =
this.notCaptiveFor { test(it) }
/** @see SlotBuilder.setCaptiveFilter */
fun SlotBuilder.setCaptiveFilter(test: (Player, Menu, ItemStack?) -> Boolean): SlotBuilder =
this.setCaptiveFilter { a, b, c -> test(a, b, c) }
/**
* @see SlotBuilder.setModifier
* @deprecated Use SlotUpdater instead.
@@ -184,6 +188,10 @@ inline fun <reified T : MenuEvent> MenuBuilder.onEvent(crossinline handler: (Pla
})
}
/** @see MenuBuilder.onBuild */
fun MenuBuilder.onBuild(action: (Menu) -> Unit): MenuBuilder =
this.onBuild { action(it) }
/** Kotlin builder for menus. */
fun menu(
rows: Int,

View File

@@ -2,13 +2,31 @@
package com.willfp.eco.core.integrations.shop
import com.willfp.eco.core.price.Price
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
/** @see ShopManager.getItemPrice **/
/** @see ShopManager.getItemPrice * */
@Deprecated(
"Prices depend on players, this will always return 0.",
level = DeprecationLevel.ERROR,
replaceWith = ReplaceWith("this.getValue(player)")
)
val ItemStack.price: Double
get() = ShopManager.getItemPrice(this)
get() = 0.0
/** @see ShopManager.getItemPrice **/
/** @see ShopManager.getItemPrice * */
@Deprecated(
"Use the price system instead, prices may not be currencies.",
ReplaceWith("this.getValue(player)"),
)
fun ItemStack.getPrice(player: Player): Double =
ShopManager.getItemPrice(this, player)
this.getUnitValue(player).getValue(player, this.amount.toDouble())
/** @see ShopManager.getUnitValue */
fun ItemStack.getUnitValue(player: Player): Price =
ShopManager.getUnitValue(this, player)
/** @see ShopManager.isSellable */
fun ItemStack?.isSellable(player: Player): Boolean =
ShopManager.isSellable(this, player)

View File

@@ -0,0 +1,14 @@
@file:JvmName("ItemBuilderExtensions")
package com.willfp.eco.core.items.builder
import com.willfp.eco.core.items.TestableItem
import org.bukkit.inventory.ItemStack
/** Modify an item with a builder. */
fun TestableItem.modify(builder: ItemBuilder.() -> Unit): ItemStack =
this.item.modify(builder)
/** Modify an item with a builder. */
fun ItemStack.modify(builder: ItemBuilder.() -> Unit): ItemStack =
ItemStackBuilder(this).apply(builder).build()

View File

@@ -0,0 +1,7 @@
@file:JvmName("TestableExtensions")
package com.willfp.eco.core.lookup
/** @see Testable.matches */
fun <T> T?.matches(test: Testable<T>) =
test.matches(this)

View File

@@ -2,8 +2,8 @@ package com.willfp.eco.internal
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.PluginProps
import com.willfp.eco.core.config.TransientConfig
import com.willfp.eco.core.config.interfaces.Config
import com.willfp.eco.core.config.readConfig
object EcoPropsParser : PluginProps.PropsParser<Config> {
override fun parseFrom(config: Config): PluginProps {
@@ -28,7 +28,7 @@ object EcoPropsParser : PluginProps.PropsParser<Config> {
}
return PluginProps.parse(
TransientConfig(plugin.getResourceAsStream("/eco.yml")),
plugin.getResourceAsStream("/eco.yml").readConfig(),
Config::class.java
)
}

View File

@@ -108,7 +108,7 @@ open class EcoConfig(
}
override fun getSubsectionsOrNull(path: String): List<Config>? {
return (get(path) as? Iterable<Config>)
return getList<Config>(path)
?.map { it.apply { this.addInjectablePlaceholder(injections) } }
?.toList()
}
@@ -122,7 +122,7 @@ open class EcoConfig(
}
override fun getIntsOrNull(path: String): List<Int>? {
return (get(path) as? Iterable<Number>)?.map { it.toInt() }
return getList<Number>(path)?.map { it.toInt() }
}
override fun getBoolOrNull(path: String): Boolean? {
@@ -130,7 +130,7 @@ open class EcoConfig(
}
override fun getBoolsOrNull(path: String): List<Boolean>? {
return (get(path) as? Iterable<Boolean>)?.toList()
return getList<Boolean>(path)?.toList()
}
override fun getStringOrNull(
@@ -154,7 +154,7 @@ open class EcoConfig(
format: Boolean,
option: StringUtils.FormatOption
): List<String>? {
val strings = (get(path) as? Iterable<*>)
val strings = getList<Any?>(path)
?.map { it?.toString() ?: "" }
?.toMutableList() ?: return null
if (placeholderInjections.isNotEmpty() && format && option == StringUtils.FormatOption.WITH_PLACEHOLDERS) {
@@ -176,7 +176,7 @@ open class EcoConfig(
}
override fun getDoublesOrNull(path: String): List<Double>? {
return (get(path) as? Iterable<Number>)?.map { it.toDouble() }
return getList<Number>(path)?.map { it.toDouble() }
}
override fun addInjectablePlaceholder(placeholders: Iterable<InjectablePlaceholder>) {
@@ -215,4 +215,15 @@ open class EcoConfig(
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()
if (asList.firstOrNull() !is T?) {
return emptyList()
}
return asList as List<T>
}
}

View File

@@ -3,7 +3,7 @@ package com.willfp.eco.internal.extensions
import com.google.common.collect.ImmutableSet
import com.willfp.eco.core.Eco
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.config.TransientConfig
import com.willfp.eco.core.config.toConfig
import com.willfp.eco.core.extensions.Extension
import com.willfp.eco.core.extensions.ExtensionLoader
import com.willfp.eco.core.extensions.ExtensionMetadata
@@ -48,7 +48,7 @@ class EcoExtensionLoader(
val ymlIn = classLoader.getResourceAsStream("extension.yml")
?: throw MalformedExtensionException("No extension.yml found in " + extensionJar.name)
val extensionYml = TransientConfig(YamlConfiguration.loadConfiguration(InputStreamReader(ymlIn)))
val extensionYml = YamlConfiguration.loadConfiguration(InputStreamReader(ymlIn)).toConfig()
val mainClass = extensionYml.getStringOrNull("main")
var name = extensionYml.getStringOrNull("name")

View File

@@ -11,8 +11,8 @@ class MergedStateMenu(
return base.getState(player) + additional.getState(player)
}
override fun addState(player: Player, key: String, value: Any?) {
base.addState(player, key, value)
override fun setState(player: Player, key: String, value: Any?) {
base.setState(player, key, value)
}
override fun clearState(player: Player) {

View File

@@ -20,7 +20,7 @@ import org.bukkit.inventory.ItemStack
class EcoMenu(
private val rows: Int,
private val columns: Int,
private val componentsAtPoints: Map<GUIPosition, List<OffsetComponent>>,
private val components: LayeredComponents,
private val title: String,
private val onClose: List<CloseHandler>,
private val onRender: List<(Player, Menu) -> Unit>,
@@ -28,38 +28,19 @@ class EcoMenu(
private val menuEventHandlers: List<MenuEventHandler<*>>,
private val allowsChangingHeldItem: Boolean
) : Menu {
private fun getPossiblyReactiveSlot(row: Int, column: Int, player: Player?, menu: Menu?): Slot {
private fun getPossiblyReactiveSlot(row: Int, column: Int, player: Player?): Slot {
if (row < 1 || row > this.rows || column < 1 || column > this.columns) {
return emptyFillerSlot
}
val guiPosition = GUIPosition(row, column)
val components = componentsAtPoints[guiPosition] ?: return emptyFillerSlot
for (component in components) {
val found = if (player != null && menu != null) component.component.getSlotAt(
component.rowOffset,
component.columnOffset,
player,
menu
) else component.component.getSlotAt(
component.rowOffset,
component.columnOffset
)
if (found != null) {
return found
}
}
return emptyFillerSlot
return components.getSlotAt(row, column, player, this)
}
override fun getSlot(row: Int, column: Int): Slot =
getPossiblyReactiveSlot(row, column, null, null)
getPossiblyReactiveSlot(row, column, null)
override fun getSlot(row: Int, column: Int, player: Player, menu: Menu): Slot =
getPossiblyReactiveSlot(row, column, player, menu)
override fun getSlot(row: Int, column: Int, player: Player): Slot =
getPossiblyReactiveSlot(row, column, player)
override fun open(player: Player): Inventory {
val inventory = if (columns == 9) {
@@ -129,7 +110,7 @@ class EcoMenu(
this.handle(player, this@EcoMenu, event as T)
}
override fun addState(player: Player, key: String, value: Any?) {
override fun setState(player: Player, key: String, value: Any?) {
val inventory = player.renderedInventory ?: return
inventory.state[key] = value
}

View File

@@ -22,6 +22,7 @@ class EcoMenuBuilder(
private val onOpen = mutableListOf<OpenHandler>()
private val onRender = mutableListOf<(Player, Menu) -> Unit>()
private val menuEventHandlers = mutableListOf<MenuEventHandler<*>>()
private val onBuild = mutableListOf<(Menu) -> Unit>()
private var allowsChangingHeldItem = false
override fun getRows() = rows
@@ -80,13 +81,18 @@ class EcoMenuBuilder(
return this
}
override fun onBuild(action: Consumer<Menu>): MenuBuilder {
onBuild += { action.accept(it) }
return this
}
override fun allowChangingHeldItem(): MenuBuilder {
allowsChangingHeldItem = true
return this
}
override fun build(): Menu {
val layeredComponents = mutableMapOf<MenuLayer, MutableMap<GUIPosition, MutableList<OffsetComponent>>>()
val layeredComponents = LayeredComponents()
// 5 nested for loops? Shut up. Silence. Quiet.
for (layer in MenuLayer.values()) {
@@ -107,11 +113,14 @@ class EcoMenuBuilder(
val point = GUIPosition(row, column)
layeredComponents.computeIfAbsent(layer) { mutableMapOf() }
.computeIfAbsent(point) { mutableListOf() } += OffsetComponent(
component,
rowOffset,
columnOffset
layeredComponents.addOffsetComponent(
layer,
point,
OffsetComponent(
component,
rowOffset,
columnOffset
)
)
}
}
@@ -119,18 +128,10 @@ class EcoMenuBuilder(
}
}
val componentsAtPoints = mutableMapOf<GUIPosition, MutableList<OffsetComponent>>()
for (menuLayer in MenuLayer.values()) {
for ((anchor, offsetComponents) in layeredComponents[menuLayer] ?: emptyMap()) {
componentsAtPoints[anchor] = offsetComponents
}
}
return EcoMenu(
val menu = EcoMenu(
rows,
columns,
componentsAtPoints,
layeredComponents,
title,
onClose,
onRender,
@@ -138,5 +139,9 @@ class EcoMenuBuilder(
menuEventHandlers,
allowsChangingHeldItem
)
onBuild.forEach { it(menu) } // Run on build functions.
return menu
}
}

View File

@@ -0,0 +1,46 @@
package com.willfp.eco.internal.gui.menu
import com.willfp.eco.core.gui.menu.Menu
import com.willfp.eco.core.gui.menu.MenuLayer
import com.willfp.eco.core.gui.slot.Slot
import org.bukkit.entity.Player
class LayeredComponents {
private val layers = mutableMapOf<MenuLayer, Map<GUIPosition, List<OffsetComponent>>>()
fun getSlotAt(row: Int, column: Int, player: Player?, menu: Menu): Slot {
val guiPosition = GUIPosition(row, column)
for (layer in MenuLayer.values().reversed()) {
val componentsAtPoints = layers[layer] ?: continue
val components = componentsAtPoints[guiPosition] ?: continue
for (component in components) {
val found = if (player != null) component.component.getSlotAt(
component.rowOffset,
component.columnOffset,
player,
menu
) else component.component.getSlotAt(
component.rowOffset,
component.columnOffset
)
if (found != null) {
return found
}
}
}
return emptyFillerSlot
}
fun addOffsetComponent(layer: MenuLayer, position: GUIPosition, component: OffsetComponent) {
val inLayer = layers[layer]?.toMutableMap() ?: mutableMapOf()
val atPosition = inLayer[position]?.toMutableList() ?: mutableListOf()
atPosition.add(component)
inLayer[position] = atPosition
layers[layer] = inLayer
}
}

View File

@@ -1,8 +1,10 @@
package com.willfp.eco.internal.gui.menu
import com.willfp.eco.core.gui.menu.events.CaptiveItemChangeEvent
import com.willfp.eco.core.items.isEmpty
import com.willfp.eco.core.recipe.parts.EmptyTestableItem
import com.willfp.eco.util.MenuUtils
import com.willfp.eco.util.openMenu
import org.bukkit.entity.Player
import org.bukkit.inventory.Inventory
import org.bukkit.inventory.ItemStack
@@ -31,14 +33,22 @@ class RenderedInventory(
val state = mutableMapOf<String, Any?>()
fun render() {
captiveItems.clear()
// This can happen when opening menus from other menus,
// fixing a bug where multiple paginated menus on top of
// each other caused bugs with page changer display.
if (this.menu != player.openMenu) {
MenuHandler.unregisterInventory(this.inventory)
return
}
val newCaptive = mutableMapOf<GUIPosition, ItemStack>()
for (row in (1..menu.rows)) {
for (column in (1..menu.columns)) {
val position = GUIPosition(row, column)
val bukkit = MenuUtils.rowColumnToSlot(row, column, menu.columns)
val slot = menu.getSlot(row, column, player, menu)
val slot = menu.getSlot(row, column, player)
val renderedItem = slot.getItemStack(player)
if (slot.isCaptive(player, menu)) {
@@ -46,11 +56,11 @@ class RenderedInventory(
if (slot.isCaptiveFromEmpty) {
if (!actualItem.isEmpty) {
captiveItems[position] = actualItem
newCaptive[position] = actualItem
}
} else {
if (actualItem != renderedItem && !EmptyTestableItem().matches(actualItem)) {
captiveItems[position] = actualItem
newCaptive[position] = actualItem
}
}
} else {
@@ -59,15 +69,51 @@ class RenderedInventory(
}
}
val previousCaptive = captiveItems.toMap()
captiveItems.clear()
captiveItems.putAll(newCaptive)
// Call captive item change event
for (position in previousCaptive.keys union newCaptive.keys) {
if (previousCaptive[position] != newCaptive[position]) {
menu.callEvent(
player, CaptiveItemChangeEvent(
position.row,
position.column,
previousCaptive[position],
newCaptive[position]
)
)
}
}
menu.runOnRender(player)
// Run second render if captive items changed
if (captiveItems != previousCaptive) {
for (row in (1..menu.rows)) {
for (column in (1..menu.columns)) {
val bukkit = MenuUtils.rowColumnToSlot(row, column, menu.columns)
val slot = menu.getSlot(row, column, player)
val renderedItem = slot.getItemStack(player)
if (!slot.isCaptive(player, menu)) {
inventory.setItem(bukkit, renderedItem)
}
}
}
}
}
fun renderDefaultCaptiveItems() {
menu.runOnRender(player)
for (row in (1..menu.rows)) {
for (column in (1..menu.columns)) {
val bukkit = MenuUtils.rowColumnToSlot(row, column, menu.columns)
val slot = menu.getSlot(row, column, player, menu)
val slot = menu.getSlot(row, column, player)
if (slot.isCaptive(player, menu)) {
inventory.setItem(bukkit, slot.getItemStack(player))

View File

@@ -1,20 +1,24 @@
package com.willfp.eco.internal.gui.slot
import com.willfp.eco.core.gui.menu.Menu
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.util.toSingletonList
import org.bukkit.Material
import org.bukkit.entity.Player
import org.bukkit.event.inventory.ClickType
import org.bukkit.inventory.ItemStack
class EcoCaptiveSlot(
provider: SlotProvider,
private val captiveFromEmpty: Boolean,
private val notCaptiveFor: (Player) -> Boolean
private val notCaptiveFor: (Player) -> Boolean,
private val filter: CaptiveFilter
) : EcoSlot(
provider,
ClickType.values().associateWith {
captiveWithTest(notCaptiveFor).toSingletonList()
captiveWithTest(notCaptiveFor, filter).toSingletonList()
},
{ _, _, prev -> prev }
) {
@@ -22,13 +26,25 @@ class EcoCaptiveSlot(
return !notCaptiveFor(player)
}
override fun isAllowedCaptive(player: Player, menu: Menu, itemStack: ItemStack?): Boolean {
return filter.isAllowed(player, menu, itemStack)
}
override fun isCaptiveFromEmpty(): Boolean {
return captiveFromEmpty
}
}
private fun captiveWithTest(test: (Player) -> Boolean): SlotHandler {
return SlotHandler { event, _, _ ->
event.isCancelled = test(event.whoClicked as Player)
}
private fun captiveWithTest(
notCaptiveFor: (Player) -> Boolean,
filter: CaptiveFilter
): SlotHandler = SlotHandler { event, _, menu ->
val player = event.whoClicked as Player
val item = event.currentItem.nullIfAir() ?: event.cursor.nullIfAir()
event.isCancelled = !filter.isAllowed(player, menu, item) || notCaptiveFor(player)
}
private fun ItemStack?.nullIfAir(): ItemStack? =
if (this?.type == Material.AIR) null else this

View File

@@ -33,9 +33,5 @@ open class EcoSlot(
return updater.update(player, menu, base) ?: return ItemStack(Material.AIR)
}
override fun isCaptive(player: Player, menu: Menu): Boolean {
return false
}
override fun getActionableSlot(player: Player, menu: Menu): EcoSlot = this
}

View File

@@ -2,6 +2,7 @@ package com.willfp.eco.internal.gui.slot
import com.willfp.eco.core.gui.slot.Slot
import com.willfp.eco.core.gui.slot.SlotBuilder
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
@@ -16,7 +17,9 @@ class EcoSlotBuilder(private val provider: SlotProvider) : SlotBuilder {
private val handlers = mutableMapOf<ClickType, MutableList<SlotHandler>>()
private var notCaptiveFor: (Player) -> Boolean = { false }
private var captiveFilter =
CaptiveFilter { _, _, _ -> true }
private var notCaptiveFor: (Player) -> Boolean = { _ -> false}
override fun onClick(type: ClickType, action: SlotHandler): SlotBuilder {
handlers.computeIfAbsent(type) { mutableListOf() } += action
@@ -24,7 +27,12 @@ class EcoSlotBuilder(private val provider: SlotProvider) : SlotBuilder {
}
override fun notCaptiveFor(predicate: Predicate<Player>): SlotBuilder {
notCaptiveFor = { predicate.test(it) }
notCaptiveFor = { player -> predicate.test(player) }
return this
}
override fun setCaptiveFilter(filter: CaptiveFilter): SlotBuilder {
captiveFilter = filter
return this
}
@@ -44,7 +52,8 @@ class EcoSlotBuilder(private val provider: SlotProvider) : SlotBuilder {
EcoCaptiveSlot(
provider,
captiveFromEmpty,
notCaptiveFor
notCaptiveFor,
captiveFilter
)
} else {
EcoSlot(

View File

@@ -1,13 +1,15 @@
package com.willfp.eco.internal.integrations
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.integrations.placeholder.PlaceholderIntegration
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager
import me.clip.placeholderapi.PlaceholderAPI
import me.clip.placeholderapi.expansion.PlaceholderExpansion
import org.bukkit.entity.Player
class PlaceholderIntegrationPAPI(private val plugin: EcoPlugin) : PlaceholderExpansion(), PlaceholderIntegration {
class PAPIExpansion(private val plugin: EcoPlugin) : PlaceholderExpansion() {
init {
register()
}
override fun persist(): Boolean {
return true
}
@@ -34,29 +36,4 @@ class PlaceholderIntegrationPAPI(private val plugin: EcoPlugin) : PlaceholderExp
): String {
return PlaceholderManager.getResult(player, identifier, plugin)
}
override fun registerIntegration() {
register()
}
override fun getPluginName(): String {
return "PlaceholderAPI"
}
override fun translate(
text: String,
player: Player?
): String {
return PlaceholderAPI.setPlaceholders(player, text)
}
override fun findPlaceholdersIn(text: String): MutableList<String> {
val placeholders = mutableListOf<String>()
val matcher = PlaceholderAPI.getPlaceholderPattern().matcher(text)
while (matcher.find()) {
placeholders.add(matcher.group())
}
return placeholders
}
}
}

View File

@@ -27,6 +27,10 @@ object ArgParserCustomModelData : LookupArgParser {
return Predicate {
val testMeta = it.itemMeta ?: return@Predicate false
if (!testMeta.hasCustomModelData()) {
return@Predicate false
}
testMeta.customModelData == modelData
}
}

View File

@@ -0,0 +1,36 @@
package com.willfp.eco.internal.particle
import com.willfp.eco.core.particle.ParticleFactory
import com.willfp.eco.core.particle.SpawnableParticle
import org.bukkit.Color
import org.bukkit.Location
import org.bukkit.Particle
object ParticleFactoryRGB : ParticleFactory {
override fun getNames() = listOf(
"color",
"rgb",
"hex"
)
override fun create(key: String): SpawnableParticle? {
val hex = key.toIntOrNull(16) ?: return null
val color = try {
Color.fromRGB(hex)
} catch (e: IllegalArgumentException) {
return null
}
return SpawnableParticleRGB(Particle.DustOptions(color, 1.0f))
}
private class SpawnableParticleRGB(
private val options: Particle.DustOptions
) : SpawnableParticle {
override fun spawn(location: Location, amount: Int) {
val world = location.world ?: return
world.spawnParticle(Particle.REDSTONE, location, amount, 0.0, 0.0, 0.0, 0.0, options)
}
}
}

View File

@@ -0,0 +1,18 @@
package com.willfp.eco.internal.price
import com.willfp.eco.core.math.MathContext
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(
"coins",
"$"
)
override fun create(baseContext: MathContext, function: Function<MathContext, Double>): Price {
return PriceEconomy(baseContext, function)
}
}

View File

@@ -0,0 +1,54 @@
package com.willfp.eco.internal.price
import com.willfp.eco.core.math.MathContext
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 {
override fun getNames() = listOf(
"xp",
"exp",
"experience"
)
override fun create(baseContext: MathContext, function: Function<MathContext, Double>): Price {
return PriceXP(baseContext) { function.apply(it).roundToInt() }
}
private class PriceXP(
private val baseContext: MathContext,
private val xp: (MathContext) -> Int
) : Price {
private val multipliers = mutableMapOf<UUID, Double>()
override fun canAfford(player: Player) = player.totalExperience >= getValue(player)
override fun pay(player: Player) {
player.totalExperience -= getValue(player).roundToInt()
}
override fun giveTo(player: Player) {
player.totalExperience += getValue(player).roundToInt()
}
override fun getValue(player: Player): Double {
return xp(MathContext.copyWithPlayer(baseContext, player)) * getMultiplier(player)
}
override fun getMultiplier(player: Player): Double {
return multipliers[player.uniqueId] ?: 1.0
}
override fun setMultiplier(player: Player, multiplier: Double) {
multipliers[player.uniqueId] = multiplier.roundToInt().toDouble()
}
override fun getIdentifier(): String {
return "eco:xp"
}
}
}

View File

@@ -0,0 +1,55 @@
package com.willfp.eco.internal.price
import com.willfp.eco.core.math.MathContext
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 {
override fun getNames() = listOf(
"l",
"levels",
"xplevels",
"explevels",
)
override fun create(baseContext: MathContext, function: Function<MathContext, Double>): Price {
return PriceXPLevel(baseContext) { function.apply(it).roundToInt() }
}
private class PriceXPLevel(
private val baseContext: MathContext,
private val level: (MathContext) -> Int
) : Price {
private val multipliers = mutableMapOf<UUID, Double>()
override fun canAfford(player: Player) = player.level >= getValue(player)
override fun pay(player: Player) {
player.level -= getValue(player).roundToInt()
}
override fun giveTo(player: Player) {
player.level += getValue(player).roundToInt()
}
override fun getValue(player: Player): Double {
return level(MathContext.copyWithPlayer(baseContext, player)) * getMultiplier(player)
}
override fun getMultiplier(player: Player): Double {
return multipliers[player.uniqueId] ?: 1.0
}
override fun setMultiplier(player: Player, multiplier: Double) {
multipliers[player.uniqueId] = multiplier.roundToInt().toDouble()
}
override fun getIdentifier(): String {
return "eco:xp-levels"
}
}
}

View File

@@ -29,7 +29,27 @@ var SkullMeta.texture: String?
setProfile.isAccessible = true
}
if (base64 == null) {
/* This length check below was lost in the conversion. For some reason the base64
* string is length 8 when this is called pretty frequently, causing an
* out of bounds exception.
*
* Could not pass event EntityPotionEffectEvent to Talismans v5.116.0
* java.lang.StringIndexOutOfBoundsException: begin -12, end 8, length 8
* at java.lang.String.checkBoundsBeginEnd(String.java:4604) ~[?:?]
* at java.lang.String.substring(String.java:2707) ~[?:?]
* at java.lang.String.substring(String.java:2680) ~[?:?]
* at com.willfp.eco.internal.spigot.proxy.v1_19_R1.common.SkullKt.setTexture(Skull.kt:36)
*
if (base64.length < 20) {
return
}
*
* ^ Update to this comment: a length 8 string ("textures") was being sent
* because the get() method wasn't working right. This has been fixed, but the
* check needs to remain implemented.
*/
if (base64 == null || base64.length < 20) {
setProfile.invoke(this, null)
} else {
val uuid = UUID(

View File

@@ -1,21 +1,23 @@
package com.willfp.eco.internal.spigot.proxy.common.ai.entity
import com.willfp.eco.core.entities.ai.entity.EntityGoalLeapAtTarget
import com.willfp.eco.core.entities.ai.entity.EntityGoalIllusionerBlindnessSpell
import com.willfp.eco.internal.spigot.proxy.common.ai.EntityGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.opengoals.IllusionerBlindnessSpellGoal
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.goal.Goal
import net.minecraft.world.entity.monster.Illusioner
object IllusionerBlindnessSpellGoalFactory : EntityGoalFactory<EntityGoalLeapAtTarget> {
override fun create(apiGoal: EntityGoalLeapAtTarget, entity: PathfinderMob): Goal? {
object IllusionerBlindnessSpellGoalFactory : EntityGoalFactory<EntityGoalIllusionerBlindnessSpell> {
override fun create(apiGoal: EntityGoalIllusionerBlindnessSpell, entity: PathfinderMob): Goal? {
if (entity !is Illusioner) return null
return IllusionerBlindnessSpellGoal(
entity
)
// Have to use reflection for it to work
return Illusioner::class.java.declaredClasses[1]
.getDeclaredConstructor(Illusioner::class.java)
.apply { isAccessible = true }
.newInstance(entity) as Goal
}
override fun isGoalOfType(goal: Goal) = goal is IllusionerBlindnessSpellGoal
|| goal::class.java.name.contains("IllusionerBlindnessSpellGoal")
override fun isGoalOfType(goal: Goal): Boolean {
return Illusioner::class.java.declaredClasses[1].isInstance(goal)
}
}

View File

@@ -1,21 +1,23 @@
package com.willfp.eco.internal.spigot.proxy.common.ai.entity
import com.willfp.eco.core.entities.ai.entity.EntityGoalLeapAtTarget
import com.willfp.eco.core.entities.ai.entity.EntityGoalIllusionerMirrorSpell
import com.willfp.eco.internal.spigot.proxy.common.ai.EntityGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.opengoals.IllusionerMirrorSpellGoal
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.goal.Goal
import net.minecraft.world.entity.monster.Illusioner
object IllusionerMirrorSpellGoalFactory : EntityGoalFactory<EntityGoalLeapAtTarget> {
override fun create(apiGoal: EntityGoalLeapAtTarget, entity: PathfinderMob): Goal? {
object IllusionerMirrorSpellGoalFactory : EntityGoalFactory<EntityGoalIllusionerMirrorSpell> {
override fun create(apiGoal: EntityGoalIllusionerMirrorSpell, entity: PathfinderMob): Goal? {
if (entity !is Illusioner) return null
return IllusionerMirrorSpellGoal(
entity
)
// Have to use reflection for it to work
return Illusioner::class.java.declaredClasses[0]
.getDeclaredConstructor(Illusioner::class.java)
.apply { isAccessible = true }
.newInstance(entity) as Goal
}
override fun isGoalOfType(goal: Goal) = goal is IllusionerMirrorSpellGoal
|| goal::class.java.name.contains("IllusionerMirrorSpellGoal")
override fun isGoalOfType(goal: Goal): Boolean {
return Illusioner::class.java.declaredClasses[0].isInstance(goal)
}
}

View File

@@ -1,50 +0,0 @@
package com.willfp.eco.internal.spigot.proxy.common.ai.opengoals
import net.minecraft.sounds.SoundEvent
import net.minecraft.sounds.SoundEvents
import net.minecraft.world.Difficulty
import net.minecraft.world.effect.MobEffectInstance
import net.minecraft.world.effect.MobEffects
import net.minecraft.world.entity.monster.Illusioner
import net.minecraft.world.entity.monster.SpellcasterIllager
import org.bukkit.event.entity.EntityPotionEffectEvent
class IllusionerBlindnessSpellGoal(
private val illusioner: Illusioner
) : OpenUseSpellGoal(illusioner) {
override val castingInterval = 180
override val castingTime = 20
override val spellPrepareSound: SoundEvent = SoundEvents.ILLUSIONER_PREPARE_BLINDNESS
override val spell: SpellcasterIllager.IllagerSpell = SpellcasterIllager.IllagerSpell.BLINDNESS
private var lastTargetId = 0
override fun canUse(): Boolean {
return if (super.canUse()) {
false
} else if (illusioner.target == null) {
false
} else if (illusioner.target!!.id == lastTargetId) {
false
} else {
illusioner.level.getCurrentDifficultyAt(illusioner.blockPosition()).isHarderThan(
Difficulty.NORMAL.ordinal.toFloat()
)
}
}
override fun start() {
super.start()
if (illusioner.target != null) {
lastTargetId = illusioner.target!!.id
}
}
override fun performSpellCasting() {
illusioner.target?.addEffect(
MobEffectInstance(MobEffects.BLINDNESS, 400),
illusioner,
EntityPotionEffectEvent.Cause.ATTACK
) // CraftBukkit
}
}

View File

@@ -1,29 +0,0 @@
package com.willfp.eco.internal.spigot.proxy.common.ai.opengoals
import net.minecraft.sounds.SoundEvent
import net.minecraft.sounds.SoundEvents
import net.minecraft.world.effect.MobEffectInstance
import net.minecraft.world.effect.MobEffects
import net.minecraft.world.entity.monster.Illusioner
import net.minecraft.world.entity.monster.SpellcasterIllager
import org.bukkit.event.entity.EntityPotionEffectEvent
class IllusionerMirrorSpellGoal(
private val illusioner: Illusioner
) : OpenUseSpellGoal(illusioner) {
override val castingInterval = 340
override val castingTime = 20
override val spellPrepareSound: SoundEvent = SoundEvents.ILLUSIONER_PREPARE_MIRROR
override val spell: SpellcasterIllager.IllagerSpell = SpellcasterIllager.IllagerSpell.DISAPPEAR
override fun canUse(): Boolean {
return if (!super.canUse()) false else !illusioner.hasEffect(MobEffects.INVISIBILITY)
}
override fun performSpellCasting() {
illusioner.addEffect(
MobEffectInstance(MobEffects.INVISIBILITY, 1200),
EntityPotionEffectEvent.Cause.ILLUSION
)
}
}

View File

@@ -1,79 +0,0 @@
package com.willfp.eco.internal.spigot.proxy.common.ai.opengoals
import net.minecraft.sounds.SoundEvent
import net.minecraft.world.entity.EntityType
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.goal.Goal
import net.minecraft.world.entity.monster.SpellcasterIllager
@Suppress("UNCHECKED_CAST")
class DelegatedSpellcaster(private val handle: SpellcasterIllager) : SpellcasterIllager(
handle.type as EntityType<out SpellcasterIllager>,
handle.level
) {
var openSpellCastingTickCount
get() = this.spellCastingTickCount
set(value) {
this.spellCastingTickCount = value
}
val openCastingSoundEvent = this.castingSoundEvent
override fun applyRaidBuffs(wave: Int, unused: Boolean) {
handle.applyRaidBuffs(wave, unused)
}
override fun getCelebrateSound(): SoundEvent {
return handle.celebrateSound
}
override fun getCastingSoundEvent(): SoundEvent {
return this.openCastingSoundEvent
}
}
@Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS")
abstract class OpenUseSpellGoal(
private val handle: SpellcasterIllager
) : Goal() {
private var attackWarmupDelay = 0
private var nextAttackTickCount = 0
private val openHandle = DelegatedSpellcaster(handle)
override fun canUse(): Boolean {
val entityliving: LivingEntity = handle.target ?: return false
return if (entityliving.isAlive) if (handle.isCastingSpell) false else handle.tickCount >= nextAttackTickCount else false
}
override fun canContinueToUse(): Boolean {
val entityliving: LivingEntity = handle.target ?: return false
return entityliving.isAlive && attackWarmupDelay > 0
}
override fun start() {
attackWarmupDelay = castWarmupTime
openHandle.openSpellCastingTickCount = castingTime
nextAttackTickCount = handle.tickCount + castingInterval
val soundeffect = spellPrepareSound
if (soundeffect != null) {
handle.playSound(soundeffect, 1.0f, 1.0f)
}
handle.setIsCastingSpell(spell)
}
override fun tick() {
--attackWarmupDelay
if (attackWarmupDelay == 0) {
performSpellCasting()
handle.playSound(openHandle.openCastingSoundEvent, 1.0f, 1.0f)
}
}
protected abstract fun performSpellCasting()
protected abstract val castingTime: Int
protected abstract val castingInterval: Int
protected abstract val spellPrepareSound: SoundEvent?
protected abstract val spell: SpellcasterIllager.IllagerSpell?
protected open val castWarmupTime: Int = 60
}

View File

@@ -0,0 +1,35 @@
package com.willfp.eco.internal.spigot.proxy.v1_17_R1
import com.willfp.eco.core.command.impl.PluginCommand
import com.willfp.eco.internal.spigot.proxy.BukkitCommandsProxy
import org.bukkit.Bukkit
import org.bukkit.command.Command
import org.bukkit.command.SimpleCommandMap
import org.bukkit.craftbukkit.v1_17_R1.CraftServer
import java.lang.reflect.Field
class BukkitCommands : BukkitCommandsProxy {
private val knownCommandsField: Field by lazy {
SimpleCommandMap::class.java.getDeclaredField("knownCommands")
.apply {
isAccessible = true
}
}
@Suppress("UNCHECKED_CAST")
private val knownCommands: MutableMap<String, Command>
get() = knownCommandsField.get(getCommandMap()) as MutableMap<String, Command>
override fun getCommandMap(): SimpleCommandMap {
return (Bukkit.getServer() as CraftServer).commandMap
}
override fun syncCommands() {
(Bukkit.getServer() as CraftServer).syncCommands()
}
override fun unregisterCommand(command: PluginCommand) {
knownCommands.remove(command.name)
knownCommands.remove("${command.plugin.name.lowercase()}:${command.name}")
}
}

View File

@@ -3,16 +3,28 @@ package com.willfp.eco.internal.spigot.proxy.v1_17_R1
import com.willfp.eco.core.data.ExtendedPersistentDataContainer
import com.willfp.eco.internal.spigot.proxy.ExtendedPersistentDataContainerFactoryProxy
import net.minecraft.nbt.Tag
import org.bukkit.Material
import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack
import org.bukkit.craftbukkit.v1_17_R1.persistence.CraftPersistentDataContainer
import org.bukkit.craftbukkit.v1_17_R1.persistence.CraftPersistentDataTypeRegistry
import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataContainer
import org.bukkit.persistence.PersistentDataType
class ExtendedPersistentDataContainerFactory: ExtendedPersistentDataContainerFactoryProxy {
class ExtendedPersistentDataContainerFactory : ExtendedPersistentDataContainerFactoryProxy {
@Suppress("UNCHECKED_CAST")
private val registry: CraftPersistentDataTypeRegistry =
CraftPersistentDataContainer::class.java.getDeclaredField("registry")
.apply { isAccessible = true }.get(null) as CraftPersistentDataTypeRegistry
private val registry: CraftPersistentDataTypeRegistry
init {
/*
Can't grab actual instance since it's in CraftMetaItem (which is package-private)
And getting it would mean more janky reflection
*/
val item = CraftItemStack.asCraftCopy(ItemStack(Material.STONE))
val pdc = item.itemMeta!!.persistentDataContainer
this.registry = CraftPersistentDataContainer::class.java.getDeclaredField("registry")
.apply { isAccessible = true }.get(pdc) as CraftPersistentDataTypeRegistry
}
override fun adapt(pdc: PersistentDataContainer): ExtendedPersistentDataContainer {
return when (pdc) {
@@ -34,7 +46,8 @@ class ExtendedPersistentDataContainerFactory: ExtendedPersistentDataContainerFac
.apply { isAccessible = true }.get(handle) as MutableMap<String, Tag>
override fun <T : Any, Z : Any> set(key: String, dataType: PersistentDataType<T, Z>, value: Z) {
customDataTags[key] = registry.wrap(dataType.primitiveType, dataType.toPrimitive(value, handle.adapterContext))
customDataTags[key] =
registry.wrap(dataType.primitiveType, dataType.toPrimitive(value, handle.adapterContext))
}
override fun <T : Any, Z : Any> has(key: String, dataType: PersistentDataType<T, Z>): Boolean {

View File

@@ -0,0 +1,35 @@
package com.willfp.eco.internal.spigot.proxy.v1_18_R1
import com.willfp.eco.core.command.impl.PluginCommand
import com.willfp.eco.internal.spigot.proxy.BukkitCommandsProxy
import org.bukkit.Bukkit
import org.bukkit.command.Command
import org.bukkit.command.SimpleCommandMap
import org.bukkit.craftbukkit.v1_18_R1.CraftServer
import java.lang.reflect.Field
class BukkitCommands : BukkitCommandsProxy {
private val knownCommandsField: Field by lazy {
SimpleCommandMap::class.java.getDeclaredField("knownCommands")
.apply {
isAccessible = true
}
}
@Suppress("UNCHECKED_CAST")
private val knownCommands: MutableMap<String, Command>
get() = knownCommandsField.get(getCommandMap()) as MutableMap<String, Command>
override fun getCommandMap(): SimpleCommandMap {
return (Bukkit.getServer() as CraftServer).commandMap
}
override fun syncCommands() {
(Bukkit.getServer() as CraftServer).syncCommands()
}
override fun unregisterCommand(command: PluginCommand) {
knownCommands.remove(command.name)
knownCommands.remove("${command.plugin.name.lowercase()}:${command.name}")
}
}

View File

@@ -3,16 +3,28 @@ package com.willfp.eco.internal.spigot.proxy.v1_18_R1
import com.willfp.eco.core.data.ExtendedPersistentDataContainer
import com.willfp.eco.internal.spigot.proxy.ExtendedPersistentDataContainerFactoryProxy
import net.minecraft.nbt.Tag
import org.bukkit.Material
import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftItemStack
import org.bukkit.craftbukkit.v1_18_R1.persistence.CraftPersistentDataContainer
import org.bukkit.craftbukkit.v1_18_R1.persistence.CraftPersistentDataTypeRegistry
import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataContainer
import org.bukkit.persistence.PersistentDataType
class ExtendedPersistentDataContainerFactory : ExtendedPersistentDataContainerFactoryProxy {
@Suppress("UNCHECKED_CAST")
private val registry: CraftPersistentDataTypeRegistry =
CraftPersistentDataContainer::class.java.getDeclaredField("registry")
.apply { isAccessible = true }.get(null) as CraftPersistentDataTypeRegistry
private val registry: CraftPersistentDataTypeRegistry
init {
/*
Can't grab actual instance since it's in CraftMetaItem (which is package-private)
And getting it would mean more janky reflection
*/
val item = CraftItemStack.asCraftCopy(ItemStack(Material.STONE))
val pdc = item.itemMeta!!.persistentDataContainer
this.registry = CraftPersistentDataContainer::class.java.getDeclaredField("registry")
.apply { isAccessible = true }.get(pdc) as CraftPersistentDataTypeRegistry
}
override fun adapt(pdc: PersistentDataContainer): ExtendedPersistentDataContainer {
return when (pdc) {

View File

@@ -0,0 +1,35 @@
package com.willfp.eco.internal.spigot.proxy.v1_18_R2
import com.willfp.eco.core.command.impl.PluginCommand
import com.willfp.eco.internal.spigot.proxy.BukkitCommandsProxy
import org.bukkit.Bukkit
import org.bukkit.command.Command
import org.bukkit.command.SimpleCommandMap
import org.bukkit.craftbukkit.v1_18_R2.CraftServer
import java.lang.reflect.Field
class BukkitCommands : BukkitCommandsProxy {
private val knownCommandsField: Field by lazy {
SimpleCommandMap::class.java.getDeclaredField("knownCommands")
.apply {
isAccessible = true
}
}
@Suppress("UNCHECKED_CAST")
private val knownCommands: MutableMap<String, Command>
get() = knownCommandsField.get(getCommandMap()) as MutableMap<String, Command>
override fun getCommandMap(): SimpleCommandMap {
return (Bukkit.getServer() as CraftServer).commandMap
}
override fun syncCommands() {
(Bukkit.getServer() as CraftServer).syncCommands()
}
override fun unregisterCommand(command: PluginCommand) {
knownCommands.remove(command.name)
knownCommands.remove("${command.plugin.name.lowercase()}:${command.name}")
}
}

View File

@@ -3,16 +3,28 @@ package com.willfp.eco.internal.spigot.proxy.v1_18_R2
import com.willfp.eco.core.data.ExtendedPersistentDataContainer
import com.willfp.eco.internal.spigot.proxy.ExtendedPersistentDataContainerFactoryProxy
import net.minecraft.nbt.Tag
import org.bukkit.Material
import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftItemStack
import org.bukkit.craftbukkit.v1_18_R2.persistence.CraftPersistentDataContainer
import org.bukkit.craftbukkit.v1_18_R2.persistence.CraftPersistentDataTypeRegistry
import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataContainer
import org.bukkit.persistence.PersistentDataType
class ExtendedPersistentDataContainerFactory: ExtendedPersistentDataContainerFactoryProxy {
class ExtendedPersistentDataContainerFactory : ExtendedPersistentDataContainerFactoryProxy {
@Suppress("UNCHECKED_CAST")
private val registry: CraftPersistentDataTypeRegistry =
CraftPersistentDataContainer::class.java.getDeclaredField("registry")
.apply { isAccessible = true }.get(null) as CraftPersistentDataTypeRegistry
private val registry: CraftPersistentDataTypeRegistry
init {
/*
Can't grab actual instance since it's in CraftMetaItem (which is package-private)
And getting it would mean more janky reflection
*/
val item = CraftItemStack.asCraftCopy(ItemStack(Material.STONE))
val pdc = item.itemMeta!!.persistentDataContainer
this.registry = CraftPersistentDataContainer::class.java.getDeclaredField("registry")
.apply { isAccessible = true }.get(pdc) as CraftPersistentDataTypeRegistry
}
override fun adapt(pdc: PersistentDataContainer): ExtendedPersistentDataContainer {
return when (pdc) {
@@ -34,7 +46,8 @@ class ExtendedPersistentDataContainerFactory: ExtendedPersistentDataContainerFac
.apply { isAccessible = true }.get(handle) as MutableMap<String, Tag>
override fun <T : Any, Z : Any> set(key: String, dataType: PersistentDataType<T, Z>, value: Z) {
customDataTags[key] = registry.wrap(dataType.primitiveType, dataType.toPrimitive(value, handle.adapterContext))
customDataTags[key] =
registry.wrap(dataType.primitiveType, dataType.toPrimitive(value, handle.adapterContext))
}
override fun <T : Any, Z : Any> has(key: String, dataType: PersistentDataType<T, Z>): Boolean {

View File

@@ -0,0 +1,35 @@
package com.willfp.eco.internal.spigot.proxy.v1_19_R1
import com.willfp.eco.core.command.impl.PluginCommand
import com.willfp.eco.internal.spigot.proxy.BukkitCommandsProxy
import org.bukkit.Bukkit
import org.bukkit.command.Command
import org.bukkit.command.SimpleCommandMap
import org.bukkit.craftbukkit.v1_19_R1.CraftServer
import java.lang.reflect.Field
class BukkitCommands : BukkitCommandsProxy {
private val knownCommandsField: Field by lazy {
SimpleCommandMap::class.java.getDeclaredField("knownCommands")
.apply {
isAccessible = true
}
}
@Suppress("UNCHECKED_CAST")
private val knownCommands: MutableMap<String, Command>
get() = knownCommandsField.get(getCommandMap()) as MutableMap<String, Command>
override fun getCommandMap(): SimpleCommandMap {
return (Bukkit.getServer() as CraftServer).commandMap
}
override fun syncCommands() {
(Bukkit.getServer() as CraftServer).syncCommands()
}
override fun unregisterCommand(command: PluginCommand) {
knownCommands.remove(command.name)
knownCommands.remove("${command.plugin.name.lowercase()}:${command.name}")
}
}

View File

@@ -3,21 +3,33 @@ package com.willfp.eco.internal.spigot.proxy.v1_19_R1
import com.willfp.eco.core.data.ExtendedPersistentDataContainer
import com.willfp.eco.internal.spigot.proxy.ExtendedPersistentDataContainerFactoryProxy
import net.minecraft.nbt.Tag
import org.bukkit.Material
import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack
import org.bukkit.craftbukkit.v1_19_R1.persistence.CraftPersistentDataContainer
import org.bukkit.craftbukkit.v1_19_R1.persistence.CraftPersistentDataTypeRegistry
import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataContainer
import org.bukkit.persistence.PersistentDataType
class ExtendedPersistentDataContainerFactory: ExtendedPersistentDataContainerFactoryProxy {
class ExtendedPersistentDataContainerFactory : ExtendedPersistentDataContainerFactoryProxy {
@Suppress("UNCHECKED_CAST")
private val registry: CraftPersistentDataTypeRegistry =
CraftPersistentDataContainer::class.java.getDeclaredField("registry")
.apply { isAccessible = true }.get(null) as CraftPersistentDataTypeRegistry
private val registry: CraftPersistentDataTypeRegistry
init {
/*
Can't grab actual instance since it's in CraftMetaItem (which is package-private)
And getting it would mean more janky reflection
*/
val item = CraftItemStack.asCraftCopy(ItemStack(Material.STONE))
val pdc = item.itemMeta!!.persistentDataContainer
this.registry = CraftPersistentDataContainer::class.java.getDeclaredField("registry")
.apply { isAccessible = true }.get(pdc) as CraftPersistentDataTypeRegistry
}
override fun adapt(pdc: PersistentDataContainer): ExtendedPersistentDataContainer {
return when (pdc) {
is CraftPersistentDataContainer -> EcoPersistentDataContainer(pdc)
else -> throw IllegalArgumentException("Custom PDC instance is not supported!")
else -> throw IllegalArgumentException("Custom PDC instance ims not supported!")
}
}
@@ -34,7 +46,8 @@ class ExtendedPersistentDataContainerFactory: ExtendedPersistentDataContainerFac
.apply { isAccessible = true }.get(handle) as MutableMap<String, Tag>
override fun <T : Any, Z : Any> set(key: String, dataType: PersistentDataType<T, Z>, value: Z) {
customDataTags[key] = registry.wrap(dataType.primitiveType, dataType.toPrimitive(value, handle.adapterContext))
customDataTags[key] =
registry.wrap(dataType.primitiveType, dataType.toPrimitive(value, handle.adapterContext))
}
override fun <T : Any, Z : Any> has(key: String, dataType: PersistentDataType<T, Z>): Boolean {

View File

@@ -0,0 +1,39 @@
plugins {
id("io.papermc.paperweight.userdev") version "1.3.6"
}
group = "com.willfp"
version = rootProject.version
dependencies {
implementation(project(":eco-core:core-nms:nms-common"))
paperDevBundle("1.19.3-R0.1-SNAPSHOT")
implementation("net.kyori:adventure-text-minimessage:4.11.0") {
version {
strictly("4.11.0")
}
exclude(group = "net.kyori", module = "adventure-api")
}
}
tasks {
build {
dependsOn(reobfJar)
}
reobfJar {
mustRunAfter(shadowJar)
}
shadowJar {
relocate(
"com.willfp.eco.internal.spigot.proxy.common",
"com.willfp.eco.internal.spigot.proxy.v1_19_R2.common"
)
relocate(
"net.kyori.adventure.text.minimessage",
"com.willfp.eco.internal.spigot.proxy.v1_19_R2.minimessage"
)
}
}

View File

@@ -0,0 +1,15 @@
package com.willfp.eco.internal.spigot.proxy.v1_19_R2
import com.willfp.eco.internal.spigot.proxy.AutoCraftProxy
import net.minecraft.network.protocol.game.ClientboundPlaceGhostRecipePacket
import net.minecraft.resources.ResourceLocation
class AutoCraft : AutoCraftProxy {
override fun modifyPacket(packet: Any) {
val recipePacket = packet as ClientboundPlaceGhostRecipePacket
val fKey = recipePacket.javaClass.getDeclaredField("b")
fKey.isAccessible = true
val key = fKey[recipePacket] as ResourceLocation
fKey[recipePacket] = ResourceLocation(key.namespace, key.path + "_displayed")
}
}

View File

@@ -0,0 +1,35 @@
package com.willfp.eco.internal.spigot.proxy.v1_19_R2
import com.willfp.eco.core.command.impl.PluginCommand
import com.willfp.eco.internal.spigot.proxy.BukkitCommandsProxy
import org.bukkit.Bukkit
import org.bukkit.command.Command
import org.bukkit.command.SimpleCommandMap
import org.bukkit.craftbukkit.v1_19_R2.CraftServer
import java.lang.reflect.Field
class BukkitCommands : BukkitCommandsProxy {
private val knownCommandsField: Field by lazy {
SimpleCommandMap::class.java.getDeclaredField("knownCommands")
.apply {
isAccessible = true
}
}
@Suppress("UNCHECKED_CAST")
private val knownCommands: MutableMap<String, Command>
get() = knownCommandsField.get(getCommandMap()) as MutableMap<String, Command>
override fun getCommandMap(): SimpleCommandMap {
return (Bukkit.getServer() as CraftServer).commandMap
}
override fun syncCommands() {
(Bukkit.getServer() as CraftServer).syncCommands()
}
override fun unregisterCommand(command: PluginCommand) {
knownCommands.remove(command.name)
knownCommands.remove("${command.plugin.name.lowercase()}:${command.name}")
}
}

View File

@@ -0,0 +1,93 @@
package com.willfp.eco.internal.spigot.proxy.v1_19_R2
import com.willfp.eco.core.display.Display
import com.willfp.eco.internal.spigot.proxy.ChatComponentProxy
import net.kyori.adventure.nbt.api.BinaryTagHolder
import net.kyori.adventure.text.BuildableComponent
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.TranslatableComponent
import net.kyori.adventure.text.event.HoverEvent
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer
import net.minecraft.nbt.TagParser
import org.bukkit.Material
import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftItemStack
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
@Suppress("UNCHECKED_CAST")
class ChatComponent : ChatComponentProxy {
private val gsonComponentSerializer = GsonComponentSerializer.gson()
override fun modifyComponent(obj: Any, player: Player): Any {
if (obj !is net.minecraft.network.chat.Component) {
return obj
}
val component = gsonComponentSerializer.deserialize(
net.minecraft.network.chat.Component.Serializer.toJson(
obj
)
).asComponent() as BuildableComponent<*, *>
val newComponent = modifyBaseComponent(component, player)
return net.minecraft.network.chat.Component.Serializer.fromJson(
gsonComponentSerializer.serialize(newComponent.asComponent())
) ?: obj
}
private fun modifyBaseComponent(baseComponent: Component, player: Player): Component {
var component = baseComponent
if (component is TranslatableComponent) {
val args = mutableListOf<Component>()
for (arg in component.args()) {
args.add(modifyBaseComponent(arg, player))
}
component = component.args(args)
}
val children = mutableListOf<Component>()
for (child in component.children()) {
children.add(modifyBaseComponent(child, player))
}
component = component.children(children)
val hoverEvent: HoverEvent<Any> = component.style().hoverEvent() as HoverEvent<Any>? ?: return component
val showItem = hoverEvent.value()
if (showItem !is HoverEvent.ShowItem) {
return component
}
val newShowItem = showItem.nbt(
BinaryTagHolder.binaryTagHolder(
CraftItemStack.asNMSCopy(
Display.display(
CraftItemStack.asBukkitCopy(
CraftItemStack.asNMSCopy(
ItemStack(
Material.matchMaterial(
showItem.item()
.toString()
) ?: return component,
showItem.count()
)
).apply {
this.tag = TagParser.parseTag(
showItem.nbt()?.string() ?: return component
) ?: return component
}
),
player
)
).orCreateTag.toString()
)
)
val newHover = hoverEvent.value(newShowItem)
val style = component.style().hoverEvent(newHover)
return component.style(style)
}
}

View File

@@ -0,0 +1,135 @@
package com.willfp.eco.internal.spigot.proxy.v1_19_R2
import com.willfp.eco.internal.spigot.proxy.CommonsInitializerProxy
import com.willfp.eco.internal.spigot.proxy.common.CommonsProvider
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.Tag
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.PathfinderMob
import org.bukkit.Bukkit
import org.bukkit.NamespacedKey
import org.bukkit.craftbukkit.v1_19_R2.CraftServer
import org.bukkit.craftbukkit.v1_19_R2.entity.CraftEntity
import org.bukkit.craftbukkit.v1_19_R2.entity.CraftMob
import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftItemStack
import org.bukkit.craftbukkit.v1_19_R2.persistence.CraftPersistentDataContainer
import org.bukkit.craftbukkit.v1_19_R2.persistence.CraftPersistentDataTypeRegistry
import org.bukkit.craftbukkit.v1_19_R2.util.CraftMagicNumbers
import org.bukkit.craftbukkit.v1_19_R2.util.CraftNamespacedKey
import org.bukkit.entity.LivingEntity
import org.bukkit.entity.Mob
import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataContainer
import java.lang.reflect.Field
class CommonsInitializer : CommonsInitializerProxy {
override fun init() {
CommonsProvider.setIfNeeded(CommonsProviderImpl)
}
object CommonsProviderImpl : CommonsProvider {
private val cisHandle: Field = CraftItemStack::class.java.getDeclaredField("handle").apply {
isAccessible = true
}
private val pdcRegsitry = Class.forName("org.bukkit.craftbukkit.v1_19_R2.inventory.CraftMetaItem")
.getDeclaredField("DATA_TYPE_REGISTRY")
.apply { isAccessible = true }
.get(null) as CraftPersistentDataTypeRegistry
override val nbtTagString = CraftMagicNumbers.NBT.TAG_STRING
override fun toPathfinderMob(mob: Mob): PathfinderMob? {
val craft = mob as? CraftMob ?: return null
return craft.handle as? PathfinderMob
}
override fun toResourceLocation(namespacedKey: NamespacedKey): ResourceLocation =
CraftNamespacedKey.toMinecraft(namespacedKey)
override fun asNMSStack(itemStack: ItemStack): net.minecraft.world.item.ItemStack {
return if (itemStack !is CraftItemStack) {
CraftItemStack.asNMSCopy(itemStack)
} else {
cisHandle[itemStack] as net.minecraft.world.item.ItemStack? ?: CraftItemStack.asNMSCopy(itemStack)
}
}
override fun asBukkitStack(itemStack: net.minecraft.world.item.ItemStack): ItemStack {
return CraftItemStack.asBukkitCopy(itemStack)
}
override fun mergeIfNeeded(itemStack: ItemStack, nmsStack: net.minecraft.world.item.ItemStack) {
if (itemStack !is CraftItemStack) {
itemStack.itemMeta = CraftItemStack.asCraftMirror(nmsStack).itemMeta
}
}
override fun toBukkitEntity(entity: net.minecraft.world.entity.LivingEntity): LivingEntity? =
CraftEntity.getEntity(Bukkit.getServer() as CraftServer, entity) as? LivingEntity
override fun makePdc(tag: CompoundTag, base: Boolean): PersistentDataContainer {
fun emptyPdc(): CraftPersistentDataContainer = CraftPersistentDataContainer(pdcRegsitry)
fun CompoundTag?.toPdc(): PersistentDataContainer {
val pdc = emptyPdc()
this ?: return pdc
val keys = this.allKeys
for (key in keys) {
pdc.put(key, this[key])
}
return pdc
}
return if (base) {
tag.toPdc()
} else {
if (tag.contains("PublicBukkitValues")) {
tag.getCompound("PublicBukkitValues").toPdc()
} else {
emptyPdc()
}
}
}
override fun setPdc(
tag: CompoundTag,
pdc: PersistentDataContainer?,
item: net.minecraft.world.item.ItemStack?
) {
fun CraftPersistentDataContainer.toTag(): CompoundTag {
val compound = CompoundTag()
val rawPublicMap: Map<String, Tag> = this.raw
for ((key, value) in rawPublicMap) {
compound.put(key, value)
}
return compound
}
val container = when (pdc) {
is CraftPersistentDataContainer? -> pdc
else -> null
}
if (item != null) {
if (container != null && !container.isEmpty) {
for (key in tag.allKeys.toSet()) {
tag.remove(key)
}
tag.merge(container.toTag())
} else {
item.setTag(null)
}
} else {
if (container != null && !container.isEmpty) {
tag.put("PublicBukkitValues", container.toTag())
} else {
tag.remove("PublicBukkitValues")
}
}
}
}
}

View File

@@ -0,0 +1,16 @@
package com.willfp.eco.internal.spigot.proxy.v1_19_R2
import com.willfp.eco.internal.entities.EcoDummyEntity
import com.willfp.eco.internal.spigot.proxy.DummyEntityFactoryProxy
import org.bukkit.Location
import org.bukkit.craftbukkit.v1_19_R2.CraftWorld
import org.bukkit.entity.Entity
import org.bukkit.entity.EntityType
class DummyEntityFactory : DummyEntityFactoryProxy {
override fun createDummyEntity(location: Location): Entity {
val world = location.world as CraftWorld
@Suppress("UsePropertyAccessSyntax")
return EcoDummyEntity(world.createEntity(location, EntityType.ZOMBIE.entityClass).getBukkitEntity())
}
}

View File

@@ -0,0 +1,12 @@
package com.willfp.eco.internal.spigot.proxy.v1_19_R2
import com.willfp.eco.core.entities.ai.EntityController
import com.willfp.eco.internal.spigot.proxy.EntityControllerFactoryProxy
import com.willfp.eco.internal.spigot.proxy.common.ai.EcoEntityController
import org.bukkit.entity.Mob
class EntityControllerFactory : EntityControllerFactoryProxy {
override fun <T : Mob> createEntityController(entity: T): EntityController<T> {
return EcoEntityController(entity)
}
}

View File

@@ -0,0 +1,83 @@
package com.willfp.eco.internal.spigot.proxy.v1_19_R2
import com.willfp.eco.core.data.ExtendedPersistentDataContainer
import com.willfp.eco.internal.spigot.proxy.ExtendedPersistentDataContainerFactoryProxy
import net.minecraft.nbt.Tag
import org.bukkit.Material
import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftItemStack
import org.bukkit.craftbukkit.v1_19_R2.persistence.CraftPersistentDataContainer
import org.bukkit.craftbukkit.v1_19_R2.persistence.CraftPersistentDataTypeRegistry
import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataContainer
import org.bukkit.persistence.PersistentDataType
class ExtendedPersistentDataContainerFactory : ExtendedPersistentDataContainerFactoryProxy {
@Suppress("UNCHECKED_CAST")
private val registry: CraftPersistentDataTypeRegistry
init {
/*
Can't grab actual instance since it's in CraftMetaItem (which is package-private)
And getting it would mean more janky reflection
*/
val item = CraftItemStack.asCraftCopy(ItemStack(Material.STONE))
val pdc = item.itemMeta!!.persistentDataContainer
this.registry = CraftPersistentDataContainer::class.java.getDeclaredField("registry")
.apply { isAccessible = true }.get(pdc) as CraftPersistentDataTypeRegistry
}
override fun adapt(pdc: PersistentDataContainer): ExtendedPersistentDataContainer {
return when (pdc) {
is CraftPersistentDataContainer -> EcoPersistentDataContainer(pdc)
else -> throw IllegalArgumentException("Custom PDC instance ims not supported!")
}
}
override fun newPdc(): PersistentDataContainer {
return CraftPersistentDataContainer(registry)
}
inner class EcoPersistentDataContainer(
val handle: CraftPersistentDataContainer
) : ExtendedPersistentDataContainer {
@Suppress("UNCHECKED_CAST")
private val customDataTags: MutableMap<String, Tag> =
CraftPersistentDataContainer::class.java.getDeclaredField("customDataTags")
.apply { isAccessible = true }.get(handle) as MutableMap<String, Tag>
override fun <T : Any, Z : Any> set(key: String, dataType: PersistentDataType<T, Z>, value: Z) {
customDataTags[key] =
registry.wrap(dataType.primitiveType, dataType.toPrimitive(value, handle.adapterContext))
}
override fun <T : Any, Z : Any> has(key: String, dataType: PersistentDataType<T, Z>): Boolean {
val value = customDataTags[key] ?: return false
return registry.isInstanceOf(dataType.primitiveType, value)
}
override fun <T : Any, Z : Any> get(key: String, dataType: PersistentDataType<T, Z>): Z? {
val value = customDataTags[key] ?: return null
return dataType.fromPrimitive(registry.extract(dataType.primitiveType, value), handle.adapterContext)
}
override fun <T : Any, Z : Any> getOrDefault(
key: String,
dataType: PersistentDataType<T, Z>,
defaultValue: Z
): Z {
return get(key, dataType) ?: defaultValue
}
override fun remove(key: String) {
customDataTags.remove(key)
}
override fun getAllKeys(): MutableSet<String> {
return customDataTags.keys
}
override fun getBase(): PersistentDataContainer {
return handle
}
}
}

View File

@@ -0,0 +1,12 @@
package com.willfp.eco.internal.spigot.proxy.v1_19_R2
import com.willfp.eco.core.fast.FastItemStack
import com.willfp.eco.internal.spigot.proxy.FastItemStackFactoryProxy
import com.willfp.eco.internal.spigot.proxy.common.item.EcoFastItemStack
import org.bukkit.inventory.ItemStack
class FastItemStackFactory : FastItemStackFactoryProxy {
override fun create(itemStack: ItemStack): FastItemStack {
return EcoFastItemStack(itemStack)
}
}

View File

@@ -0,0 +1,33 @@
package com.willfp.eco.internal.spigot.proxy.v1_19_R2
import com.willfp.eco.core.display.Display
import com.willfp.eco.internal.spigot.proxy.MiniMessageTranslatorProxy
import com.willfp.eco.util.toLegacy
import net.kyori.adventure.text.minimessage.MiniMessage
class MiniMessageTranslator : MiniMessageTranslatorProxy {
override fun format(message: String): String {
var mut = message
val startsWithPrefix = mut.startsWith(Display.PREFIX)
if (startsWithPrefix) {
mut = mut.substring(2)
}
mut = mut.replace('§', '&')
val miniMessage = runCatching {
MiniMessage.miniMessage().deserialize(
mut
).toLegacy()
}.getOrNull() ?: mut
mut = if (startsWithPrefix) {
Display.PREFIX + miniMessage
} else {
miniMessage
}
return mut
}
}

View File

@@ -0,0 +1,52 @@
package com.willfp.eco.internal.spigot.proxy.v1_19_R2
import com.willfp.eco.core.items.TestableItem
import com.willfp.eco.core.recipe.parts.EmptyTestableItem
import com.willfp.eco.internal.spigot.proxy.SNBTConverterProxy
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.SnbtPrinterTagVisitor
import net.minecraft.nbt.TagParser
import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftItemStack
import org.bukkit.inventory.ItemStack
class SNBTConverter : SNBTConverterProxy {
override fun fromSNBT(snbt: String): ItemStack? {
val nbt = runCatching { TagParser.parseTag(snbt) }.getOrNull() ?: return null
val nms = net.minecraft.world.item.ItemStack.of(nbt)
return CraftItemStack.asBukkitCopy(nms)
}
override fun toSNBT(itemStack: ItemStack): String {
val nms = CraftItemStack.asNMSCopy(itemStack)
return SnbtPrinterTagVisitor().visit(nms.save(CompoundTag()))
}
override fun makeSNBTTestable(snbt: String): TestableItem {
val nbt = runCatching { TagParser.parseTag(snbt) }.getOrNull() ?: return EmptyTestableItem()
val nms = net.minecraft.world.item.ItemStack.of(nbt)
if (nms == net.minecraft.world.item.ItemStack.EMPTY) {
return EmptyTestableItem()
}
nbt.remove("Count")
return SNBTTestableItem(CraftItemStack.asBukkitCopy(nms), nbt)
}
class SNBTTestableItem(
private val item: ItemStack,
private val tag: CompoundTag
) : TestableItem {
override fun matches(itemStack: ItemStack?): Boolean {
if (itemStack == null) {
return false
}
val nms = CraftItemStack.asNMSCopy(itemStack)
val nmsTag = nms.save(CompoundTag())
nmsTag.remove("Count")
return tag.copy().merge(nmsTag) == nmsTag
}
override fun getItem(): ItemStack = item
}
}

View File

@@ -0,0 +1,18 @@
package com.willfp.eco.internal.spigot.proxy.v1_19_R2
import com.willfp.eco.internal.spigot.proxy.SkullProxy
import com.willfp.eco.internal.spigot.proxy.common.texture
import org.bukkit.inventory.meta.SkullMeta
class Skull : SkullProxy {
override fun setSkullTexture(
meta: SkullMeta,
base64: String
) {
meta.texture = base64
}
override fun getSkullTexture(
meta: SkullMeta
): String? = meta.texture
}

View File

@@ -0,0 +1,11 @@
package com.willfp.eco.internal.spigot.proxy.v1_19_R2
import com.willfp.eco.internal.spigot.proxy.TPSProxy
import org.bukkit.Bukkit
import org.bukkit.craftbukkit.v1_19_R2.CraftServer
class TPS : TPSProxy {
override fun getTPS(): Double {
return (Bukkit.getServer() as CraftServer).handle.server.recentTps[0]
}
}

View File

@@ -0,0 +1,41 @@
package com.willfp.eco.internal.spigot.proxy.v1_19_R2
import com.willfp.eco.core.display.Display
import com.willfp.eco.internal.spigot.proxy.VillagerTradeProxy
import net.minecraft.nbt.CompoundTag
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.trading.MerchantOffer
import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftItemStack
import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftMerchantRecipe
import org.bukkit.entity.Player
import org.bukkit.inventory.MerchantRecipe
import java.lang.reflect.Field
class VillagerTrade : VillagerTradeProxy {
private val handle: Field = CraftMerchantRecipe::class.java.getDeclaredField("handle")
override fun displayTrade(
recipe: MerchantRecipe,
player: Player
): MerchantRecipe {
recipe as CraftMerchantRecipe
val nbt = getHandle(recipe).createTag()
for (tag in arrayOf("buy", "buyB", "sell")) {
val nms = ItemStack.of(nbt.getCompound(tag))
val displayed = Display.display(CraftItemStack.asBukkitCopy(nms), player)
val itemNBT = CraftItemStack.asNMSCopy(displayed).save(CompoundTag())
nbt.put(tag, itemNBT)
}
return CraftMerchantRecipe(MerchantOffer(nbt))
}
private fun getHandle(recipe: CraftMerchantRecipe): MerchantOffer {
return handle[recipe] as MerchantOffer
}
init {
handle.isAccessible = true
}
}

View File

@@ -50,6 +50,7 @@ dependencies {
compileOnly 'com.github.Gypopo:EconomyShopGUI-API:1.1.0'
compileOnly 'com.github.N0RSKA:ScytherAPI:55a'
compileOnly 'com.ticxo.modelengine:api:R3.0.1'
compileOnly 'me.TechsCode:UltraEconomyAPI:1.0.0'
// MythicMobs
compileOnly 'io.lumine:Mythic:5.0.1'

View File

@@ -4,7 +4,7 @@ import com.willfp.eco.core.Eco
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.PluginLike
import com.willfp.eco.core.PluginProps
import com.willfp.eco.core.Prerequisite
import com.willfp.eco.core.command.impl.PluginCommand
import com.willfp.eco.core.config.ConfigType
import com.willfp.eco.core.config.interfaces.Config
import com.willfp.eco.core.data.keys.PersistentDataKey
@@ -12,8 +12,7 @@ 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.placeholder.AdditionalPlayer
import com.willfp.eco.core.placeholder.PlaceholderInjectable
import com.willfp.eco.core.math.MathContext
import com.willfp.eco.internal.EcoPropsParser
import com.willfp.eco.internal.config.EcoConfigHandler
import com.willfp.eco.internal.config.EcoConfigSection
@@ -34,13 +33,13 @@ import com.willfp.eco.internal.gui.MergedStateMenu
import com.willfp.eco.internal.gui.menu.EcoMenuBuilder
import com.willfp.eco.internal.gui.menu.renderedInventory
import com.willfp.eco.internal.gui.slot.EcoSlotBuilder
import com.willfp.eco.internal.integrations.PlaceholderIntegrationPAPI
import com.willfp.eco.internal.integrations.PAPIExpansion
import com.willfp.eco.internal.logging.EcoLogger
import com.willfp.eco.internal.proxy.EcoProxyFactory
import com.willfp.eco.internal.scheduling.EcoScheduler
import com.willfp.eco.internal.spigot.data.DataYml
import com.willfp.eco.internal.spigot.data.EcoProfileHandler
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
@@ -52,10 +51,12 @@ import com.willfp.eco.internal.spigot.proxy.FastItemStackFactoryProxy
import com.willfp.eco.internal.spigot.proxy.MiniMessageTranslatorProxy
import com.willfp.eco.internal.spigot.proxy.SNBTConverterProxy
import com.willfp.eco.internal.spigot.proxy.SkullProxy
import com.willfp.eco.internal.spigot.proxy.BukkitCommandsProxy
import com.willfp.eco.internal.spigot.proxy.TPSProxy
import net.kyori.adventure.platform.bukkit.BukkitAudiences
import org.bukkit.Location
import org.bukkit.NamespacedKey
import org.bukkit.command.Command
import org.bukkit.command.CommandMap
import org.bukkit.configuration.ConfigurationSection
import org.bukkit.entity.Entity
import org.bukkit.entity.Mob
@@ -72,7 +73,7 @@ private val loadedEcoPlugins = mutableMapOf<String, EcoPlugin>()
class EcoImpl : EcoSpigotPlugin(), Eco {
override val dataYml = DataYml(this)
override val profileHandler = EcoProfileHandler(
override val profileHandler = ProfileHandler(
HandlerType.valueOf(this.configYml.getString("data-handler").uppercase()),
this
)
@@ -81,10 +82,6 @@ class EcoImpl : EcoSpigotPlugin(), Eco {
getProxy(CommonsInitializerProxy::class.java).init()
}
private var adventure: BukkitAudiences? = if (!Prerequisite.HAS_PAPER.isMet) {
BukkitAudiences.create(this)
} else null
@Suppress("RedundantNullableReturnType")
private val keyFactory: InternalNamespacedKeyFactory? =
if (this.configYml.getBool("use-safer-namespacedkey-creation"))
@@ -114,8 +111,9 @@ class EcoImpl : EcoSpigotPlugin(), Eco {
override fun createLogger(plugin: EcoPlugin) =
EcoLogger(plugin)
override fun createPAPIIntegration(plugin: EcoPlugin) =
PlaceholderIntegrationPAPI(plugin)
override fun createPAPIIntegration(plugin: EcoPlugin) {
PAPIExpansion(plugin)
}
override fun getEcoPlugin(): EcoPlugin =
this
@@ -174,9 +172,6 @@ class EcoImpl : EcoSpigotPlugin(), Eco {
override fun createDropQueue(player: Player) = if (this.configYml.getBool("use-fast-collated-drops"))
EcoFastCollatedDropQueue(player) else EcoDropQueue(player)
override fun getPersistentDataKeyFrom(namespacedKey: NamespacedKey) =
KeyRegistry.getKeyFrom(namespacedKey)
override fun getRegisteredPersistentDataKeys() =
KeyRegistry.getRegisteredKeys()
@@ -235,7 +230,7 @@ class EcoImpl : EcoSpigotPlugin(), Eco {
MetricHandler.createMetrics(plugin)
override fun getAdventure() =
adventure
bukkitAudiences
override fun getServerProfile() =
profileHandler.loadServerProfile()
@@ -243,12 +238,6 @@ class EcoImpl : EcoSpigotPlugin(), Eco {
override fun loadPlayerProfile(uuid: UUID) =
profileHandler.load(uuid)
override fun saveAllProfiles() =
profileHandler.save()
override fun savePersistentDataKeysFor(uuid: UUID, keys: Set<PersistentDataKey<*>>) =
profileHandler.saveKeysFor(uuid, keys)
override fun unloadPlayerProfile(uuid: UUID) =
profileHandler.unloadPlayer(uuid)
@@ -292,13 +281,18 @@ class EcoImpl : EcoSpigotPlugin(), Eco {
override fun getTPS() =
getProxy(TPSProxy::class.java).getTPS()
override fun evaluate(
expression: String,
player: Player?,
injectable: PlaceholderInjectable,
additionalPlayers: MutableCollection<AdditionalPlayer>
) = evaluateExpression(expression, player, injectable, additionalPlayers)
override fun evaluate(expression: String, context: MathContext) =
evaluateExpression(expression, context)
override fun getOpenMenu(player: Player) =
player.renderedInventory?.menu
override fun syncCommands() =
this.getProxy(BukkitCommandsProxy::class.java).syncCommands()
override fun getCommandMap(): CommandMap =
this.getProxy(BukkitCommandsProxy::class.java).getCommandMap()
override fun unregisterCommand(command: PluginCommand) =
this.getProxy(BukkitCommandsProxy::class.java).unregisterCommand(command)
}

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