Compare commits

...

89 Commits

Author SHA1 Message Date
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
Auxilor
4077a4c28b Updated to 6.43.1 2022-10-02 20:01:30 +01:00
Auxilor
6c375ef297 Fixed changing held item edge case 2022-10-02 20:01:23 +01:00
_OfTeN_
7767e48e51 Fixed scyther integration 2022-09-14 23:03:20 +03:00
94 changed files with 2516 additions and 659 deletions

View File

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

View File

@@ -21,10 +21,8 @@ import com.willfp.eco.core.gui.menu.MenuBuilder;
import com.willfp.eco.core.gui.menu.MenuType; import com.willfp.eco.core.gui.menu.MenuType;
import com.willfp.eco.core.gui.slot.SlotBuilder; import com.willfp.eco.core.gui.slot.SlotBuilder;
import com.willfp.eco.core.gui.slot.functional.SlotProvider; 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.items.TestableItem;
import com.willfp.eco.core.placeholder.AdditionalPlayer; import com.willfp.eco.core.math.MathContext;
import com.willfp.eco.core.placeholder.PlaceholderInjectable;
import com.willfp.eco.core.proxy.ProxyFactory; import com.willfp.eco.core.proxy.ProxyFactory;
import com.willfp.eco.core.scheduling.Scheduler; import com.willfp.eco.core.scheduling.Scheduler;
import net.kyori.adventure.platform.bukkit.BukkitAudiences; import net.kyori.adventure.platform.bukkit.BukkitAudiences;
@@ -42,7 +40,6 @@ import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@@ -138,10 +135,8 @@ public interface Eco {
* Create a PAPI integration. * Create a PAPI integration.
* *
* @param plugin The plugin. * @param plugin The plugin.
* @return The integration.
*/ */
@NotNull void createPAPIIntegration(@NotNull EcoPlugin plugin);
PlaceholderIntegration createPAPIIntegration(@NotNull EcoPlugin plugin);
/** /**
* Create a proxy factory. * Create a proxy factory.
@@ -173,6 +168,7 @@ public interface Eco {
* @param requiresChangesToSave If the config must be changed in order to save the config. * @param requiresChangesToSave If the config must be changed in order to save the config.
* @return The config implementation. * @return The config implementation.
*/ */
@NotNull
LoadableConfig createUpdatableConfig(@NotNull String configName, LoadableConfig createUpdatableConfig(@NotNull String configName,
@NotNull PluginLike plugin, @NotNull PluginLike plugin,
@NotNull String subDirectoryPath, @NotNull String subDirectoryPath,
@@ -193,6 +189,7 @@ public interface Eco {
* @param requiresChangesToSave If the config must be changed in order to save the config. * @param requiresChangesToSave If the config must be changed in order to save the config.
* @return The config implementation. * @return The config implementation.
*/ */
@NotNull
LoadableConfig createLoadableConfig(@NotNull String configName, LoadableConfig createLoadableConfig(@NotNull String configName,
@NotNull PluginLike plugin, @NotNull PluginLike plugin,
@NotNull String subDirectoryPath, @NotNull String subDirectoryPath,
@@ -206,6 +203,7 @@ public interface Eco {
* @param config The handle. * @param config The handle.
* @return The config implementation. * @return The config implementation.
*/ */
@NotNull
Config wrapConfigurationSection(@NotNull ConfigurationSection config); Config wrapConfigurationSection(@NotNull ConfigurationSection config);
/** /**
@@ -215,6 +213,7 @@ public interface Eco {
* @param type The config type. * @param type The config type.
* @return The config implementation. * @return The config implementation.
*/ */
@NotNull
Config createConfig(@NotNull Map<String, Object> values, Config createConfig(@NotNull Map<String, Object> values,
@NotNull ConfigType type); @NotNull ConfigType type);
@@ -225,6 +224,7 @@ public interface Eco {
* @param type The type. * @param type The type.
* @return The config implementation. * @return The config implementation.
*/ */
@NotNull
Config createConfig(@NotNull String contents, Config createConfig(@NotNull String contents,
@NotNull ConfigType type); @NotNull ConfigType type);
@@ -335,23 +335,16 @@ public interface Eco {
* *
* @return The keys. * @return The keys.
*/ */
@NotNull
Set<PersistentDataKey<?>> getRegisteredPersistentDataKeys(); 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. * Load a player profile.
* *
* @param uuid The UUID. * @param uuid The UUID.
* @return The profile. * @return The profile.
*/ */
@NotNull
PlayerProfile loadPlayerProfile(@NotNull UUID uuid); PlayerProfile loadPlayerProfile(@NotNull UUID uuid);
/** /**
@@ -359,6 +352,7 @@ public interface Eco {
* *
* @return The profile. * @return The profile.
*/ */
@NotNull
ServerProfile getServerProfile(); ServerProfile getServerProfile();
/** /**
@@ -370,24 +364,6 @@ public interface Eco {
*/ */
void unloadPlayerProfile(@NotNull UUID uuid); 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. * Create dummy entity - never spawned, exists purely in code.
* *
@@ -513,16 +489,12 @@ public interface Eco {
/** /**
* Evaluate an expression. * Evaluate an expression.
* *
* @param expression The expression. * @param expression The expression.
* @param player The player. * @param context The context.
* @param injectable The injectable placeholders.
* @param additionalPlayers The additional players.
* @return The value of the expression, or zero if invalid. * @return The value of the expression, or zero if invalid.
*/ */
double evaluate(@NotNull String expression, double evaluate(@NotNull String expression,
@Nullable Player player, @NotNull MathContext context);
@NotNull PlaceholderInjectable injectable,
@NotNull Collection<AdditionalPlayer> additionalPlayers);
/** /**
* Get the menu a player currently has open. * Get the menu a player currently has open.
@@ -533,6 +505,11 @@ public interface Eco {
@Nullable @Nullable
Menu getOpenMenu(@NotNull Player player); Menu getOpenMenu(@NotNull Player player);
/**
* Sync commands.
*/
void syncCommands();
/** /**
* Get the instance of eco; the bridge between the api frontend * Get the instance of eco; the bridge between the api frontend
* and the implementation backend. * 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.NamespacedKeyFactory;
import com.willfp.eco.core.factory.RunnableFactory; import com.willfp.eco.core.factory.RunnableFactory;
import com.willfp.eco.core.integrations.IntegrationLoader; 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.proxy.ProxyFactory;
import com.willfp.eco.core.scheduling.Scheduler; import com.willfp.eco.core.scheduling.Scheduler;
import com.willfp.eco.core.web.UpdateChecker; import com.willfp.eco.core.web.UpdateChecker;
@@ -153,6 +152,31 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
@Nullable @Nullable
private final ProxyFactory proxyFactory; 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. * Create a new plugin.
* <p> * <p>
@@ -375,8 +399,7 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
.collect(Collectors.toSet()); .collect(Collectors.toSet());
if (enabledPlugins.contains("PlaceholderAPI".toLowerCase())) { if (enabledPlugins.contains("PlaceholderAPI".toLowerCase())) {
this.loadedIntegrations.add("PlaceholderAPI"); Eco.get().createPAPIIntegration(this);
PlaceholderManager.addIntegration(Eco.get().createPAPIIntegration(this));
} }
this.loadIntegrationLoaders().forEach(integrationLoader -> { 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())); this.getLogger().info("Loaded integrations: " + String.join(", ", this.getLoadedIntegrations()));
Prerequisite.update(); Prerequisite.update();
@@ -414,10 +439,20 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
} }
this.handleEnable(); this.handleEnable();
this.onEnable.forEach(Runnable::run);
this.getLogger().info(""); 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. * Default code to be executed on plugin disable.
*/ */
@@ -429,6 +464,7 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
this.getScheduler().cancelAll(); this.getScheduler().cancelAll();
this.handleDisable(); this.handleDisable();
this.onDisable.forEach(Runnable::run);
if (this.isSupportingExtensions()) { if (this.isSupportingExtensions()) {
this.getExtensionLoader().unloadExtensions(); this.getExtensionLoader().unloadExtensions();
@@ -438,6 +474,15 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
Eco.get().clean(this); 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. * Default code to be executed on plugin load.
*/ */
@@ -446,6 +491,16 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
super.onLoad(); super.onLoad();
this.handleLoad(); 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.handleAfterLoad();
this.afterLoad.forEach(Runnable::run);
this.reload(); this.reload();
@@ -488,6 +544,15 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
this.getLogger().info("Loaded " + this.color + this.getName()); 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. * Reload the plugin.
*/ */
@@ -499,12 +564,22 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
this.getConfigHandler().callUpdate(); // Call twice to fix issues this.getConfigHandler().callUpdate(); // Call twice to fix issues
this.handleReload(); this.handleReload();
this.onReload.forEach(Runnable::run);
for (Extension extension : this.extensionLoader.getLoadedExtensions()) { for (Extension extension : this.extensionLoader.getLoadedExtensions()) {
extension.handleReload(); 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. * Reload the plugin and return the time taken to reload.
* *

View File

@@ -1,7 +1,9 @@
package com.willfp.eco.core.command; package com.willfp.eco.core.command;
import com.google.common.collect.ImmutableList;
import com.willfp.eco.core.EcoPlugin; import com.willfp.eco.core.EcoPlugin;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
@@ -43,8 +45,6 @@ public interface CommandBase {
/** /**
* Handle command execution. * Handle command execution.
* <p>
* Marked as default void with no implementation for backwards compatibility.
* *
* @param sender The sender. * @param sender The sender.
* @param args The args. * @param args The args.
@@ -54,20 +54,43 @@ public interface CommandBase {
// Do nothing. // 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. * Handle tab completion.
* <p>
* Marked as default void with no implementation for backwards compatibility.
* *
* @param sender The sender. * @param sender The sender.
* @param args The args. * @param args The args.
* @return The results. * @return The results.
*/ */
@NotNull
default List<String> tabComplete(@NotNull CommandSender sender, default List<String> tabComplete(@NotNull CommandSender sender,
@NotNull List<String> args) { @NotNull List<String> args) {
return new ArrayList<>(); 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. * Get the plugin.
* *
@@ -83,7 +106,11 @@ public interface CommandBase {
* @deprecated Use {@link CommandBase#onExecute(CommandSender, List)} instead. * @deprecated Use {@link CommandBase#onExecute(CommandSender, List)} instead.
*/ */
@Deprecated(forRemoval = true) @Deprecated(forRemoval = true)
CommandHandler getHandler(); default CommandHandler getHandler() {
return (a, b) -> {
};
}
/** /**
* Set the handler. * Set the handler.
@@ -93,7 +120,9 @@ public interface CommandBase {
* @deprecated Handlers have been deprecated. * @deprecated Handlers have been deprecated.
*/ */
@Deprecated(forRemoval = true) @Deprecated(forRemoval = true)
void setHandler(@NotNull CommandHandler handler); default void setHandler(@NotNull final CommandHandler handler) {
// Do nothing.
}
/** /**
* Get the tab completer. * Get the tab completer.
@@ -103,7 +132,9 @@ public interface CommandBase {
* @deprecated Use {@link CommandBase#tabComplete(CommandSender, List)} instead. * @deprecated Use {@link CommandBase#tabComplete(CommandSender, List)} instead.
*/ */
@Deprecated(forRemoval = true) @Deprecated(forRemoval = true)
TabCompleteHandler getTabCompleter(); default TabCompleteHandler getTabCompleter() {
return (a, b) -> ImmutableList.of();
}
/** /**
* Set the tab completer. * Set the tab completer.
@@ -113,5 +144,7 @@ public interface CommandBase {
* @deprecated Handlers have been deprecated. * @deprecated Handlers have been deprecated.
*/ */
@Deprecated(forRemoval = true) @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)); this.getHandler().onExecute(sender, Arrays.asList(args));
} else { } else {
this.onExecute(sender, Arrays.asList(args)); 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) { if (this.getTabCompleter() != null) {
return this.getTabCompleter().tabComplete(sender, Arrays.asList(args)); return this.getTabCompleter().tabComplete(sender, Arrays.asList(args));
} else { } else {
return this.tabComplete(sender, Arrays.asList(args)); List<String> completions = 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; package com.willfp.eco.core.command.impl;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.EcoPlugin; import com.willfp.eco.core.EcoPlugin;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandMap;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter; import org.bukkit.command.TabCompleter;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
@@ -38,14 +42,63 @@ public abstract class PluginCommand extends HandledCommand implements CommandExe
/** /**
* Registers the command with the server, * Registers the command with the server,
* <p>
* Requires the command name to exist, defined in plugin.yml.
*/ */
public final void register() { public final void register() {
org.bukkit.command.PluginCommand command = Bukkit.getPluginCommand(this.getName()); org.bukkit.command.PluginCommand command = Bukkit.getPluginCommand(this.getName());
assert command != null; if (command != null) {
command.setExecutor(this); command.setExecutor(this);
command.setTabCompleter(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();
Command found = commandMap.getCommand(this.getName());
if (found != null) {
found.unregister(commandMap);
}
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 +146,19 @@ public abstract class PluginCommand extends HandledCommand implements CommandExe
return this.handleTabCompletion(sender, args); 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. * Builder for configs to create them programmatically.
*/ */
public class BuildableConfig extends TransientConfig { public class BuildableConfig extends GenericConfig {
/** /**
* Create a new empty config builder. * Create a new empty config builder.
*/ */
public BuildableConfig() { 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. * Config that exists purely in the code, not linked to any file.
* <p> * <p>
* Use for inline configs to move data around or to add subsections to other configs. * 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> { public class TransientConfig extends ConfigWrapper<Config> {
/** /**
* @param config The ConfigurationSection handle. * @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.BuildableConfig;
import com.willfp.eco.core.config.ConfigType; import com.willfp.eco.core.config.ConfigType;
import com.willfp.eco.core.config.TransientConfig; import com.willfp.eco.core.config.Configs;
import com.willfp.eco.core.placeholder.AdditionalPlayer; import com.willfp.eco.core.placeholder.AdditionalPlayer;
import com.willfp.eco.core.placeholder.InjectablePlaceholder; import com.willfp.eco.core.placeholder.InjectablePlaceholder;
import com.willfp.eco.core.placeholder.PlaceholderInjectable; import com.willfp.eco.core.placeholder.PlaceholderInjectable;
@@ -103,7 +103,7 @@ public interface Config extends Cloneable, PlaceholderInjectable {
*/ */
@NotNull @NotNull
default Config getSubsection(@NotNull String path) { 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.Inventory;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.persistence.PersistentDataType; import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;

View File

@@ -63,11 +63,29 @@ public interface Menu {
* @param player The player * @param player The player
* @param menu The menu. * @param menu The menu.
* @return The slot. * @return The slot.
* @deprecated Menu shouldn't be a parameter.
*/ */
default Slot getSlot(int row, @Deprecated(since = "6.46.0", forRemoval = true)
int column, default Slot getSlot(final int row,
@NotNull Player player, final int column,
@NotNull Menu menu) { @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); 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 player The player.
* @param key The key. * @param key The key.
* @param value The state. * @param value The state.
*/ */
void addState(@NotNull Player player, default void setState(@NotNull Player player,
@NotNull String key, @NotNull String key,
@Nullable Object value); @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. * Remove state for a player.
@@ -221,7 +256,7 @@ public interface Menu {
@NotNull final NamespacedKey key, @NotNull final NamespacedKey key,
@NotNull final PersistentDataType<T, Z> type, @NotNull final PersistentDataType<T, Z> type,
@NotNull final Z value) { @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. * @return The builder.
*/ */
default MenuBuilder addPage(@NotNull final Page page) { 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. * @return The builder.
*/ */
default MenuBuilder maxPages(@NotNull final Function<Player, Integer> pages) { default MenuBuilder maxPages(@NotNull final Function<Player, Integer> pages) {
return onOpen((player, menu) -> menu.addState(player, Page.MAX_PAGE_KEY, pages.apply(player))); return onRender((player, menu) -> menu.setState(player, Page.MAX_PAGE_KEY, pages.apply(player)));
} }
/** /**
@@ -195,6 +195,16 @@ public interface MenuBuilder extends PageBuilder {
return this; 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. * 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.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.Objects;
/** /**
* A page is a component representing another menu. * A page is a component representing another menu.
* This allows full component support in pagination. * This allows full component support in pagination.
@@ -85,7 +83,7 @@ public final class Page implements GUIComponent {
delegate = Eco.get().blendMenuState(page, menu); delegate = Eco.get().blendMenuState(page, menu);
} }
return page.getSlot(row, column, player, delegate); return page.getSlot(row, column, player);
} }
@Override @Override

View File

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

View File

@@ -31,7 +31,7 @@ public abstract class CustomSlot implements Slot {
} }
@Override @Override
public ItemStack getItemStack(@NotNull final Player player) { public @NotNull ItemStack getItemStack(@NotNull final Player player) {
if (delegate == null) { if (delegate == null) {
throw new IllegalStateException("Custom Slot was not initialized!"); throw new IllegalStateException("Custom Slot was not initialized!");
} }

View File

@@ -29,8 +29,8 @@ public abstract class ReactiveSlot implements Slot {
@NotNull final Menu menu); @NotNull final Menu menu);
@Override @Override
public ItemStack getItemStack(@NotNull final Player player) { public @NotNull ItemStack getItemStack(@NotNull final Player player) {
return new ItemStack(Material.STONE); return new ItemStack(Material.AIR);
} }
@Override @Override
@@ -40,8 +40,8 @@ public abstract class ReactiveSlot implements Slot {
} }
@Override @Override
public final Slot getActionableSlot(@NotNull final Player player, public final @NotNull Slot getActionableSlot(@NotNull final Player player,
@NotNull final Menu menu) { @NotNull final Menu menu) {
return getSlot(player, menu); return getSlot(player, menu);
} }

View File

@@ -30,6 +30,7 @@ public interface Slot extends GUIComponent {
* @param player The player. * @param player The player.
* @return The ItemStack. * @return The ItemStack.
*/ */
@NotNull
ItemStack getItemStack(@NotNull Player player); ItemStack getItemStack(@NotNull Player player);
/** /**
@@ -60,6 +61,7 @@ public interface Slot extends GUIComponent {
* @param menu The menu. * @param menu The menu.
* @return The slot. * @return The slot.
*/ */
@NotNull
default Slot getActionableSlot(@NotNull final Player player, default Slot getActionableSlot(@NotNull final Player player,
@NotNull final Menu menu) { @NotNull final Menu menu) {
return this; return this;
@@ -125,7 +127,9 @@ public interface Slot extends GUIComponent {
* *
* @param provider The provider. * @param provider The provider.
* @return The builder. * @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) { static SlotBuilder builder(@NotNull final Function<Player, ItemStack> provider) {
return Eco.get().createSlotBuilder((player, menu) -> provider.apply(player)); return Eco.get().createSlotBuilder((player, menu) -> provider.apply(player));
} }

View File

@@ -51,10 +51,15 @@ public final class PlaceholderManager {
.expireAfterWrite(50, TimeUnit.MILLISECONDS) .expireAfterWrite(50, TimeUnit.MILLISECONDS)
.build(key -> key.entry.getValue(key.player)); .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. * Empty injectable object.
*/ */
private static final PlaceholderInjectable EMPTY_INJECTABLE = new PlaceholderInjectable() { public static final PlaceholderInjectable EMPTY_INJECTABLE = new PlaceholderInjectable() {
@Override @Override
public void clearInjectedPlaceholders() { public void clearInjectedPlaceholders() {
// Do nothing. // 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. * Register a new placeholder integration.
* *

View File

@@ -1,10 +1,7 @@
package com.willfp.eco.core.integrations.shop; package com.willfp.eco.core.integrations.shop;
import com.willfp.eco.core.EcoPlugin;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;

View File

@@ -25,6 +25,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
@@ -86,6 +87,11 @@ public final class Items {
*/ */
private static final TestableItem EMPTY_TESTABLE_ITEM = new EmptyTestableItem(); 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. * Register a new custom item.
* *
@@ -216,7 +222,7 @@ public final class Items {
if (isWildcard) { if (isWildcard) {
itemType = itemType.substring(1); itemType = itemType.substring(1);
} }
Material material = Material.getMaterial(itemType.toUpperCase()); Material material = FRIENDLY_MATERIAL_NAMES.get(itemType.toLowerCase());
if (material == null || material == Material.AIR) { if (material == null || material == Material.AIR) {
return new EmptyTestableItem(); return new EmptyTestableItem();
} }
@@ -565,4 +571,25 @@ public final class Items {
private Items() { private Items() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); 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.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.function.Supplier; import java.util.function.Supplier;

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 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.
*/
private 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) {
return this.price.canAfford(player);
}
@Override
public void pay(@NotNull final Player player) {
this.price.pay(player);
}
@Override
public void giveTo(@NotNull final Player player) {
this.price.giveTo(player);
}
@Override
public double getValue(@NotNull final Player player) {
return this.price.getValue(player);
}
@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);
}
/**
* 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 StringUtils.format(
formatString.replace("%value%", NumberUtils.format(this.getPrice().getValue(player))),
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,91 @@
package com.willfp.eco.core.price;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
/**
* A price that a player should pay.
*/
public interface Price {
/**
* Get if the player can afford the price.
*
* @param player The player.
* @return If the player can afford.
*/
boolean canAfford(@NotNull Player player);
/**
* Make the player pay the price.
* <p>
* Only run this if the player can afford the price.
*
* @param player The player.
*/
void pay(@NotNull Player player);
/**
* Give the value of the price to the player.
* <p>
* You should override this method, it's only marked as default for
* backwards compatibility purposes.
*
* @param player The player.
*/
default void giveTo(@NotNull Player player) {
// Override when needed.
}
/**
* If the price is backed by a value, get it here.
*
* @param player The player.
* @return The value.
*/
default double getValue(@NotNull final Player player) {
return 0;
}
/**
* 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.
*/
@Deprecated(since = "6.45.0", forRemoval = true)
default void setValue(final double value) {
// Override when needed.
}
/**
* Get the price multiplier for a player.
*
* @param player The player.
* @return The value.
*/
default double getMultiplier(@NotNull final Player player) {
return 1;
}
/**
* Set the price multiplier for a player.
*
* @param player The player.
* @param multiplier The multiplier.
*/
default void setMultiplier(@NotNull final Player player,
final double multiplier) {
// 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,84 @@
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) {
return EconomyManager.getBalance(player) >= getValue(player);
}
@Override
public void pay(@NotNull final Player player) {
EconomyManager.removeMoney(player, getValue(player));
}
@Override
public void giveTo(@NotNull final Player player) {
EconomyManager.giveMoney(player, getValue(player));
}
@Override
public double getValue(@NotNull final Player player) {
return this.function.apply(MathContext.copyWithPlayer(baseContext, player)) * getMultiplier(player);
}
@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);
}
}

View File

@@ -0,0 +1,27 @@
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) {
return true;
}
@Override
public void pay(@NotNull final Player player) {
// Do nothing.
}
}

View File

@@ -0,0 +1,149 @@
package com.willfp.eco.core.price.impl;
import com.willfp.eco.core.drops.DropQueue;
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) {
int toRemove = (int) getValue(player);
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) {
int toRemove = (int) getValue(player);
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) {
ItemStack itemStack = item.getItem().clone();
itemStack.setAmount((int) getValue(player));
new DropQueue(player)
.addItem(itemStack)
.forceTelekinesis()
.push();
}
@Override
public double getValue(@NotNull final Player player) {
return Math.toIntExact(Math.round(
this.function.apply(MathContext.copyWithPlayer(baseContext, player)) * getMultiplier(player)
));
}
@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);
}
}

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

View File

@@ -3,9 +3,12 @@
package com.willfp.eco.core.config package com.willfp.eco.core.config
import com.willfp.eco.core.config.interfaces.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. */ /** 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. * 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 = fun config(type: ConfigType = ConfigType.YAML, builder: DSLConfig.() -> Unit): Config =
DSLConfig(type).apply(builder) 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.Menu
import com.willfp.eco.core.gui.menu.MenuBuilder 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.MenuEvent
import com.willfp.eco.core.gui.menu.MenuEventHandler 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.Page
import com.willfp.eco.core.gui.page.PageBuilder import com.willfp.eco.core.gui.page.PageBuilder
import com.willfp.eco.core.gui.slot.Slot import com.willfp.eco.core.gui.slot.Slot
@@ -184,6 +184,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. */ /** Kotlin builder for menus. */
fun menu( fun menu(
rows: Int, rows: Int,

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

View File

@@ -108,7 +108,7 @@ open class EcoConfig(
} }
override fun getSubsectionsOrNull(path: String): List<Config>? { override fun getSubsectionsOrNull(path: String): List<Config>? {
return (get(path) as? Iterable<Config>) return getList<Config>(path)
?.map { it.apply { this.addInjectablePlaceholder(injections) } } ?.map { it.apply { this.addInjectablePlaceholder(injections) } }
?.toList() ?.toList()
} }
@@ -122,7 +122,7 @@ open class EcoConfig(
} }
override fun getIntsOrNull(path: String): List<Int>? { 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? { override fun getBoolOrNull(path: String): Boolean? {
@@ -130,7 +130,7 @@ open class EcoConfig(
} }
override fun getBoolsOrNull(path: String): List<Boolean>? { override fun getBoolsOrNull(path: String): List<Boolean>? {
return (get(path) as? Iterable<Boolean>)?.toList() return getList<Boolean>(path)?.toList()
} }
override fun getStringOrNull( override fun getStringOrNull(
@@ -154,7 +154,7 @@ open class EcoConfig(
format: Boolean, format: Boolean,
option: StringUtils.FormatOption option: StringUtils.FormatOption
): List<String>? { ): List<String>? {
val strings = (get(path) as? Iterable<*>) val strings = getList<Any?>(path)
?.map { it?.toString() ?: "" } ?.map { it?.toString() ?: "" }
?.toMutableList() ?: return null ?.toMutableList() ?: return null
if (placeholderInjections.isNotEmpty() && format && option == StringUtils.FormatOption.WITH_PLACEHOLDERS) { if (placeholderInjections.isNotEmpty() && format && option == StringUtils.FormatOption.WITH_PLACEHOLDERS) {
@@ -176,7 +176,7 @@ open class EcoConfig(
} }
override fun getDoublesOrNull(path: String): List<Double>? { 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>) { override fun addInjectablePlaceholder(placeholders: Iterable<InjectablePlaceholder>) {
@@ -215,4 +215,15 @@ open class EcoConfig(
override fun toString(): String { override fun toString(): String {
return this.toPlaintext() 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.google.common.collect.ImmutableSet
import com.willfp.eco.core.Eco import com.willfp.eco.core.Eco
import com.willfp.eco.core.EcoPlugin import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.config.TransientConfig import com.willfp.eco.core.config.toConfig
import com.willfp.eco.core.extensions.Extension import com.willfp.eco.core.extensions.Extension
import com.willfp.eco.core.extensions.ExtensionLoader import com.willfp.eco.core.extensions.ExtensionLoader
import com.willfp.eco.core.extensions.ExtensionMetadata import com.willfp.eco.core.extensions.ExtensionMetadata
@@ -48,7 +48,7 @@ class EcoExtensionLoader(
val ymlIn = classLoader.getResourceAsStream("extension.yml") val ymlIn = classLoader.getResourceAsStream("extension.yml")
?: throw MalformedExtensionException("No extension.yml found in " + extensionJar.name) ?: 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") val mainClass = extensionYml.getStringOrNull("main")
var name = extensionYml.getStringOrNull("name") var name = extensionYml.getStringOrNull("name")

View File

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

View File

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

View File

@@ -22,6 +22,7 @@ class EcoMenuBuilder(
private val onOpen = mutableListOf<OpenHandler>() private val onOpen = mutableListOf<OpenHandler>()
private val onRender = mutableListOf<(Player, Menu) -> Unit>() private val onRender = mutableListOf<(Player, Menu) -> Unit>()
private val menuEventHandlers = mutableListOf<MenuEventHandler<*>>() private val menuEventHandlers = mutableListOf<MenuEventHandler<*>>()
private val onBuild = mutableListOf<(Menu) -> Unit>()
private var allowsChangingHeldItem = false private var allowsChangingHeldItem = false
override fun getRows() = rows override fun getRows() = rows
@@ -80,13 +81,18 @@ class EcoMenuBuilder(
return this return this
} }
override fun onBuild(action: Consumer<Menu>): MenuBuilder {
onBuild += { action.accept(it) }
return this
}
override fun allowChangingHeldItem(): MenuBuilder { override fun allowChangingHeldItem(): MenuBuilder {
allowsChangingHeldItem = true allowsChangingHeldItem = true
return this return this
} }
override fun build(): Menu { override fun build(): Menu {
val layeredComponents = mutableMapOf<MenuLayer, MutableMap<GUIPosition, MutableList<OffsetComponent>>>() val layeredComponents = LayeredComponents()
// 5 nested for loops? Shut up. Silence. Quiet. // 5 nested for loops? Shut up. Silence. Quiet.
for (layer in MenuLayer.values()) { for (layer in MenuLayer.values()) {
@@ -107,11 +113,14 @@ class EcoMenuBuilder(
val point = GUIPosition(row, column) val point = GUIPosition(row, column)
layeredComponents.computeIfAbsent(layer) { mutableMapOf() } layeredComponents.addOffsetComponent(
.computeIfAbsent(point) { mutableListOf() } += OffsetComponent( layer,
component, point,
rowOffset, OffsetComponent(
columnOffset component,
rowOffset,
columnOffset
)
) )
} }
} }
@@ -119,18 +128,10 @@ class EcoMenuBuilder(
} }
} }
val componentsAtPoints = mutableMapOf<GUIPosition, MutableList<OffsetComponent>>() val menu = EcoMenu(
for (menuLayer in MenuLayer.values()) {
for ((anchor, offsetComponents) in layeredComponents[menuLayer] ?: emptyMap()) {
componentsAtPoints[anchor] = offsetComponents
}
}
return EcoMenu(
rows, rows,
columns, columns,
componentsAtPoints, layeredComponents,
title, title,
onClose, onClose,
onRender, onRender,
@@ -138,5 +139,9 @@ class EcoMenuBuilder(
menuEventHandlers, menuEventHandlers,
allowsChangingHeldItem 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,5 +1,6 @@
package com.willfp.eco.internal.gui.menu 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.items.isEmpty
import com.willfp.eco.core.recipe.parts.EmptyTestableItem import com.willfp.eco.core.recipe.parts.EmptyTestableItem
import com.willfp.eco.util.MenuUtils import com.willfp.eco.util.MenuUtils
@@ -31,14 +32,14 @@ class RenderedInventory(
val state = mutableMapOf<String, Any?>() val state = mutableMapOf<String, Any?>()
fun render() { fun render() {
captiveItems.clear() val newCaptive = mutableMapOf<GUIPosition, ItemStack>()
for (row in (1..menu.rows)) { for (row in (1..menu.rows)) {
for (column in (1..menu.columns)) { for (column in (1..menu.columns)) {
val position = GUIPosition(row, column) val position = GUIPosition(row, column)
val bukkit = MenuUtils.rowColumnToSlot(row, column, 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)
val renderedItem = slot.getItemStack(player) val renderedItem = slot.getItemStack(player)
if (slot.isCaptive(player, menu)) { if (slot.isCaptive(player, menu)) {
@@ -46,11 +47,11 @@ class RenderedInventory(
if (slot.isCaptiveFromEmpty) { if (slot.isCaptiveFromEmpty) {
if (!actualItem.isEmpty) { if (!actualItem.isEmpty) {
captiveItems[position] = actualItem newCaptive[position] = actualItem
} }
} else { } else {
if (actualItem != renderedItem && !EmptyTestableItem().matches(actualItem)) { if (actualItem != renderedItem && !EmptyTestableItem().matches(actualItem)) {
captiveItems[position] = actualItem newCaptive[position] = actualItem
} }
} }
} else { } else {
@@ -59,15 +60,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) 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() { fun renderDefaultCaptiveItems() {
menu.runOnRender(player)
for (row in (1..menu.rows)) { for (row in (1..menu.rows)) {
for (column in (1..menu.columns)) { for (column in (1..menu.columns)) {
val bukkit = MenuUtils.rowColumnToSlot(row, column, 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)) { if (slot.isCaptive(player, menu)) {
inventory.setItem(bukkit, slot.getItemStack(player)) inventory.setItem(bukkit, slot.getItemStack(player))

View File

@@ -1,13 +1,15 @@
package com.willfp.eco.internal.integrations package com.willfp.eco.internal.integrations
import com.willfp.eco.core.EcoPlugin import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.integrations.placeholder.PlaceholderIntegration
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager import com.willfp.eco.core.integrations.placeholder.PlaceholderManager
import me.clip.placeholderapi.PlaceholderAPI
import me.clip.placeholderapi.expansion.PlaceholderExpansion import me.clip.placeholderapi.expansion.PlaceholderExpansion
import org.bukkit.entity.Player 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 { override fun persist(): Boolean {
return true return true
} }
@@ -34,29 +36,4 @@ class PlaceholderIntegrationPAPI(private val plugin: EcoPlugin) : PlaceholderExp
): String { ): String {
return PlaceholderManager.getResult(player, identifier, plugin) 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 { return Predicate {
val testMeta = it.itemMeta ?: return@Predicate false val testMeta = it.itemMeta ?: return@Predicate false
if (!testMeta.hasCustomModelData()) {
return@Predicate false
}
testMeta.customModelData == modelData 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,50 @@
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()
}
}
}

View File

@@ -0,0 +1,51 @@
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()
}
}
}

View File

@@ -29,7 +29,27 @@ var SkullMeta.texture: String?
setProfile.isAccessible = true 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) setProfile.invoke(this, null)
} else { } else {
val uuid = UUID( val uuid = UUID(

View File

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

View File

@@ -1,21 +1,23 @@
package com.willfp.eco.internal.spigot.proxy.common.ai.entity 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.EntityGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.opengoals.IllusionerMirrorSpellGoal
import net.minecraft.world.entity.PathfinderMob import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.goal.Goal import net.minecraft.world.entity.ai.goal.Goal
import net.minecraft.world.entity.monster.Illusioner import net.minecraft.world.entity.monster.Illusioner
object IllusionerMirrorSpellGoalFactory : EntityGoalFactory<EntityGoalLeapAtTarget> { object IllusionerMirrorSpellGoalFactory : EntityGoalFactory<EntityGoalIllusionerMirrorSpell> {
override fun create(apiGoal: EntityGoalLeapAtTarget, entity: PathfinderMob): Goal? { override fun create(apiGoal: EntityGoalIllusionerMirrorSpell, entity: PathfinderMob): Goal? {
if (entity !is Illusioner) return null if (entity !is Illusioner) return null
return IllusionerMirrorSpellGoal( // Have to use reflection for it to work
entity 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 override fun isGoalOfType(goal: Goal): Boolean {
|| goal::class.java.name.contains("IllusionerMirrorSpellGoal") 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

@@ -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.core.data.ExtendedPersistentDataContainer
import com.willfp.eco.internal.spigot.proxy.ExtendedPersistentDataContainerFactoryProxy import com.willfp.eco.internal.spigot.proxy.ExtendedPersistentDataContainerFactoryProxy
import net.minecraft.nbt.Tag 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.CraftPersistentDataContainer
import org.bukkit.craftbukkit.v1_17_R1.persistence.CraftPersistentDataTypeRegistry import org.bukkit.craftbukkit.v1_17_R1.persistence.CraftPersistentDataTypeRegistry
import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataContainer import org.bukkit.persistence.PersistentDataContainer
import org.bukkit.persistence.PersistentDataType import org.bukkit.persistence.PersistentDataType
class ExtendedPersistentDataContainerFactory: ExtendedPersistentDataContainerFactoryProxy { class ExtendedPersistentDataContainerFactory : ExtendedPersistentDataContainerFactoryProxy {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
private val registry: CraftPersistentDataTypeRegistry = private val registry: CraftPersistentDataTypeRegistry
CraftPersistentDataContainer::class.java.getDeclaredField("registry")
.apply { isAccessible = true }.get(null) as 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 { override fun adapt(pdc: PersistentDataContainer): ExtendedPersistentDataContainer {
return when (pdc) { return when (pdc) {
@@ -34,7 +46,8 @@ class ExtendedPersistentDataContainerFactory: ExtendedPersistentDataContainerFac
.apply { isAccessible = true }.get(handle) as MutableMap<String, Tag> .apply { isAccessible = true }.get(handle) as MutableMap<String, Tag>
override fun <T : Any, Z : Any> set(key: String, dataType: PersistentDataType<T, Z>, value: Z) { 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 { override fun <T : Any, Z : Any> has(key: String, dataType: PersistentDataType<T, Z>): Boolean {

View File

@@ -0,0 +1,11 @@
package com.willfp.eco.internal.spigot.proxy.v1_17_R1
import com.willfp.eco.internal.spigot.proxy.SyncCommandsProxy
import org.bukkit.Bukkit
import org.bukkit.craftbukkit.v1_17_R1.CraftServer
class SyncCommands : SyncCommandsProxy {
override fun syncCommands() {
(Bukkit.getServer() as CraftServer).syncCommands()
}
}

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.core.data.ExtendedPersistentDataContainer
import com.willfp.eco.internal.spigot.proxy.ExtendedPersistentDataContainerFactoryProxy import com.willfp.eco.internal.spigot.proxy.ExtendedPersistentDataContainerFactoryProxy
import net.minecraft.nbt.Tag 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.CraftPersistentDataContainer
import org.bukkit.craftbukkit.v1_18_R1.persistence.CraftPersistentDataTypeRegistry import org.bukkit.craftbukkit.v1_18_R1.persistence.CraftPersistentDataTypeRegistry
import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataContainer import org.bukkit.persistence.PersistentDataContainer
import org.bukkit.persistence.PersistentDataType import org.bukkit.persistence.PersistentDataType
class ExtendedPersistentDataContainerFactory : ExtendedPersistentDataContainerFactoryProxy { class ExtendedPersistentDataContainerFactory : ExtendedPersistentDataContainerFactoryProxy {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
private val registry: CraftPersistentDataTypeRegistry = private val registry: CraftPersistentDataTypeRegistry
CraftPersistentDataContainer::class.java.getDeclaredField("registry")
.apply { isAccessible = true }.get(null) as 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 { override fun adapt(pdc: PersistentDataContainer): ExtendedPersistentDataContainer {
return when (pdc) { return when (pdc) {

View File

@@ -0,0 +1,11 @@
package com.willfp.eco.internal.spigot.proxy.v1_18_R1
import com.willfp.eco.internal.spigot.proxy.SyncCommandsProxy
import org.bukkit.Bukkit
import org.bukkit.craftbukkit.v1_18_R1.CraftServer
class SyncCommands : SyncCommandsProxy {
override fun syncCommands() {
(Bukkit.getServer() as CraftServer).syncCommands()
}
}

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.core.data.ExtendedPersistentDataContainer
import com.willfp.eco.internal.spigot.proxy.ExtendedPersistentDataContainerFactoryProxy import com.willfp.eco.internal.spigot.proxy.ExtendedPersistentDataContainerFactoryProxy
import net.minecraft.nbt.Tag 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.CraftPersistentDataContainer
import org.bukkit.craftbukkit.v1_18_R2.persistence.CraftPersistentDataTypeRegistry import org.bukkit.craftbukkit.v1_18_R2.persistence.CraftPersistentDataTypeRegistry
import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataContainer import org.bukkit.persistence.PersistentDataContainer
import org.bukkit.persistence.PersistentDataType import org.bukkit.persistence.PersistentDataType
class ExtendedPersistentDataContainerFactory: ExtendedPersistentDataContainerFactoryProxy { class ExtendedPersistentDataContainerFactory : ExtendedPersistentDataContainerFactoryProxy {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
private val registry: CraftPersistentDataTypeRegistry = private val registry: CraftPersistentDataTypeRegistry
CraftPersistentDataContainer::class.java.getDeclaredField("registry")
.apply { isAccessible = true }.get(null) as 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 { override fun adapt(pdc: PersistentDataContainer): ExtendedPersistentDataContainer {
return when (pdc) { return when (pdc) {
@@ -34,7 +46,8 @@ class ExtendedPersistentDataContainerFactory: ExtendedPersistentDataContainerFac
.apply { isAccessible = true }.get(handle) as MutableMap<String, Tag> .apply { isAccessible = true }.get(handle) as MutableMap<String, Tag>
override fun <T : Any, Z : Any> set(key: String, dataType: PersistentDataType<T, Z>, value: Z) { 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 { override fun <T : Any, Z : Any> has(key: String, dataType: PersistentDataType<T, Z>): Boolean {

View File

@@ -0,0 +1,11 @@
package com.willfp.eco.internal.spigot.proxy.v1_18_R2
import com.willfp.eco.internal.spigot.proxy.SyncCommandsProxy
import org.bukkit.Bukkit
import org.bukkit.craftbukkit.v1_18_R2.CraftServer
class SyncCommands : SyncCommandsProxy {
override fun syncCommands() {
(Bukkit.getServer() as CraftServer).syncCommands()
}
}

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.core.data.ExtendedPersistentDataContainer
import com.willfp.eco.internal.spigot.proxy.ExtendedPersistentDataContainerFactoryProxy import com.willfp.eco.internal.spigot.proxy.ExtendedPersistentDataContainerFactoryProxy
import net.minecraft.nbt.Tag 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.CraftPersistentDataContainer
import org.bukkit.craftbukkit.v1_19_R1.persistence.CraftPersistentDataTypeRegistry import org.bukkit.craftbukkit.v1_19_R1.persistence.CraftPersistentDataTypeRegistry
import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataContainer import org.bukkit.persistence.PersistentDataContainer
import org.bukkit.persistence.PersistentDataType import org.bukkit.persistence.PersistentDataType
class ExtendedPersistentDataContainerFactory: ExtendedPersistentDataContainerFactoryProxy { class ExtendedPersistentDataContainerFactory : ExtendedPersistentDataContainerFactoryProxy {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
private val registry: CraftPersistentDataTypeRegistry = private val registry: CraftPersistentDataTypeRegistry
CraftPersistentDataContainer::class.java.getDeclaredField("registry")
.apply { isAccessible = true }.get(null) as 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 { override fun adapt(pdc: PersistentDataContainer): ExtendedPersistentDataContainer {
return when (pdc) { return when (pdc) {
is CraftPersistentDataContainer -> EcoPersistentDataContainer(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> .apply { isAccessible = true }.get(handle) as MutableMap<String, Tag>
override fun <T : Any, Z : Any> set(key: String, dataType: PersistentDataType<T, Z>, value: Z) { 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 { override fun <T : Any, Z : Any> has(key: String, dataType: PersistentDataType<T, Z>): Boolean {

View File

@@ -0,0 +1,11 @@
package com.willfp.eco.internal.spigot.proxy.v1_19_R1
import com.willfp.eco.internal.spigot.proxy.SyncCommandsProxy
import org.bukkit.Bukkit
import org.bukkit.craftbukkit.v1_19_R1.CraftServer
class SyncCommands : SyncCommandsProxy {
override fun syncCommands() {
(Bukkit.getServer() as CraftServer).syncCommands()
}
}

View File

@@ -4,7 +4,6 @@ import com.willfp.eco.core.Eco
import com.willfp.eco.core.EcoPlugin import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.PluginLike import com.willfp.eco.core.PluginLike
import com.willfp.eco.core.PluginProps import com.willfp.eco.core.PluginProps
import com.willfp.eco.core.Prerequisite
import com.willfp.eco.core.config.ConfigType import com.willfp.eco.core.config.ConfigType
import com.willfp.eco.core.config.interfaces.Config import com.willfp.eco.core.config.interfaces.Config
import com.willfp.eco.core.data.keys.PersistentDataKey import com.willfp.eco.core.data.keys.PersistentDataKey
@@ -12,8 +11,7 @@ import com.willfp.eco.core.gui.menu.Menu
import com.willfp.eco.core.gui.menu.MenuType import com.willfp.eco.core.gui.menu.MenuType
import com.willfp.eco.core.gui.slot.functional.SlotProvider import com.willfp.eco.core.gui.slot.functional.SlotProvider
import com.willfp.eco.core.items.Items import com.willfp.eco.core.items.Items
import com.willfp.eco.core.placeholder.AdditionalPlayer import com.willfp.eco.core.math.MathContext
import com.willfp.eco.core.placeholder.PlaceholderInjectable
import com.willfp.eco.internal.EcoPropsParser import com.willfp.eco.internal.EcoPropsParser
import com.willfp.eco.internal.config.EcoConfigHandler import com.willfp.eco.internal.config.EcoConfigHandler
import com.willfp.eco.internal.config.EcoConfigSection import com.willfp.eco.internal.config.EcoConfigSection
@@ -34,13 +32,13 @@ import com.willfp.eco.internal.gui.MergedStateMenu
import com.willfp.eco.internal.gui.menu.EcoMenuBuilder import com.willfp.eco.internal.gui.menu.EcoMenuBuilder
import com.willfp.eco.internal.gui.menu.renderedInventory import com.willfp.eco.internal.gui.menu.renderedInventory
import com.willfp.eco.internal.gui.slot.EcoSlotBuilder 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.logging.EcoLogger
import com.willfp.eco.internal.proxy.EcoProxyFactory import com.willfp.eco.internal.proxy.EcoProxyFactory
import com.willfp.eco.internal.scheduling.EcoScheduler import com.willfp.eco.internal.scheduling.EcoScheduler
import com.willfp.eco.internal.spigot.data.DataYml 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.KeyRegistry
import com.willfp.eco.internal.spigot.data.ProfileHandler
import com.willfp.eco.internal.spigot.data.storage.HandlerType import com.willfp.eco.internal.spigot.data.storage.HandlerType
import com.willfp.eco.internal.spigot.integrations.bstats.MetricHandler import com.willfp.eco.internal.spigot.integrations.bstats.MetricHandler
import com.willfp.eco.internal.spigot.math.evaluateExpression import com.willfp.eco.internal.spigot.math.evaluateExpression
@@ -52,8 +50,8 @@ import com.willfp.eco.internal.spigot.proxy.FastItemStackFactoryProxy
import com.willfp.eco.internal.spigot.proxy.MiniMessageTranslatorProxy import com.willfp.eco.internal.spigot.proxy.MiniMessageTranslatorProxy
import com.willfp.eco.internal.spigot.proxy.SNBTConverterProxy import com.willfp.eco.internal.spigot.proxy.SNBTConverterProxy
import com.willfp.eco.internal.spigot.proxy.SkullProxy import com.willfp.eco.internal.spigot.proxy.SkullProxy
import com.willfp.eco.internal.spigot.proxy.SyncCommandsProxy
import com.willfp.eco.internal.spigot.proxy.TPSProxy import com.willfp.eco.internal.spigot.proxy.TPSProxy
import net.kyori.adventure.platform.bukkit.BukkitAudiences
import org.bukkit.Location import org.bukkit.Location
import org.bukkit.NamespacedKey import org.bukkit.NamespacedKey
import org.bukkit.configuration.ConfigurationSection import org.bukkit.configuration.ConfigurationSection
@@ -72,7 +70,7 @@ private val loadedEcoPlugins = mutableMapOf<String, EcoPlugin>()
class EcoImpl : EcoSpigotPlugin(), Eco { class EcoImpl : EcoSpigotPlugin(), Eco {
override val dataYml = DataYml(this) override val dataYml = DataYml(this)
override val profileHandler = EcoProfileHandler( override val profileHandler = ProfileHandler(
HandlerType.valueOf(this.configYml.getString("data-handler").uppercase()), HandlerType.valueOf(this.configYml.getString("data-handler").uppercase()),
this this
) )
@@ -81,10 +79,6 @@ class EcoImpl : EcoSpigotPlugin(), Eco {
getProxy(CommonsInitializerProxy::class.java).init() getProxy(CommonsInitializerProxy::class.java).init()
} }
private var adventure: BukkitAudiences? = if (!Prerequisite.HAS_PAPER.isMet) {
BukkitAudiences.create(this)
} else null
@Suppress("RedundantNullableReturnType") @Suppress("RedundantNullableReturnType")
private val keyFactory: InternalNamespacedKeyFactory? = private val keyFactory: InternalNamespacedKeyFactory? =
if (this.configYml.getBool("use-safer-namespacedkey-creation")) if (this.configYml.getBool("use-safer-namespacedkey-creation"))
@@ -114,8 +108,9 @@ class EcoImpl : EcoSpigotPlugin(), Eco {
override fun createLogger(plugin: EcoPlugin) = override fun createLogger(plugin: EcoPlugin) =
EcoLogger(plugin) EcoLogger(plugin)
override fun createPAPIIntegration(plugin: EcoPlugin) = override fun createPAPIIntegration(plugin: EcoPlugin) {
PlaceholderIntegrationPAPI(plugin) PAPIExpansion(plugin)
}
override fun getEcoPlugin(): EcoPlugin = override fun getEcoPlugin(): EcoPlugin =
this this
@@ -174,9 +169,6 @@ class EcoImpl : EcoSpigotPlugin(), Eco {
override fun createDropQueue(player: Player) = if (this.configYml.getBool("use-fast-collated-drops")) override fun createDropQueue(player: Player) = if (this.configYml.getBool("use-fast-collated-drops"))
EcoFastCollatedDropQueue(player) else EcoDropQueue(player) EcoFastCollatedDropQueue(player) else EcoDropQueue(player)
override fun getPersistentDataKeyFrom(namespacedKey: NamespacedKey) =
KeyRegistry.getKeyFrom(namespacedKey)
override fun getRegisteredPersistentDataKeys() = override fun getRegisteredPersistentDataKeys() =
KeyRegistry.getRegisteredKeys() KeyRegistry.getRegisteredKeys()
@@ -235,7 +227,7 @@ class EcoImpl : EcoSpigotPlugin(), Eco {
MetricHandler.createMetrics(plugin) MetricHandler.createMetrics(plugin)
override fun getAdventure() = override fun getAdventure() =
adventure bukkitAudiences
override fun getServerProfile() = override fun getServerProfile() =
profileHandler.loadServerProfile() profileHandler.loadServerProfile()
@@ -243,12 +235,6 @@ class EcoImpl : EcoSpigotPlugin(), Eco {
override fun loadPlayerProfile(uuid: UUID) = override fun loadPlayerProfile(uuid: UUID) =
profileHandler.load(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) = override fun unloadPlayerProfile(uuid: UUID) =
profileHandler.unloadPlayer(uuid) profileHandler.unloadPlayer(uuid)
@@ -292,13 +278,12 @@ class EcoImpl : EcoSpigotPlugin(), Eco {
override fun getTPS() = override fun getTPS() =
getProxy(TPSProxy::class.java).getTPS() getProxy(TPSProxy::class.java).getTPS()
override fun evaluate( override fun evaluate(expression: String, context: MathContext) =
expression: String, evaluateExpression(expression, context)
player: Player?,
injectable: PlaceholderInjectable,
additionalPlayers: MutableCollection<AdditionalPlayer>
) = evaluateExpression(expression, player, injectable, additionalPlayers)
override fun getOpenMenu(player: Player) = override fun getOpenMenu(player: Player) =
player.renderedInventory?.menu player.renderedInventory?.menu
override fun syncCommands() =
this.getProxy(SyncCommandsProxy::class.java).syncCommands()
} }

View File

@@ -14,8 +14,11 @@ import com.willfp.eco.core.integrations.customitems.CustomItemsManager
import com.willfp.eco.core.integrations.economy.EconomyManager import com.willfp.eco.core.integrations.economy.EconomyManager
import com.willfp.eco.core.integrations.hologram.HologramManager import com.willfp.eco.core.integrations.hologram.HologramManager
import com.willfp.eco.core.integrations.mcmmo.McmmoManager import com.willfp.eco.core.integrations.mcmmo.McmmoManager
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager
import com.willfp.eco.core.integrations.shop.ShopManager import com.willfp.eco.core.integrations.shop.ShopManager
import com.willfp.eco.core.items.Items import com.willfp.eco.core.items.Items
import com.willfp.eco.core.particle.Particles
import com.willfp.eco.core.price.Prices
import com.willfp.eco.internal.entities.EntityArgParserAdult import com.willfp.eco.internal.entities.EntityArgParserAdult
import com.willfp.eco.internal.entities.EntityArgParserAttackDamage import com.willfp.eco.internal.entities.EntityArgParserAttackDamage
import com.willfp.eco.internal.entities.EntityArgParserAttackSpeed import com.willfp.eco.internal.entities.EntityArgParserAttackSpeed
@@ -44,11 +47,15 @@ import com.willfp.eco.internal.items.ArgParserTexture
import com.willfp.eco.internal.items.ArgParserUnbreakable import com.willfp.eco.internal.items.ArgParserUnbreakable
import com.willfp.eco.internal.lookup.SegmentParserGroup import com.willfp.eco.internal.lookup.SegmentParserGroup
import com.willfp.eco.internal.lookup.SegmentParserUseIfPresent import com.willfp.eco.internal.lookup.SegmentParserUseIfPresent
import com.willfp.eco.internal.particle.ParticleFactoryRGB
import com.willfp.eco.internal.price.PriceFactoryEconomy
import com.willfp.eco.internal.price.PriceFactoryXP
import com.willfp.eco.internal.price.PriceFactoryXPLevels
import com.willfp.eco.internal.spigot.arrows.ArrowDataListener import com.willfp.eco.internal.spigot.arrows.ArrowDataListener
import com.willfp.eco.internal.spigot.data.DataListener import com.willfp.eco.internal.spigot.data.DataListener
import com.willfp.eco.internal.spigot.data.DataYml import com.willfp.eco.internal.spigot.data.DataYml
import com.willfp.eco.internal.spigot.data.EcoProfileHandler
import com.willfp.eco.internal.spigot.data.PlayerBlockListener import com.willfp.eco.internal.spigot.data.PlayerBlockListener
import com.willfp.eco.internal.spigot.data.ProfileHandler
import com.willfp.eco.internal.spigot.data.storage.ProfileSaver import com.willfp.eco.internal.spigot.data.storage.ProfileSaver
import com.willfp.eco.internal.spigot.display.PacketAutoRecipe import com.willfp.eco.internal.spigot.display.PacketAutoRecipe
import com.willfp.eco.internal.spigot.display.PacketChat import com.willfp.eco.internal.spigot.display.PacketChat
@@ -86,6 +93,7 @@ import com.willfp.eco.internal.spigot.integrations.antigrief.AntigriefGriefPreve
import com.willfp.eco.internal.spigot.integrations.antigrief.AntigriefIridiumSkyblock import com.willfp.eco.internal.spigot.integrations.antigrief.AntigriefIridiumSkyblock
import com.willfp.eco.internal.spigot.integrations.antigrief.AntigriefKingdoms import com.willfp.eco.internal.spigot.integrations.antigrief.AntigriefKingdoms
import com.willfp.eco.internal.spigot.integrations.antigrief.AntigriefLands import com.willfp.eco.internal.spigot.integrations.antigrief.AntigriefLands
import com.willfp.eco.internal.spigot.integrations.antigrief.AntigriefPvPManager
import com.willfp.eco.internal.spigot.integrations.antigrief.AntigriefRPGHorses import com.willfp.eco.internal.spigot.integrations.antigrief.AntigriefRPGHorses
import com.willfp.eco.internal.spigot.integrations.antigrief.AntigriefSuperiorSkyblock2 import com.willfp.eco.internal.spigot.integrations.antigrief.AntigriefSuperiorSkyblock2
import com.willfp.eco.internal.spigot.integrations.antigrief.AntigriefTowny import com.willfp.eco.internal.spigot.integrations.antigrief.AntigriefTowny
@@ -106,11 +114,11 @@ import com.willfp.eco.internal.spigot.integrations.hologram.HologramDecentHologr
import com.willfp.eco.internal.spigot.integrations.hologram.HologramHolographicDisplays import com.willfp.eco.internal.spigot.integrations.hologram.HologramHolographicDisplays
import com.willfp.eco.internal.spigot.integrations.mcmmo.McmmoIntegrationImpl import com.willfp.eco.internal.spigot.integrations.mcmmo.McmmoIntegrationImpl
import com.willfp.eco.internal.spigot.integrations.multiverseinventories.MultiverseInventoriesIntegration import com.willfp.eco.internal.spigot.integrations.multiverseinventories.MultiverseInventoriesIntegration
import com.willfp.eco.internal.spigot.integrations.placeholder.PlaceholderIntegrationPAPI
import com.willfp.eco.internal.spigot.integrations.shop.ShopDeluxeSellwands import com.willfp.eco.internal.spigot.integrations.shop.ShopDeluxeSellwands
import com.willfp.eco.internal.spigot.integrations.shop.ShopEconomyShopGUI import com.willfp.eco.internal.spigot.integrations.shop.ShopEconomyShopGUI
import com.willfp.eco.internal.spigot.integrations.shop.ShopShopGuiPlus import com.willfp.eco.internal.spigot.integrations.shop.ShopShopGuiPlus
import com.willfp.eco.internal.spigot.integrations.shop.ShopZShop import com.willfp.eco.internal.spigot.integrations.shop.ShopZShop
import com.willfp.eco.internal.spigot.player.PlayerHealthFixer
import com.willfp.eco.internal.spigot.proxy.FastItemStackFactoryProxy import com.willfp.eco.internal.spigot.proxy.FastItemStackFactoryProxy
import com.willfp.eco.internal.spigot.recipes.CraftingRecipeListener import com.willfp.eco.internal.spigot.recipes.CraftingRecipeListener
import com.willfp.eco.internal.spigot.recipes.StackedRecipeListener import com.willfp.eco.internal.spigot.recipes.StackedRecipeListener
@@ -118,6 +126,7 @@ import com.willfp.eco.internal.spigot.recipes.listeners.ComplexInComplex
import com.willfp.eco.internal.spigot.recipes.listeners.ComplexInVanilla import com.willfp.eco.internal.spigot.recipes.listeners.ComplexInVanilla
import com.willfp.eco.internal.spigot.recipes.stackhandlers.ShapedCraftingRecipeStackHandler import com.willfp.eco.internal.spigot.recipes.stackhandlers.ShapedCraftingRecipeStackHandler
import com.willfp.eco.internal.spigot.recipes.stackhandlers.ShapelessCraftingRecipeStackHandler import com.willfp.eco.internal.spigot.recipes.stackhandlers.ShapelessCraftingRecipeStackHandler
import net.kyori.adventure.platform.bukkit.BukkitAudiences
import net.milkbowl.vault.economy.Economy import net.milkbowl.vault.economy.Economy
import org.bukkit.Bukkit import org.bukkit.Bukkit
import org.bukkit.Material import org.bukkit.Material
@@ -126,7 +135,8 @@ import org.bukkit.inventory.ItemStack
abstract class EcoSpigotPlugin : EcoPlugin() { abstract class EcoSpigotPlugin : EcoPlugin() {
abstract val dataYml: DataYml abstract val dataYml: DataYml
protected abstract val profileHandler: EcoProfileHandler protected abstract val profileHandler: ProfileHandler
protected var bukkitAudiences: BukkitAudiences? = null
init { init {
Items.registerArgParser(ArgParserEnchantment) Items.registerArgParser(ArgParserEnchantment)
@@ -157,6 +167,12 @@ abstract class EcoSpigotPlugin : EcoPlugin() {
Entities.registerArgParser(EntityArgParserSilent) Entities.registerArgParser(EntityArgParserSilent)
Entities.registerArgParser(EntityArgParserEquipment) Entities.registerArgParser(EntityArgParserEquipment)
Prices.registerPriceFactory(PriceFactoryEconomy)
Prices.registerPriceFactory(PriceFactoryXPLevels)
Prices.registerPriceFactory(PriceFactoryXP)
Particles.registerParticleFactory(ParticleFactoryRGB)
CraftingRecipeListener.registerListener(ComplexInComplex) CraftingRecipeListener.registerListener(ComplexInComplex)
CraftingRecipeListener.registerListener(ComplexInVanilla) CraftingRecipeListener.registerListener(ComplexInVanilla)
@@ -205,6 +221,11 @@ abstract class EcoSpigotPlugin : EcoPlugin() {
// Preload categorized persistent data keys // Preload categorized persistent data keys
profileHandler.initialize() profileHandler.initialize()
// Init adventure
if (!Prerequisite.HAS_PAPER.isMet) {
bukkitAudiences = BukkitAudiences.create(this)
}
} }
override fun handleDisable() { override fun handleDisable() {
@@ -263,6 +284,7 @@ abstract class EcoSpigotPlugin : EcoPlugin() {
AntigriefManager.register(AntigriefCombatLogXV11()) AntigriefManager.register(AntigriefCombatLogXV11())
} }
}, },
IntegrationLoader("PvPManager") { AntigriefManager.register(AntigriefPvPManager()) },
// Anticheat // Anticheat
IntegrationLoader("AAC5") { AnticheatManager.register(AnticheatAAC()) }, IntegrationLoader("AAC5") { AnticheatManager.register(AnticheatAAC()) },
@@ -311,6 +333,9 @@ abstract class EcoSpigotPlugin : EcoPlugin() {
} }
}, },
// Placeholder
IntegrationLoader("PlaceholderAPI") { PlaceholderManager.addIntegration(PlaceholderIntegrationPAPI()) },
// Misc // Misc
IntegrationLoader("mcMMO") { McmmoManager.register(McmmoIntegrationImpl()) }, IntegrationLoader("mcMMO") { McmmoManager.register(McmmoIntegrationImpl()) },
IntegrationLoader("Multiverse-Inventories") { IntegrationLoader("Multiverse-Inventories") {
@@ -345,7 +370,6 @@ abstract class EcoSpigotPlugin : EcoPlugin() {
ArmorChangeEventListeners(this), ArmorChangeEventListeners(this),
DataListener(this), DataListener(this),
PlayerBlockListener(this), PlayerBlockListener(this),
PlayerHealthFixer(this),
ServerLocking ServerLocking
) )

View File

@@ -48,8 +48,4 @@ object KeyRegistry {
else -> throw NullPointerException("Null value found!") else -> throw NullPointerException("Null value found!")
} }
} }
fun getKeyFrom(namespacedKey: NamespacedKey): PersistentDataKey<*>? {
return registry[namespacedKey]
}
} }

View File

@@ -18,7 +18,7 @@ import java.util.UUID
val serverProfileUUID = UUID(0, 0) val serverProfileUUID = UUID(0, 0)
class EcoProfileHandler( class ProfileHandler(
private val type: HandlerType, private val type: HandlerType,
private val plugin: EcoSpigotPlugin private val plugin: EcoSpigotPlugin
) { ) {

View File

@@ -7,7 +7,7 @@ import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.data.keys.PersistentDataKey import com.willfp.eco.core.data.keys.PersistentDataKey
import com.willfp.eco.core.data.keys.PersistentDataKeyType import com.willfp.eco.core.data.keys.PersistentDataKeyType
import com.willfp.eco.internal.spigot.EcoSpigotPlugin import com.willfp.eco.internal.spigot.EcoSpigotPlugin
import com.willfp.eco.internal.spigot.data.EcoProfileHandler import com.willfp.eco.internal.spigot.data.ProfileHandler
import com.willfp.eco.internal.spigot.data.serverProfileUUID import com.willfp.eco.internal.spigot.data.serverProfileUUID
import com.zaxxer.hikari.HikariConfig import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource import com.zaxxer.hikari.HikariDataSource
@@ -49,8 +49,9 @@ the worst bodge I've shipped in production.
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
class LegacyMySQLDataHandler( class LegacyMySQLDataHandler(
plugin: EcoSpigotPlugin, plugin: EcoSpigotPlugin,
handler: EcoProfileHandler handler: ProfileHandler
) : DataHandler(HandlerType.LEGACY_MYSQL) { ) : DataHandler(HandlerType.LEGACY_MYSQL) {
private val database: Database
private val playerHandler: ImplementedMySQLHandler private val playerHandler: ImplementedMySQLHandler
private val serverHandler: ImplementedMySQLHandler private val serverHandler: ImplementedMySQLHandler
@@ -65,7 +66,7 @@ class LegacyMySQLDataHandler(
plugin.configYml.getString("mysql.database") plugin.configYml.getString("mysql.database")
config.maximumPoolSize = plugin.configYml.getInt("mysql.connections") config.maximumPoolSize = plugin.configYml.getInt("mysql.connections")
Database.connect(HikariDataSource(config)) database = Database.connect(HikariDataSource(config))
playerHandler = ImplementedMySQLHandler( playerHandler = ImplementedMySQLHandler(
handler, handler,
@@ -110,180 +111,180 @@ class LegacyMySQLDataHandler(
playerHandler.initialize() playerHandler.initialize()
serverHandler.initialize() serverHandler.initialize()
} }
}
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
private class ImplementedMySQLHandler( private inner class ImplementedMySQLHandler(
private val handler: EcoProfileHandler, private val handler: ProfileHandler,
private val table: UUIDTable, private val table: UUIDTable,
private val plugin: EcoPlugin private val plugin: EcoPlugin
) { ) {
private val rows = Caffeine.newBuilder() private val rows = Caffeine.newBuilder()
.expireAfterWrite(3, TimeUnit.SECONDS) .expireAfterWrite(3, TimeUnit.SECONDS)
.build<UUID, ResultRow>() .build<UUID, ResultRow>()
private val threadFactory = ThreadFactoryBuilder().setNameFormat("eco-mysql-thread-%d").build() private val threadFactory = ThreadFactoryBuilder().setNameFormat("eco-legacy-mysql-thread-%d").build()
private val executor = Executors.newFixedThreadPool(plugin.configYml.getInt("mysql.threads"), threadFactory) private val executor = Executors.newFixedThreadPool(plugin.configYml.getInt("mysql.threads"), threadFactory)
val registeredKeys = mutableSetOf<PersistentDataKey<*>>() val registeredKeys = mutableSetOf<PersistentDataKey<*>>()
init { init {
transaction { transaction(database) {
SchemaUtils.create(table) SchemaUtils.create(table)
}
} }
}
fun initialize() { fun initialize() {
transaction { transaction(database) {
SchemaUtils.createMissingTablesAndColumns(table, withLogs = false) SchemaUtils.createMissingTablesAndColumns(table, withLogs = false)
}
} }
}
fun ensureKeyRegistration(key: PersistentDataKey<*>) { fun ensureKeyRegistration(key: PersistentDataKey<*>) {
if (table.columns.any { it.name == key.key.toString() }) { if (table.columns.any { it.name == key.key.toString() }) {
registeredKeys.add(key)
return
}
registerColumn(key)
registeredKeys.add(key) registeredKeys.add(key)
return
} }
registerColumn(key) fun <T : Any> write(uuid: UUID, key: PersistentDataKey<T>, value: Any) {
registeredKeys.add(key) getRow(uuid)
} doWrite(uuid, key, key.type.constrainSQLTypes(value))
fun <T : Any> write(uuid: UUID, key: PersistentDataKey<T>, value: Any) {
getRow(uuid)
doWrite(uuid, key, key.type.constrainSQLTypes(value))
}
private fun doWrite(uuid: UUID, key: PersistentDataKey<*>, constrainedValue: Any) {
val column: Column<Any> = getColumn(key) as Column<Any>
executor.submit {
transaction {
table.update({ table.id eq uuid }) {
it[column] = constrainedValue
}
}
}
}
fun saveKeysForRow(uuid: UUID, keys: Set<PersistentDataKey<*>>) {
saveRow(uuid, keys)
}
private fun saveRow(uuid: UUID, keys: Set<PersistentDataKey<*>>) {
val profile = handler.loadGenericProfile(uuid)
executor.submit {
transaction {
getRow(uuid)
for (key in keys) {
doWrite(uuid, key, key.type.constrainSQLTypes(profile.read(key)))
}
}
}
}
fun <T> read(uuid: UUID, key: PersistentDataKey<T>): T? {
val doRead = Callable<T?> {
transaction {
val row = getRow(uuid)
val column = getColumn(key)
val raw = row[column]
key.type.fromConstrained(raw)
}
} }
ensureKeyRegistration(key) // DON'T DELETE THIS LINE! I know it's covered in getColumn, but I need to do it here as well. private fun doWrite(uuid: UUID, key: PersistentDataKey<*>, constrainedValue: Any) {
val column: Column<Any> = getColumn(key) as Column<Any>
doRead.call() executor.submit {
transaction(database) {
return if (Eco.get().ecoPlugin.configYml.getBool("mysql.async-reads")) { table.update({ table.id eq uuid }) {
executor.submit(doRead).get() it[column] = constrainedValue
} else {
doRead.call()
}
}
private fun <T> registerColumn(key: PersistentDataKey<T>) {
try {
transaction {
try {
table.apply {
if (table.columns.any { it.name == key.key.toString() }) {
return@apply
}
when (key.type) {
PersistentDataKeyType.INT -> registerColumn<Int>(key.key.toString(), IntegerColumnType())
.default(key.defaultValue as Int)
PersistentDataKeyType.DOUBLE -> registerColumn<Double>(
key.key.toString(),
DoubleColumnType()
).default(key.defaultValue as Double)
PersistentDataKeyType.BOOLEAN -> registerColumn<Boolean>(
key.key.toString(),
BooleanColumnType()
).default(key.defaultValue as Boolean)
PersistentDataKeyType.STRING -> registerColumn<String>(
key.key.toString(),
VarCharColumnType(512)
).default(key.defaultValue as String)
PersistentDataKeyType.STRING_LIST -> registerColumn<String>(
key.key.toString(),
VarCharColumnType(8192)
).default(PersistentDataKeyType.STRING_LIST.constrainSQLTypes(key.defaultValue as List<String>) as String)
PersistentDataKeyType.CONFIG -> throw IllegalArgumentException(
"Config Persistent Data Keys are not supported by the legacy MySQL handler!"
)
else -> throw NullPointerException("Null value found!")
}
} }
SchemaUtils.createMissingTablesAndColumns(table, withLogs = false)
} catch (e: Exception) {
plugin.logger.info("MySQL Error 1!")
e.printStackTrace()
// What's that? Two enormous exception catches? That's right! This code sucks.
} }
} }
} catch (e: Exception) {
plugin.logger.info("MySQL Error 2!")
e.printStackTrace()
// It might fail. Who cares? This is legacy.
} }
}
private fun getColumn(key: PersistentDataKey<*>): Column<*> { fun saveKeysForRow(uuid: UUID, keys: Set<PersistentDataKey<*>>) {
ensureKeyRegistration(key) saveRow(uuid, keys)
}
val name = key.key.toString() private fun saveRow(uuid: UUID, keys: Set<PersistentDataKey<*>>) {
val profile = handler.loadGenericProfile(uuid)
return table.columns.first { it.name == name } executor.submit {
} transaction(database) {
getRow(uuid)
private fun getRow(uuid: UUID): ResultRow { for (key in keys) {
fun select(uuid: UUID): ResultRow? { doWrite(uuid, key, key.type.constrainSQLTypes(profile.read(key)))
return transaction { }
table.select { table.id eq uuid }.limit(1).singleOrNull() }
} }
} }
return rows.get(uuid) { fun <T> read(uuid: UUID, key: PersistentDataKey<T>): T? {
val row = select(uuid) val doRead = Callable<T?> {
transaction(database) {
val row = getRow(uuid)
val column = getColumn(key)
val raw = row[column]
key.type.fromConstrained(raw)
}
}
return@get if (row != null) { ensureKeyRegistration(key) // DON'T DELETE THIS LINE! I know it's covered in getColumn, but I need to do it here as well.
row
doRead.call()
return if (Eco.get().ecoPlugin.configYml.getBool("mysql.async-reads")) {
executor.submit(doRead).get()
} else { } else {
transaction { doRead.call()
table.insert { it[id] = uuid } }
}
private fun <T> registerColumn(key: PersistentDataKey<T>) {
try {
transaction(database) {
try {
table.apply {
if (table.columns.any { it.name == key.key.toString() }) {
return@apply
}
when (key.type) {
PersistentDataKeyType.INT -> registerColumn<Int>(key.key.toString(), IntegerColumnType())
.default(key.defaultValue as Int)
PersistentDataKeyType.DOUBLE -> registerColumn<Double>(
key.key.toString(),
DoubleColumnType()
).default(key.defaultValue as Double)
PersistentDataKeyType.BOOLEAN -> registerColumn<Boolean>(
key.key.toString(),
BooleanColumnType()
).default(key.defaultValue as Boolean)
PersistentDataKeyType.STRING -> registerColumn<String>(
key.key.toString(),
VarCharColumnType(512)
).default(key.defaultValue as String)
PersistentDataKeyType.STRING_LIST -> registerColumn<String>(
key.key.toString(),
VarCharColumnType(8192)
).default(PersistentDataKeyType.STRING_LIST.constrainSQLTypes(key.defaultValue as List<String>) as String)
PersistentDataKeyType.CONFIG -> throw IllegalArgumentException(
"Config Persistent Data Keys are not supported by the legacy MySQL handler!"
)
else -> throw NullPointerException("Null value found!")
}
}
SchemaUtils.createMissingTablesAndColumns(table, withLogs = false)
} catch (e: Exception) {
plugin.logger.info("MySQL Error 1!")
e.printStackTrace()
// What's that? Two enormous exception catches? That's right! This code sucks.
}
}
} catch (e: Exception) {
plugin.logger.info("MySQL Error 2!")
e.printStackTrace()
// It might fail. Who cares? This is legacy.
}
}
private fun getColumn(key: PersistentDataKey<*>): Column<*> {
ensureKeyRegistration(key)
val name = key.key.toString()
return table.columns.first { it.name == name }
}
private fun getRow(uuid: UUID): ResultRow {
fun select(uuid: UUID): ResultRow? {
return transaction(database) {
table.select { table.id eq uuid }.limit(1).singleOrNull()
}
}
return rows.get(uuid) {
val row = select(uuid)
return@get if (row != null) {
row
} else {
transaction(database) {
table.insert { it[id] = uuid }
}
select(uuid)
} }
select(uuid)
} }
} }
} }

View File

@@ -3,7 +3,7 @@ package com.willfp.eco.internal.spigot.data.storage
import com.willfp.eco.core.data.Profile import com.willfp.eco.core.data.Profile
import com.willfp.eco.core.data.keys.PersistentDataKey import com.willfp.eco.core.data.keys.PersistentDataKey
import com.willfp.eco.internal.spigot.EcoSpigotPlugin import com.willfp.eco.internal.spigot.EcoSpigotPlugin
import com.willfp.eco.internal.spigot.data.EcoProfileHandler import com.willfp.eco.internal.spigot.data.ProfileHandler
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -20,7 +20,7 @@ import java.util.UUID
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
class MongoDataHandler( class MongoDataHandler(
plugin: EcoSpigotPlugin, plugin: EcoSpigotPlugin,
private val handler: EcoProfileHandler private val handler: ProfileHandler
) : DataHandler(HandlerType.MONGO) { ) : DataHandler(HandlerType.MONGO) {
private val client: CoroutineClient private val client: CoroutineClient
private val collection: CoroutineCollection<UUIDProfile> private val collection: CoroutineCollection<UUIDProfile>

View File

@@ -3,12 +3,12 @@ package com.willfp.eco.internal.spigot.data.storage
import com.github.benmanes.caffeine.cache.Caffeine import com.github.benmanes.caffeine.cache.Caffeine
import com.google.common.util.concurrent.ThreadFactoryBuilder import com.google.common.util.concurrent.ThreadFactoryBuilder
import com.willfp.eco.core.config.ConfigType import com.willfp.eco.core.config.ConfigType
import com.willfp.eco.core.config.TransientConfig
import com.willfp.eco.core.config.interfaces.Config import com.willfp.eco.core.config.interfaces.Config
import com.willfp.eco.core.config.readConfig
import com.willfp.eco.core.data.keys.PersistentDataKey import com.willfp.eco.core.data.keys.PersistentDataKey
import com.willfp.eco.core.data.keys.PersistentDataKeyType import com.willfp.eco.core.data.keys.PersistentDataKeyType
import com.willfp.eco.internal.spigot.EcoSpigotPlugin import com.willfp.eco.internal.spigot.EcoSpigotPlugin
import com.willfp.eco.internal.spigot.data.EcoProfileHandler import com.willfp.eco.internal.spigot.data.ProfileHandler
import com.zaxxer.hikari.HikariConfig import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource import com.zaxxer.hikari.HikariDataSource
import org.jetbrains.exposed.dao.id.UUIDTable import org.jetbrains.exposed.dao.id.UUIDTable
@@ -35,8 +35,9 @@ Whatever. At least it works.
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
class MySQLDataHandler( class MySQLDataHandler(
private val plugin: EcoSpigotPlugin, private val plugin: EcoSpigotPlugin,
private val handler: EcoProfileHandler private val handler: ProfileHandler
) : DataHandler(HandlerType.MYSQL) { ) : DataHandler(HandlerType.MYSQL) {
private val database: Database
private val table = UUIDTable("eco_data") private val table = UUIDTable("eco_data")
private val rows = Caffeine.newBuilder() private val rows = Caffeine.newBuilder()
@@ -60,9 +61,9 @@ class MySQLDataHandler(
plugin.configYml.getString("mysql.database") plugin.configYml.getString("mysql.database")
config.maximumPoolSize = plugin.configYml.getInt("mysql.connections") config.maximumPoolSize = plugin.configYml.getInt("mysql.connections")
Database.connect(HikariDataSource(config)) database = Database.connect(HikariDataSource(config))
transaction { transaction(database) {
SchemaUtils.create(table) SchemaUtils.create(table)
table.apply { table.apply {
@@ -110,14 +111,14 @@ class MySQLDataHandler(
} }
private fun getData(uuid: UUID): Config { private fun getData(uuid: UUID): Config {
val plaintext = transaction { val plaintext = transaction(database) {
val row = rows.get(uuid) { val row = rows.get(uuid) {
val row = table.select { table.id eq uuid }.limit(1).singleOrNull() val row = table.select { table.id eq uuid }.limit(1).singleOrNull()
if (row != null) { if (row != null) {
row row
} else { } else {
transaction { transaction(database) {
table.insert { table.insert {
it[id] = uuid it[id] = uuid
it[dataColumn] = "{}" it[dataColumn] = "{}"
@@ -130,13 +131,12 @@ class MySQLDataHandler(
row.getOrNull(dataColumn) ?: "{}" row.getOrNull(dataColumn) ?: "{}"
} }
return readConfig(plaintext, ConfigType.JSON)
return TransientConfig(plaintext, ConfigType.JSON)
} }
private fun setData(uuid: UUID, config: Config) { private fun setData(uuid: UUID, config: Config) {
executor.submit { executor.submit {
transaction { transaction(database) {
table.update({ table.id eq uuid }) { table.update({ table.id eq uuid }) {
it[dataColumn] = config.toPlaintext() it[dataColumn] = config.toPlaintext()
} }
@@ -145,7 +145,7 @@ class MySQLDataHandler(
} }
override fun initialize() { override fun initialize() {
transaction { transaction(database) {
SchemaUtils.createMissingTablesAndColumns(table, withLogs = false) SchemaUtils.createMissingTablesAndColumns(table, withLogs = false)
} }
} }

View File

@@ -2,14 +2,16 @@ package com.willfp.eco.internal.spigot.data.storage
import com.willfp.eco.core.EcoPlugin import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.internal.spigot.data.EcoProfile import com.willfp.eco.internal.spigot.data.EcoProfile
import com.willfp.eco.internal.spigot.data.EcoProfileHandler import com.willfp.eco.internal.spigot.data.ProfileHandler
class ProfileSaver( class ProfileSaver(
plugin: EcoPlugin, plugin: EcoPlugin,
handler: EcoProfileHandler handler: ProfileHandler
) { ) {
init { init {
plugin.scheduler.runTimer(1, 1) { val interval = plugin.configYml.getInt("save-interval").toLong()
plugin.scheduler.runTimer(20, interval) {
for ((uuid, set) in EcoProfile.CHANGE_MAP) { for ((uuid, set) in EcoProfile.CHANGE_MAP) {
handler.saveKeysFor(uuid, set) handler.saveKeysFor(uuid, set)
} }

View File

@@ -3,14 +3,14 @@ package com.willfp.eco.internal.spigot.data.storage
import com.willfp.eco.core.data.keys.PersistentDataKey import com.willfp.eco.core.data.keys.PersistentDataKey
import com.willfp.eco.core.data.keys.PersistentDataKeyType import com.willfp.eco.core.data.keys.PersistentDataKeyType
import com.willfp.eco.internal.spigot.EcoSpigotPlugin import com.willfp.eco.internal.spigot.EcoSpigotPlugin
import com.willfp.eco.internal.spigot.data.EcoProfileHandler import com.willfp.eco.internal.spigot.data.ProfileHandler
import org.bukkit.NamespacedKey import org.bukkit.NamespacedKey
import java.util.UUID import java.util.UUID
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
class YamlDataHandler( class YamlDataHandler(
plugin: EcoSpigotPlugin, plugin: EcoSpigotPlugin,
private val handler: EcoProfileHandler private val handler: ProfileHandler
) : DataHandler(HandlerType.YAML) { ) : DataHandler(HandlerType.YAML) {
private val dataYml = plugin.dataYml private val dataYml = plugin.dataYml

View File

@@ -1,5 +1,6 @@
package com.willfp.eco.internal.spigot.display.frame package com.willfp.eco.internal.spigot.display.frame
import com.comphenix.protocol.injector.temporary.TemporaryPlayer
import com.willfp.eco.core.items.HashedItem import com.willfp.eco.core.items.HashedItem
import org.bukkit.entity.Player import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
@@ -32,9 +33,19 @@ private val frames = ConcurrentHashMap<UUID, DisplayFrame>()
var Player.lastDisplayFrame: DisplayFrame var Player.lastDisplayFrame: DisplayFrame
get() { get() {
// ProtocolLib fix
if (this is TemporaryPlayer) {
return DisplayFrame.EMPTY
}
return frames[this.uniqueId] ?: DisplayFrame.EMPTY return frames[this.uniqueId] ?: DisplayFrame.EMPTY
} }
set(value) { set(value) {
// ProtocolLib fix
if (this is TemporaryPlayer) {
return
}
frames[this.uniqueId] = value frames[this.uniqueId] = value
} }

View File

@@ -50,6 +50,14 @@ class GUIListener(private val plugin: EcoPlugin) : Listener {
} }
} }
@EventHandler(
priority = EventPriority.HIGHEST
)
fun handleRender(event: InventoryClickEvent) {
val player = event.whoClicked as? Player ?: return
player.renderActiveMenu()
}
@EventHandler( @EventHandler(
priority = EventPriority.HIGH priority = EventPriority.HIGH
) )
@@ -62,9 +70,7 @@ class GUIListener(private val plugin: EcoPlugin) : Listener {
val (row, column) = MenuUtils.convertSlotToRowColumn(event.slot, menu.columns) val (row, column) = MenuUtils.convertSlotToRowColumn(event.slot, menu.columns)
menu.getSlot(row, column, player, menu).handle(player, event, menu) menu.getSlot(row, column, player).handle(player, event, menu)
plugin.scheduler.run { rendered.render() }
} }
@EventHandler( @EventHandler(
@@ -87,7 +93,7 @@ class GUIListener(private val plugin: EcoPlugin) : Listener {
val (row, column) = MenuUtils.convertSlotToRowColumn(inv.firstEmpty(), menu.columns) val (row, column) = MenuUtils.convertSlotToRowColumn(inv.firstEmpty(), menu.columns)
val slot = menu.getSlot(row, column, player, menu) val slot = menu.getSlot(row, column, player)
if (!slot.isCaptive(player, menu)) { if (!slot.isCaptive(player, menu)) {
event.isCancelled = true event.isCancelled = true
@@ -125,6 +131,27 @@ class GUIListener(private val plugin: EcoPlugin) : Listener {
event.isCancelled = true event.isCancelled = true
} }
@EventHandler(
priority = EventPriority.HIGHEST
)
fun preventMovingHeld(event: InventoryClickEvent) {
val player = event.player
val rendered = player.renderedInventory ?: return
if (rendered.menu.allowsChangingHeldItem()) {
return
}
if (event.clickedInventory !is PlayerInventory) {
return
}
if (event.slot == player.inventory.heldItemSlot) {
event.isCancelled = true
}
}
@EventHandler( @EventHandler(
priority = EventPriority.LOW priority = EventPriority.LOW
) )
@@ -151,7 +178,6 @@ class GUIListener(private val plugin: EcoPlugin) : Listener {
val rendered = player.renderedInventory ?: return val rendered = player.renderedInventory ?: return
if (rendered.menu.allowsChangingHeldItem()) { if (rendered.menu.allowsChangingHeldItem()) {
player.renderActiveMenu()
return return
} }

View File

@@ -0,0 +1,38 @@
package com.willfp.eco.internal.spigot.integrations.antigrief
import com.willfp.eco.core.integrations.antigrief.AntigriefIntegration
import org.bukkit.Location
import org.bukkit.block.Block
import org.bukkit.entity.LivingEntity
import org.bukkit.entity.Player
import me.NoChance.PvPManager.PvPlayer
class AntigriefPvPManager: AntigriefIntegration {
override fun getPluginName(): String {
return "PvPManager"
}
override fun canBreakBlock(player: Player, block: Block): Boolean {
return true
}
override fun canCreateExplosion(player: Player, location: Location): Boolean {
return true
}
override fun canPlaceBlock(player: Player, block: Block): Boolean {
return true
}
override fun canInjure(player: Player, victim: LivingEntity): Boolean {
return when(victim) {
is Player -> {
(PvPlayer.get(victim).isInCombat())}
else -> true
}
}
override fun canPickupItem(player: Player, location: Location): Boolean {
return true
}
}

View File

@@ -11,7 +11,11 @@ import com.willfp.eco.core.integrations.antigrief.AntigriefIntegration
import org.apache.commons.lang.Validate import org.apache.commons.lang.Validate
import org.bukkit.Location import org.bukkit.Location
import org.bukkit.block.Block import org.bukkit.block.Block
import org.bukkit.entity.* import org.bukkit.entity.Animals
import org.bukkit.entity.ArmorStand
import org.bukkit.entity.LivingEntity
import org.bukkit.entity.Monster
import org.bukkit.entity.Player
class AntigriefWorldGuard : AntigriefIntegration { class AntigriefWorldGuard : AntigriefIntegration {
override fun canBreakBlock( override fun canBreakBlock(

View File

@@ -45,4 +45,4 @@ class CustomItemsScyther : CustomItemsIntegration {
) )
} }
} }
} }

View File

@@ -0,0 +1,35 @@
package com.willfp.eco.internal.spigot.integrations.placeholder
import com.willfp.eco.core.integrations.placeholder.PlaceholderIntegration
import me.clip.placeholderapi.PlaceholderAPI
import org.bukkit.entity.Player
import java.util.regex.Pattern
class PlaceholderIntegrationPAPI : PlaceholderIntegration {
private val pattern = Pattern.compile("[%]([^% ]+)[%]")
override fun registerIntegration() {
// Do nothing.
}
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 = pattern.matcher(text)
while (matcher.find()) {
placeholders.add(matcher.group())
}
return placeholders
}
}

View File

@@ -4,6 +4,7 @@ import com.github.benmanes.caffeine.cache.Cache
import com.github.benmanes.caffeine.cache.Caffeine import com.github.benmanes.caffeine.cache.Caffeine
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager import com.willfp.eco.core.integrations.placeholder.PlaceholderManager
import com.willfp.eco.core.placeholder.AdditionalPlayer import com.willfp.eco.core.placeholder.AdditionalPlayer
import com.willfp.eco.core.math.MathContext
import com.willfp.eco.core.placeholder.PlaceholderInjectable import com.willfp.eco.core.placeholder.PlaceholderInjectable
import org.bukkit.entity.Player import org.bukkit.entity.Player
import redempt.crunch.CompiledExpression import redempt.crunch.CompiledExpression
@@ -25,7 +26,15 @@ private val max = Function("max", 2) {
max(it[0], it[1]) max(it[0], it[1])
} }
fun evaluateExpression(expression: String, player: Player?, context: PlaceholderInjectable, additional: Collection<AdditionalPlayer>): Double { fun evaluateExpression(expression: String, context: MathContext) =
evaluateExpression(expression, context.player, context.injectableContext, context.additionalPlayers)
private fun evaluateExpression(
expression: String,
player: Player?,
context: PlaceholderInjectable,
additional: Collection<AdditionalPlayer>
): Double {
val placeholderValues = PlaceholderManager.findPlaceholdersIn(expression) val placeholderValues = PlaceholderManager.findPlaceholdersIn(expression)
.map { PlaceholderManager.translatePlaceholders(it, player, context, additional) } .map { PlaceholderManager.translatePlaceholders(it, player, context, additional) }
.map { runCatching { FastNumberParsing.parseDouble(it) }.getOrDefault(0.0) } .map { runCatching { FastNumberParsing.parseDouble(it) }.getOrDefault(0.0) }

View File

@@ -1,42 +0,0 @@
package com.willfp.eco.internal.spigot.player
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.data.keys.PersistentDataKey
import com.willfp.eco.core.data.keys.PersistentDataKeyType
import com.willfp.eco.core.data.profile
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.event.player.PlayerJoinEvent
import org.bukkit.event.player.PlayerQuitEvent
class PlayerHealthFixer(
private val plugin: EcoPlugin
): Listener {
private val lastHealthKey = PersistentDataKey(
plugin.createNamespacedKey("last_health"),
PersistentDataKeyType.DOUBLE,
0.0
)
@EventHandler
fun onLeave(event: PlayerQuitEvent) {
if (!plugin.configYml.getBool("health-fixer")) {
return
}
val player = event.player
player.profile.write(lastHealthKey, player.health)
}
@EventHandler
fun onJoin(event: PlayerJoinEvent) {
if (!plugin.configYml.getBool("health-fixer")) {
return
}
val player = event.player
plugin.scheduler.runLater(2) {
player.health = player.profile.read(lastHealthKey)
}
}
}

View File

@@ -30,6 +30,12 @@ mysql:
user: username user: username
password: passy password: passy
# How many ticks to wait between committing data to a database. This doesn't
# affect yaml storage, only MySQL and MongoDB. By default, data is committed
# every tick, but you can increase this to be every x ticks, for example 20
# would be committing once a second.
save-interval: 1
# Options to manage the conflict finder # Options to manage the conflict finder
conflicts: conflicts:
whitelist: # Plugins that should never be marked as conflicts whitelist: # Plugins that should never be marked as conflicts
@@ -73,9 +79,6 @@ log-full-extension-errors: false
# a custom crafting table, though, this won't affect anything, and you should disable the option. # a custom crafting table, though, this won't affect anything, and you should disable the option.
displayed-recipes: true displayed-recipes: true
# Save health on leave and set it back on join - works around attribute modifiers.
health-fixer: false
# If eco plugins should not check for updates; only enable this if you know what you're doing # If eco plugins should not check for updates; only enable this if you know what you're doing
# as there can be urgent hotfixes that you are then not notified about. If you're confident # as there can be urgent hotfixes that you are then not notified about. If you're confident
# that you can manage updates on your own, turn this on. # that you can manage updates on your own, turn this on.

View File

@@ -48,4 +48,5 @@ softdepend:
- zShop - zShop
- DeluxeSellwands - DeluxeSellwands
- Scyther - Scyther
- ModelEngine - ModelEngine
- PvPManager

View File

@@ -0,0 +1,5 @@
package com.willfp.eco.internal.spigot.proxy
interface SyncCommandsProxy {
fun syncCommands()
}

View File

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

BIN
lib/PvPManager-3.10.9.jar Normal file

Binary file not shown.