Compare commits

..

84 Commits

Author SHA1 Message Date
Auxilor
4102be1201 Updated to 6.50.1 2023-01-27 17:47:21 +00:00
Will FP
f6bdb9cc65 Merge pull request #236 from 0ft3n/develop
Fixed fix
2023-01-27 17:46:19 +00:00
Will FP
c8282d1acf Merge branch 'develop' into develop 2023-01-27 17:46:13 +00:00
_OfTeN_
b056b537ef Fixed fix x2 2023-01-27 20:31:00 +03:00
_OfTeN_
f69b458731 Fixed fix 2023-01-27 19:48:28 +03:00
Auxilor
b035fa8940 Changed deprecations 2023-01-27 12:52:55 +00:00
Auxilor
25c087592d Cached placeholder lookups 2023-01-27 12:45:57 +00:00
Auxilor
083cb39771 Cleanups, fixes 2023-01-27 12:20:51 +00:00
Auxilor
eb3e0f5c09 Updated to 6.50.0 2023-01-27 12:18:01 +00:00
Auxilor
08f43ddafd Added dynamic placeholders 2023-01-27 12:17:51 +00:00
Auxilor
9d3efb5e83 Imports, often. Imports 2023-01-27 11:50:50 +00:00
Auxilor
8a5d1a604a Updated to 6.49.2 2023-01-27 11:46:15 +00:00
Auxilor
ef67c6d6ae Merge branch 'develop' 2023-01-27 11:45:42 +00:00
Auxilor
5b2654db15 Merge remote-tracking branch 'origin/master' 2023-01-27 11:45:18 +00:00
Will FP
eccd793317 Merge pull request #235 from Baterka/master
Fix Prices evaluation if they include player placeholders
2023-01-27 11:43:56 +00:00
Will FP
1bc44755a0 Merge pull request #234 from 0ft3n/develop
EconomyShopGUI integration fixes
2023-01-27 11:35:39 +00:00
Baterka
ec606d9ebe Fix Prices evaluation if they include player placeholders 2023-01-27 03:04:48 +01:00
_OfTeN_
c5556f15ab Attempted to fix Lands integration 2023-01-27 04:54:09 +03:00
_OfTeN_
399cce21f5 EconomyShopGUI integration fixes 2023-01-27 04:48:18 +03:00
Auxilor
b25feffdfa Codestyle 2023-01-24 13:54:59 +00:00
Auxilor
1a96fdf465 Updated to 6.49.1 2023-01-24 11:47:18 +00:00
Auxilor
cee1ac4cc2 Fixed eco setting off it's own warnings 2023-01-23 12:10:12 +00:00
Auxilor
ac10fa46dc Fixed javadoc 2023-01-23 11:27:44 +00:00
Auxilor
7c616e64ae Fixed XP / XPL 2023-01-18 20:03:28 +00:00
Auxilor
708f9130c6 Removed old docs URL 2023-01-18 12:53:13 +00:00
Auxilor
9118d49c67 Updated README.md 2023-01-18 12:52:29 +00:00
Auxilor
a1ce72476f Fixed README.md 2023-01-18 12:38:39 +00:00
Auxilor
2cfab99644 Fixed a plethora of codestyle issues 2023-01-17 17:57:40 +00:00
Auxilor
cc9b3f7710 Small blips 2023-01-17 17:50:07 +00:00
Auxilor
5bfe48c8d9 Updated CONTRIBUTING.md 2023-01-17 17:48:45 +00:00
Auxilor
22ff157ffc Merge branch 'MisterFrans_master' into develop 2023-01-17 17:44:35 +00:00
Auxilor
720dbe789c Merge branch 'patch-1' into develop 2023-01-17 17:42:49 +00:00
Auxilor
b51dd51941 Deprecated Prerequisite#HAS_BUNGEECORD and Prerequisite#HAS_VELOCITY 2023-01-17 16:05:06 +00:00
Auxilor
f3ffaa4cf6 Javadoc improvements 2023-01-17 16:02:07 +00:00
Auxilor
085032e315 Further command cleanup, added lang.yml validation and removed magic strings 2023-01-17 15:59:15 +00:00
Auxilor
3d920ee2b4 Updated to 6.49.0 2023-01-17 12:20:51 +00:00
Auxilor
06a04e4375 Fixed spelling inconsistency 2023-01-17 12:19:59 +00:00
Auxilor
7349f15784 Cleaned up command rework 2023-01-17 12:18:10 +00:00
Auxilor
a4c77857d5 Merge branch 'master' into Samkist_master 2023-01-17 12:00:10 +00:00
Auxilor
999fafc8df Javadoc cleanjp 2023-01-17 12:00:04 +00:00
Auxilor
0d533850f6 Updated to 6.48.3 2023-01-13 17:53:44 +00:00
Auxilor
569f9cfcb4 Merge remote-tracking branch 'origin/master' 2023-01-13 17:52:53 +00:00
Auxilor
f0619f2374 Updated to 6.48.2 2023-01-13 17:52:48 +00:00
Auxilor
7e8d97e11d Updated MythicMobs and ExecutableItems 2023-01-13 17:52:20 +00:00
Auxilor
d3414f25ad Cleanup 2023-01-13 17:39:29 +00:00
MCCasper
f0cf118448 Update McmmoManager.java
Change to not quit after first iteration of loop
2022-12-29 10:07:05 -05:00
François
297bb10b85 Removal of an unnecessary semicolon 2022-12-27 13:06:22 +01:00
François
751624bc8d Update of Lands api to the latest version (6.26.7). 2022-12-26 15:34:11 +01:00
Auxilor
8b1b15a3e4 Updated to 6.48.2 2022-12-20 15:05:30 +00:00
Auxilor
7fe330bafb Fixed 1.19.3 support 2022-12-20 15:05:21 +00:00
Auxilor
20584b2a9b Updated to 6.48.1 2022-12-12 12:13:32 +00:00
Auxilor
bd7594a117 Fixed 1.19.3 build 2022-12-12 12:13:22 +00:00
Samuel Pizette
f1bfa21270 restructured some methods 2022-12-11 20:32:23 -05:00
Samuel Pizette
01aa1e708a more missing documentations 2022-12-11 14:17:16 -05:00
Samuel Pizette
8424baa285 wrote missing documentations 2022-12-11 14:14:37 -05:00
Samuel Pizette
d54a2b9516 fixed method signature of extensions 2022-12-10 17:18:06 -05:00
Samuel Pizette
f7f12b6255 finished working refactor 2022-12-10 16:50:52 -05:00
Samuel Pizette
42eb1344a6 removed unneeded debug statement 2022-12-10 16:08:33 -05:00
Samuel Pizette
4c2a8585cc got command functionality working 2022-12-10 16:08:01 -05:00
Samuel Pizette
8cccc67b0d wrote documentation for Eco Plugin and Eco Sub Command 2022-12-09 14:42:12 -05:00
Samuel Pizette
396d74497c wrote EcoHandledCommand.kt documentation 2022-12-09 14:38:13 -05:00
Samuel Pizette
49602dce04 rewrote notify documentation 2022-12-09 14:24:13 -05:00
Samuel Pizette
5da811ba74 added notifyPermissionRequired and updated some comments 2022-12-09 13:52:03 -05:00
Samuel Pizette
a4d57e21fe cleaned up eco handled command 2022-12-09 13:29:33 -05:00
Samuel Pizette
4b8efdc79f whoops 2022-12-09 12:10:39 -05:00
Samuel Pizette
a87f675269 updated method signature annotations to accurately reflect behavior 2022-12-06 03:42:34 -05:00
Samuel Pizette
a371d314b8 shortened logic of notifyNull extension 2022-12-06 03:38:28 -05:00
Samuel Pizette
9a9097adc5 updated some annotations 2022-12-05 20:27:31 -05:00
Samuel Pizette
0669a57e4b updated some annotations 2022-12-05 20:08:35 -05:00
Samuel Pizette
692eaf6836 changed onTabComplete return signature in EcoDelegatedBukkitCommand.kt from nullable to not null 2022-12-05 19:22:09 -05:00
Samuel Pizette
85f02c5ca2 wrote notification kotlin extensions 2022-12-04 19:35:54 -05:00
Samuel Pizette
74c428b90d rearranged notify methods 2022-12-04 19:17:36 -05:00
Samuel Pizette
fd8c67fa66 wrote handle tab complete for ecohandled command 2022-12-04 19:13:23 -05:00
Samuel Pizette
a396754e2e commands rework progress
- Wrote EcoDelegatedBukkitCommand.kt
- Wrote EcoHandledCommand.kt
- Wrote Eco#createSubCommand
- Renamed RegistrableCommandBase.java to PluginCommandBase
- Moved most of EcoPluginCommand.kt to EcoHandledCommand.kt
- Changed Delegate type in PluginCommand from CommandBase to PluginCommandBase
2022-12-04 18:52:19 -05:00
Samuel Pizette
6f97f47712 removed getAliases and getDescription from commandbase 2022-12-04 18:50:03 -05:00
Samuel Pizette
d1109e485a more fixing auto reformat 2022-12-04 17:56:07 -05:00
Samuel Pizette
476e5c7cae fuck auto reformat 2022-12-04 17:54:50 -05:00
Samuel Pizette
dc2b7a6fda returned instance get to original location 2022-12-04 17:53:49 -05:00
Samuel Pizette
00f18519b0 progress commit 2022-12-04 17:52:05 -05:00
Samuel Pizette
f7ea5fd182 fix eco format 2022-11-30 16:50:02 -05:00
Samuel Pizette
70d29c872a removed HandledCommand 2022-11-29 16:29:23 -05:00
Samuel Pizette
f79f4a84c3 deprecated DelegatedBukkitCommand 2022-11-29 16:29:16 -05:00
Samuel Pizette
9af63907ef Merge branch 'master' into master 2022-11-29 16:28:37 -05:00
Samuel Pizette
c9aa92895b work in progress commit 2022-11-29 16:13:01 -05:00
76 changed files with 1476 additions and 737 deletions

View File

@@ -1,38 +1,7 @@
# How to contribute to eco
## Codestyle
Please open any Pull Requests into the `develop` branch or ideally into a new branch for your changes. PRs that go into `master` won't be ignored, but I have to checkout and merge manually, which makes your PR show as being closed.
1. The eco checkstyle is in /config/checkstyle.xml
Do not write any Kotlin-only APIs; all API components should be written in Java, Kotlin extensions should not have functionality that isn't available in java. The same applies the other way round, do not write any backend code in Java, it should be Kotlin-exclusive.
- The pull request must not have any checkstyle issues.
- Every method and field must have a javadoc attached.
2. Use JetBrains annotations
- Every parameter should be annotated with @NotNull or @Nullable
3. Imports
- No group (*) imports.
- No static imports.
4. Kotlin
- Kotlin should be the only language used in the backend, java should be the only language used in the frontend.
- Kotlin API extensions should only be for creating extension functions and extra niceties that aren't possible in java.
Do not write API components in kotlin.
- Kotlin code should never be called directly from the frontend Java API. Kotlin API extensions should always rely on
java, not the other way round.
## Dependency Injection
- eco uses Dependency Injection
- Any calls to Eco#getHandler#getEcoPlugin are code smells and should never be used unless **absolutely necessary**.
- NamespacedKeys, FixedMetadataValues, Runnables, and Schedules should be managed using AbstractEcoPlugin through DI.
- Any DI class should extend PluginDependent where possible. If the class extends another, then you **must** store the
plugin instance in a private final variable called **plugin** with a private or protected getter.
## Other
- All drops **must** be sent through a DropQueue - calls to World#dropItem will get your PR rejected.
- eco is built with java 17.
If you have any questions about contributing, feel free to ask in the [Discord](https://discord.gg/ZcwpSsE)!

View File

@@ -1,5 +1,5 @@
# eco
eco is a powerful Spigot development library that simplifies the process of plugin creation and supercharges
eco is a powerful Spigot plugin framework that simplifies the process of plugin creation and supercharges
your plugins.
It's the engine behind [EcoEnchants](https://polymart.org/resource/490), [Reforges](https://polymart.org/resource/1330),
[EcoItems](https://polymart.org/resource/1247), [EcoSkills](https://polymart.org/resource/1351),
@@ -16,30 +16,54 @@ and many more.
<a href="https://bstats.org/plugin/bukkit/EcoEnchants" alt="bstats players">
<img src="https://img.shields.io/bstats/players/7666?color=informational"/>
</a>
<a href="https://plugins.auxilor.io/" alt="Docs (gitbook)">
<img src="https://img.shields.io/badge/docs-gitbook-informational"/>
</a>
<a href="https://discord.gg/ZcwpSsE/" alt="Discord">
<img src="https://img.shields.io/discord/452518336627081236?label=discord&color=informational"/>
</a>
<a href="https://github.com/Auxilor/eco/actions/workflows/java-ci.yml" alt="Latest Dev Build">
<img src="https://img.shields.io/github/workflow/status/Auxilor/eco/Java%20CI/develop?color=informational"/>
<img src="https://img.shields.io/github/actions/workflow/status/Auxilor/eco/java-ci.yml?branch=develop&color=informational"/>
</a>
</p>
eco comes packed with all the tools you need in your plugins:
- Modern command API
- Native color parsing with full hex/RGB/MiniMessage support
- Yaml/JSON/TOML config system
- Persistent data storage API with Yaml/MySQL/MongoDB support
- Packet item display system
- Entity AI API with near-1:1 NMS mappings
- More events
- Extension API, essentially plugins for plugins
- Fluent dependency injection for NamespacedKey, Metadata values, etc.
- Ultra-fast ItemStack reimplementation bypassing ItemMeta
- Complete GUI API with pre-made components available from [ecomponent](https://github.com/Auxilor/ecomponent)
- Over 30 native integrations for other plugins
- First-class custom item support with lookup strings
- Math expression parsing via [Crunch](https://github.com/Redempt/Crunch)
- Particle lookups
- Complete Placeholder API
- Price system, supporting economy plugins, XP, Items, etc.
- NMS/Version-specific tooling
- Custom crafting recipe API with support for stacks and custom items
- Native plugin update checking
- Native bStats support
- Full Kotlin support and native extensions
- Tooling to make meta-frameworks, like [libreforge](https://github.com/Auxilor/libreforge)
- And much more
# For server owners
- Requires ProtocolLib to be installed: get the latest version [here](https://www.spigotmc.org/resources/protocollib.1997/)
- Supports 1.17+
## Downloads
- Stable (Recommended): [GitHub](https://github.com/Auxilor/eco/releases), [Polymart](https://polymart.org/resource/eco.773)
- Dev (Not Recommended): [GitHub](https://github.com/Auxilor/eco/actions/workflows/java-ci.yml) (Open latest run and download)
- Stable: [GitHub](https://github.com/Auxilor/eco/releases), [Polymart](https://polymart.org/resource/eco.773)
- Dev: [GitHub](https://github.com/Auxilor/eco/actions/workflows/java-ci.yml) (Open latest run and download)
# For developers
## Javadoc
The 6.45.0 Javadoc can be found [here](https://javadoc.jitpack.io/com/willfp/eco/6.45.0/javadoc/)
The 6.49.0 Javadoc can be found [here](https://javadoc.jitpack.io/com/willfp/eco/6.49.0/javadoc/)
## Plugin Information
@@ -68,7 +92,7 @@ dependencies {
}
```
Replace `Tag` with a release tag for eco, eg `6.45.0`.
Replace `Tag` with a release tag for eco, eg `6.49.0`.
Maven:
@@ -88,7 +112,7 @@ Maven:
</dependency>
```
Replace `Tag` with a release tag for eco, eg `6.45.0`.
Replace `Tag` with a release tag for eco, eg `6.49.0`.
## Build locally:
@@ -103,7 +127,7 @@ cd eco
## License
*Click here to read [the entire license](https://github.com/Auxilor/eco/blob/master/LICENSE.md).*
eco is licensed under GNU GPL3. *Click here to read [the entire license](https://github.com/Auxilor/eco/blob/master/LICENSE.md).*
<h1 align="center">
Check out our partners!

View File

@@ -25,6 +25,7 @@ dependencies {
implementation(project(path = ":eco-core:core-nms:v1_18_R1", configuration = "reobf"))
implementation(project(path = ":eco-core:core-nms:v1_18_R2", configuration = "reobf"))
implementation(project(path = ":eco-core:core-nms:v1_19_R1", configuration = "reobf"))
implementation(project(path = ":eco-core:core-nms:v1_19_R2", configuration = "reobf"))
}
allprojects {

View File

@@ -1,5 +1,7 @@
package com.willfp.eco.core;
import com.willfp.eco.core.command.CommandBase;
import com.willfp.eco.core.command.PluginCommandBase;
import com.willfp.eco.core.command.impl.PluginCommand;
import com.willfp.eco.core.config.ConfigType;
import com.willfp.eco.core.config.interfaces.Config;
@@ -49,18 +51,18 @@ import java.util.UUID;
import java.util.logging.Logger;
/**
* Holds the instance of eco for bridging between the frontend
* and backend.
* Holds the instance of eco for bridging between the frontend and backend.
* <p>
* <strong>Do not use this in your plugins!</strong> It can and will contain
* breaking changes between minor versions and even patches, and you will create
* compatibility issues by. All parts of this have been abstracted
* into logically named API components that you can use.
* breaking changes between minor versions and even patches, and you will create compatibility
* issues by. All parts of this have been abstracted into logically named API components that you
* can use.
*
* @see Eco#get()
*/
@ApiStatus.Internal
public interface Eco {
/**
* Create a scheduler.
*
@@ -157,6 +159,40 @@ public interface Eco {
@NotNull
EcoPlugin getEcoPlugin();
/**
* Create PluginCommandBase implementation of {@link PluginCommand}.
*
* @param parentDelegate the enclosing class of this implementation.
* @param plugin the plugin.
* @param name the name of the command.
* @param permission the permission of the command.
* @param playersOnly if the command is players only.
* @return The PluginCommandBase implementation
*/
@NotNull
PluginCommandBase createPluginCommand(@NotNull CommandBase parentDelegate,
@NotNull EcoPlugin plugin,
@NotNull String name,
@NotNull String permission,
boolean playersOnly);
/**
* Create CommandBase implementation of {@link com.willfp.eco.core.command.impl.Subcommand Subcommand}.
*
* @param parentDelegate the enclosing class of this implementation.
* @param plugin the plugin.
* @param name the name of the command.
* @param permission the permission of the command.
* @param playersOnly if the command is players only.
* @return The CommandBase implementation
*/
@NotNull
CommandBase createSubcommand(@NotNull CommandBase parentDelegate,
@NotNull EcoPlugin plugin,
@NotNull String name,
@NotNull String permission,
boolean playersOnly);
/**
* Updatable config.
*
@@ -378,9 +414,8 @@ public interface Eco {
/**
* Create a {@link NamespacedKey} quickly
* <p>
* Bypasses the constructor, allowing for the creation of invalid keys,
* therefore this is considered unsafe and should only be called after
* the key has been confirmed to be valid.
* Bypasses the constructor, allowing for the creation of invalid keys, therefore this is
* considered unsafe and should only be called after the key has been confirmed to be valid.
*
* @param namespace The namespace.
* @param key The key.
@@ -527,8 +562,7 @@ public interface Eco {
void unregisterCommand(@NotNull final PluginCommand command);
/**
* Get the instance of eco; the bridge between the api frontend
* and the implementation backend.
* Get the instance of eco; the bridge between the api frontend and the implementation backend.
*
* @return The instance of eco.
*/
@@ -542,6 +576,7 @@ public interface Eco {
*/
@ApiStatus.Internal
final class Instance {
/**
* Instance of eco.
*/

View File

@@ -344,6 +344,13 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
this.configHandler = Eco.get().createConfigHandler(this);
this.langYml = this.createLangYml();
if (!this.langYml.isValid() && !(this instanceof Eco)) {
this.getLogger().warning("Notify plugin authors " + String.join(", ", this.getDescription().getAuthors()) + " that");
this.getLogger().warning("they are missing crucial lang.yml keys! They can be found");
this.getLogger().warning("in the LangYml class.");
}
this.configYml = this.createConfigYml();
Eco.get().addNewPlugin(this);

View File

@@ -221,7 +221,7 @@ public final class PluginProps {
/**
* Create new props from known values.
*
* <p>
* Marked as internal as this method will break whenever the properties themselves
* are updated (e.g. if a new property is added) - so to prevent any potential
* backwards-compatibility bugs, this method cannot be invoked outside eco itself.

View File

@@ -70,7 +70,10 @@ public class Prerequisite {
/**
* Requires the server to be running an implementation of BungeeCord.
*
* @deprecated This will never return true.
*/
@Deprecated(since = "6.49.0", forRemoval = true)
public static final Prerequisite HAS_BUNGEECORD = new Prerequisite(
() -> ClassUtils.exists("net.md_5.bungee.api.event.ServerConnectedEvent"),
"Requires server to be running BungeeCord (or a fork)"
@@ -78,7 +81,10 @@ public class Prerequisite {
/**
* Requires the server to be running an implementation of Velocity.
*
* @deprecated This will never return true.
*/
@Deprecated(since = "6.49.0", forRemoval = true)
public static final Prerequisite HAS_VELOCITY = new Prerequisite(
() -> ClassUtils.exists("com.velocitypowered.api.event.player.ServerConnectedEvent"),
"Requires server to be running Velocity (or a fork)"

View File

@@ -1,32 +1,37 @@
package com.willfp.eco.core.command;
import com.google.common.collect.ImmutableList;
import com.willfp.eco.core.EcoPlugin;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;
/**
* Interface for all command implementations.
* Generic interface for commands.
*/
@SuppressWarnings("removal")
@SuppressWarnings("null")
public interface CommandBase {
/**
* Get command name.
*
* @return The name.
*/
String getName();
@NotNull String getName();
/**
* Get command permission.
*
* @return The permission.
*/
String getPermission();
@NotNull String getPermission();
/**
* If only players can execute the command.
@@ -41,110 +46,237 @@ public interface CommandBase {
* @param command The subcommand.
* @return The parent command.
*/
CommandBase addSubcommand(@NotNull CommandBase command);
@NotNull CommandBase addSubcommand(@NotNull CommandBase command);
/**
* Get the subcommands of the command.
*
* @return The subcommands.
*/
@NotNull List<CommandBase> getSubcommands();
/**
* Intended for returning the enclosing CommandBase,
* when this instance is serving as the delegate command base.
*
* @return the wrapping object of this delegate.
*/
default @NotNull CommandBase getWrapped() {
return this;
}
/**
* Handle command execution.
* <p>
* This will always be called on command execution.
*
* @param sender The sender.
* @param args The args.
* @throws NotificationException naturally, this is handled as a part of the command system.
*/
default void onExecute(@NotNull CommandSender sender,
@NotNull List<String> args) {
default void onExecute(@NotNull final CommandSender sender, @NotNull final List<String> args) throws NotificationException {
// Do nothing.
}
/**
* Handle command execution from players.
* <p>
* This will only be called if the sender is a player.
*
* @param sender The sender.
* @param args The args.
* @throws NotificationException naturally, this is handled as a part of the command system.
*/
default void onExecute(@NotNull Player sender,
@NotNull List<String> args) {
default void onExecute(@NotNull final Player sender, @NotNull final List<String> args) throws NotificationException {
// Do nothing.
}
/**
* Handle tab completion.
* <p>
* This will always be called on tab completion.
*
* @param sender The sender.
* @param args The args.
* @return The results.
*/
@NotNull
default List<String> tabComplete(@NotNull CommandSender sender,
@NotNull List<String> args) {
default List<String> tabComplete(@NotNull final CommandSender sender, @NotNull final List<String> args) {
return new ArrayList<>();
}
/**
* Handle tab completion.
* <p>
* This will only be called if the sender is a player.
*
* @param sender The sender.
* @param args The args.
* @return The results.
*/
@NotNull
default List<String> tabComplete(@NotNull Player sender,
@NotNull List<String> args) {
default List<String> tabComplete(@NotNull final Player sender, @NotNull final List<String> args) {
return new ArrayList<>();
}
/**
* Throws an {@link NotificationException} relating to a specific lang.yml key.
* <p>
* This is automatically handled with eco, and should not be surrounded by a
* try/catch block.
*
* @param key The lang.yml key for the message to be sent.
* @throws NotificationException always.
*/
default void notify(@NotNull final String key) throws NotificationException {
throw new NotificationException(key);
}
/**
* Throws an {@link NotificationException} relating to a specific lang.yml key
* if the passed object is null.
* <p>
* This is automatically handled with eco, and should not be surrounded by a
* try/catch block.
*
* @param obj The object to test.
* @param key The lang.yml key for the message to be sent.
* @param <T> The object type.
* @return Returns the object, definitely not-null.
* @throws NotificationException If the object is null.
*/
@NotNull
default <T> T notifyNull(@Nullable final T obj,
@NotNull final String key) throws NotificationException {
if (Objects.isNull(obj)) {
notify(key);
}
return Objects.requireNonNull(obj);
}
/**
* Throws an {@link NotificationException} relating to a specific lang.yml key
* if the passed object doesn't match the predicate.
* <p>
* This is automatically handled with eco, and should not be surrounded by a
* try/catch block.
*
* @param obj The object to test.
* @param key The lang.yml key for the message to be sent.
* @param predicate The predicate to test the object against.
* @param <T> The type of the object.
* @return Returns the object, definitely not-null.
* @throws NotificationException If the object doesn't satisfy the predicate.
*/
@NotNull
default <T> T notifyFalse(@NotNull final T obj,
@NotNull final String key,
@NotNull final Predicate<T> predicate) throws NotificationException {
notifyFalse(predicate.test(obj), key);
return obj;
}
/**
* Throws an {@link NotificationException} relating to a specific lang.yml key
* if a condition is false.
* <p>
* This is automatically handled with eco, and should not be surrounded by a
* try/catch block.
*
* @param condition The condition to test.
* @param key The lang.yml key for the message to be sent.
* @return True.
* @throws NotificationException If the condition is false.
*/
default boolean notifyFalse(final boolean condition,
@NotNull final String key) throws NotificationException {
if (!condition) {
notify(key);
}
return true;
}
/**
* Throws an {@link NotificationException} relating to a specific lang.yml key
* if the passed string doesn't relate to a currently online player.
* <p>
* This is automatically handled with eco, and should not be surrounded by a
* try/catch block.
*
* @param playerName The player name.
* @param key The lang.yml key for the message to be sent.
* @return Returns the player, definitely not-null.
* @throws NotificationException If the player name is invalid.
*/
@NotNull
default Player notifyPlayerRequired(@Nullable final String playerName, @NotNull final String key) throws NotificationException {
if (playerName == null) {
notify(key);
}
assert playerName != null;
final Player player = Bukkit.getPlayer(playerName);
notifyNull(player, key);
return Objects.requireNonNull(player);
}
/**
* Throws an {@link NotificationException} relating to a specific lang.yml key
* if the passed string doesn't relate to a player on the server.
* <p>
* This is automatically handled with eco, and should not be surrounded by a
* try/catch block.
*
* @param playerName The player name.
* @param key The lang.yml key for the message to be sent.
* @return Returns the offline player, definitely not-null.
* @throws NotificationException If the player name is invalid.
*/
@NotNull
default OfflinePlayer notifyOfflinePlayerRequired(@Nullable final String playerName,
@NotNull final String key) throws NotificationException {
if (playerName == null) {
notify(key);
}
assert playerName != null;
@SuppressWarnings("deprecation") final OfflinePlayer player = Bukkit.getOfflinePlayer(playerName);
boolean hasPlayedBefore = player.hasPlayedBefore() || player.isOnline();
notifyFalse(!hasPlayedBefore, key);
return player;
}
/**
* Throws an exception containing a langYml key if player doesn't have permission.
*
* @param player The player.
* @param permission The permission.
* @param key The lang.yml key for the message to be sent.
* @return The player.
* @throws NotificationException If the player doesn't have the required permission.
*/
@NotNull
default Player notifyPermissionRequired(@NotNull final Player player,
@NotNull final String permission,
@NotNull final String key) throws NotificationException {
return notifyFalse(player, key, p -> p.hasPermission(permission));
}
/**
* Get the plugin.
*
* @return The plugin.
*/
EcoPlugin getPlugin();
/**
* Get the handler.
*
* @return The handler.
* @see CommandHandler
* @deprecated Use {@link CommandBase#onExecute(CommandSender, List)} instead.
*/
@Deprecated(forRemoval = true)
default CommandHandler getHandler() {
return (a, b) -> {
};
}
/**
* Set the handler.
*
* @param handler The handler.
* @see CommandHandler
* @deprecated Handlers have been deprecated.
*/
@Deprecated(forRemoval = true)
default void setHandler(@NotNull final CommandHandler handler) {
// Do nothing.
}
/**
* Get the tab completer.
*
* @return The tab completer.
* @see TabCompleteHandler
* @deprecated Use {@link CommandBase#tabComplete(CommandSender, List)} instead.
*/
@Deprecated(forRemoval = true)
default TabCompleteHandler getTabCompleter() {
return (a, b) -> ImmutableList.of();
}
/**
* Set the tab completer.
*
* @param handler The handler.
* @see TabCompleteHandler
* @deprecated Handlers have been deprecated.
*/
@Deprecated(forRemoval = true)
default void setTabCompleter(@NotNull final TabCompleteHandler handler) {
// Do nothing.
}
}

View File

@@ -1,29 +0,0 @@
package com.willfp.eco.core.command;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* A command handler handles the actual code for a command.
* <p>
* The replacement for {@link org.bukkit.command.CommandExecutor#onCommand(CommandSender, Command, String, String[])}
*
* @see CommandBase
* @deprecated Handlers have been deprecated. This legacy system will eventually be removed,
* update to use the new system: {@link CommandBase#onExecute(CommandSender, List)}.
*/
@FunctionalInterface
@Deprecated(since = "6.17.0", forRemoval = true)
public interface CommandHandler {
/**
* The code to be called on execution.
*
* @param sender The sender.
* @param args The arguments.
*/
void onExecute(@NotNull CommandSender sender,
@NotNull List<String> args);
}

View File

@@ -0,0 +1,36 @@
package com.willfp.eco.core.command;
import org.jetbrains.annotations.NotNull;
/**
* A notification exception is thrown when {@link org.bukkit.command.CommandSender}s don't
* specify valid arguments in commands.
* <p>
* Methods in eco that throw this will contain automatic handling and thus
* should not be surrounded by try / catch blocks.
*/
public class NotificationException extends Exception {
/**
* The key for the lang.yml message to be sent.
*/
private final String key;
/**
* Creates a notification exception.
*
* @param key The lang key of the notification.
*/
public NotificationException(@NotNull final String key) {
super(key);
this.key = key;
}
/**
* Get the lang key.
*
* @return The lang key.
*/
public String getKey() {
return key;
}
}

View File

@@ -0,0 +1,44 @@
package com.willfp.eco.core.command;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
/**
* Plugin command bases can be registered directly with the server,
* this essentially functions as the interface that is implemented generically
* via {@link com.willfp.eco.core.command.impl.PluginCommand}.
*/
public interface PluginCommandBase extends CommandBase {
/**
* Register the PluginCommandBase to the bukkit commandMap.
*/
void register();
/**
* Unregister the PluginCommandBase from the bukkit commandMap.
*/
void unregister();
/**
* Get aliases. Leave null if this command is from plugin.yml.
*
* @return The aliases.
*/
@NotNull
default List<String> getAliases() {
return new ArrayList<>();
}
/**
* Get description.
*
* @return The description.
*/
@Nullable
default String getDescription() {
return null;
}
}

View File

@@ -1,30 +0,0 @@
package com.willfp.eco.core.command;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* A Tab Complete handler handles the actual tab-completion code.
* <p>
* The replacement for {@link org.bukkit.command.TabCompleter#onTabComplete(CommandSender, Command, String, String[])}
*
* @see CommandBase
* @deprecated Handlers have been deprecated. This legacy system will eventually be removed,
* update to use the new system: {@link CommandBase#tabComplete(CommandSender, List)}
*/
@FunctionalInterface
@Deprecated(since = "6.17.0", forRemoval = true)
public interface TabCompleteHandler {
/**
* Handle Tab Completion.
*
* @param sender The sender.
* @param args The arguments.
* @return The tab completion results.
*/
List<String> tabComplete(@NotNull CommandSender sender,
@NotNull List<String> args);
}

View File

@@ -6,13 +6,15 @@ 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).
*
* @deprecated Internal command implementations have been removed from the API.
*/
@Deprecated(forRemoval = true, since = "6.49.0")
public final class DelegatedBukkitCommand extends Command implements TabCompleter, PluginIdentifiableCommand {
/**
* The delegate command.
@@ -34,7 +36,7 @@ public final class DelegatedBukkitCommand extends Command implements TabComplete
public boolean execute(@NotNull final CommandSender commandSender,
@NotNull final String label,
@NotNull final String[] args) {
return delegate.onCommand(commandSender, this, label, args);
return false;
}
@Override
@@ -42,7 +44,7 @@ public final class DelegatedBukkitCommand extends Command implements TabComplete
@NotNull final Command command,
@NotNull final String label,
@NotNull final String[] args) {
return delegate.onTabComplete(commandSender, command, label, args);
return List.of();
}
@NotNull
@@ -51,9 +53,8 @@ public final class DelegatedBukkitCommand extends Command implements TabComplete
return this.delegate.getPlugin();
}
@Nullable
@Override
public String getPermission() {
public @NotNull String getPermission() {
return this.delegate.getPermission();
}

View File

@@ -1,294 +0,0 @@
package com.willfp.eco.core.command.impl;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.command.CommandBase;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.util.StringUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
/**
* Abstract class for commands that can be handled.
* <p>
* Handled commands have a method to pass in raw input from bukkit commands
* in order to execute the command-specific code. It's essentially an internal
* layer, hence why it's a package-private class.
*/
@SuppressWarnings({"DeprecatedIsStillUsed", "removal"})
abstract class HandledCommand implements CommandBase {
/**
* The plugin.
*/
private final EcoPlugin plugin;
/**
* The name of the command.
*/
private final String name;
/**
* The permission required to execute the command.
* <p>
* Written out as a string for flexibility with subclasses.
*/
private final String permission;
/**
* Should the command only be allowed to be executed by players?
* <p>
* In other worlds, only allowed to be executed by console.
*/
private final boolean playersOnly;
/**
* The actual code to be executed in the command.
*/
@Deprecated
@Nullable
private com.willfp.eco.core.command.CommandHandler handler = null;
/**
* The tab completion code to be executed in the command.
*/
@Deprecated
@Nullable
private com.willfp.eco.core.command.TabCompleteHandler tabCompleter = null;
/**
* All subcommands for the command.
*/
private final List<CommandBase> subcommands;
/**
* Create a new command.
* <p>
* The name cannot be the same as an existing command as this will conflict.
*
* @param plugin Instance of a plugin.
* @param name The name used in execution.
* @param permission The permission required to execute the command.
* @param playersOnly If only players should be able to execute this command.
*/
HandledCommand(@NotNull final EcoPlugin plugin,
@NotNull final String name,
@NotNull final String permission,
final boolean playersOnly) {
this.plugin = plugin;
this.name = name;
this.permission = permission;
this.playersOnly = playersOnly;
this.subcommands = new ArrayList<>();
}
/**
* Add a subcommand to the command.
*
* @param subcommand The subcommand.
* @return The parent command.
*/
@Override
public final CommandBase addSubcommand(@NotNull final CommandBase subcommand) {
subcommands.add(subcommand);
return this;
}
/**
* Get the plugin.
*
* @return The plugin.
*/
@Override
public EcoPlugin getPlugin() {
return this.plugin;
}
/**
* Handle the command.
*
* @param sender The sender.
* @param args The arguments.
*/
protected final void handle(@NotNull final CommandSender sender,
@NotNull final String[] args) {
if (!canExecute(sender, this, this.getPlugin())) {
return;
}
if (args.length > 0) {
for (CommandBase subcommand : this.getSubcommands()) {
if (subcommand.getName().equalsIgnoreCase(args[0])) {
if (!canExecute(sender, subcommand, this.getPlugin())) {
return;
}
((HandledCommand) subcommand).handle(sender, Arrays.copyOfRange(args, 1, args.length));
return;
}
}
}
if (this.isPlayersOnly() && !(sender instanceof Player)) {
sender.sendMessage(this.getPlugin().getLangYml().getMessage("not-player"));
return;
}
if (this.getHandler() != null) {
this.getHandler().onExecute(sender, Arrays.asList(args));
} else {
this.onExecute(sender, Arrays.asList(args));
if (sender instanceof Player player) {
this.onExecute(player, Arrays.asList(args));
}
}
}
/**
* Handle the tab completion.
*
* @param sender The sender.
* @param args The arguments.
* @return The tab completion results.
*/
protected final List<String> handleTabCompletion(@NotNull final CommandSender sender,
@NotNull final String[] args) {
if (!sender.hasPermission(this.getPermission())) {
return null;
}
if (args.length == 1) {
List<String> completions = new ArrayList<>();
StringUtil.copyPartialMatches(
args[0],
this.getSubcommands().stream()
.filter(subCommand -> sender.hasPermission(subCommand.getPermission()))
.map(CommandBase::getName)
.collect(Collectors.toList()),
completions
);
Collections.sort(completions);
if (!completions.isEmpty()) {
return completions;
}
}
if (args.length >= 2) {
HandledCommand command = null;
for (CommandBase subcommand : this.getSubcommands()) {
if (!sender.hasPermission(subcommand.getPermission())) {
continue;
}
if (args[0].equalsIgnoreCase(subcommand.getName())) {
command = (HandledCommand) subcommand;
}
}
if (command != null) {
return command.handleTabCompletion(sender, Arrays.copyOfRange(args, 1, args.length));
}
}
if (this.getTabCompleter() != null) {
return this.getTabCompleter().tabComplete(sender, Arrays.asList(args));
} else {
List<String> completions = new ArrayList<>(this.tabComplete(sender, Arrays.asList(args)));
if (sender instanceof Player player) {
completions.addAll(this.tabComplete(player, Arrays.asList(args)));
}
return completions;
}
}
/**
* If a sender can execute the command.
*
* @param sender The sender.
* @param command The command.
* @param plugin The plugin.
* @return If the sender can execute.
*/
public static boolean canExecute(@NotNull final CommandSender sender,
@NotNull final CommandBase command,
@NotNull final EcoPlugin plugin) {
if (!sender.hasPermission(command.getPermission()) && sender instanceof Player) {
sender.sendMessage(plugin.getLangYml().getNoPermission());
return false;
}
return true;
}
/**
* Get the command name.
*
* @return The name.
*/
public String getName() {
return this.name;
}
/**
* Get the permission required to execute the command.
*
* @return The permission.
*/
public String getPermission() {
return this.permission;
}
/**
* Get if the command can only be executed by players.
*
* @return If players only.
*/
public boolean isPlayersOnly() {
return this.playersOnly;
}
/**
* Get the subcommands of the command.
*
* @return The subcommands.
*/
public List<CommandBase> getSubcommands() {
return this.subcommands;
}
@Deprecated(forRemoval = true)
@Override
public @Nullable com.willfp.eco.core.command.CommandHandler getHandler() {
return this.handler;
}
@Deprecated(forRemoval = true)
@Override
public @Nullable com.willfp.eco.core.command.TabCompleteHandler getTabCompleter() {
return this.tabCompleter;
}
@Deprecated(forRemoval = true)
@Override
public void setHandler(@Nullable final com.willfp.eco.core.command.CommandHandler handler) {
this.handler = handler;
}
@Deprecated(forRemoval = true)
@Override
public void setTabCompleter(@Nullable final com.willfp.eco.core.command.TabCompleteHandler tabCompleter) {
this.tabCompleter = tabCompleter;
}
}

View File

@@ -2,29 +2,27 @@ package com.willfp.eco.core.command.impl;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.EcoPlugin;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandMap;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import com.willfp.eco.core.command.CommandBase;
import com.willfp.eco.core.command.PluginCommandBase;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
/**
* PluginCommands are the class to be used instead of CommandExecutor,
* they function as the base command, e.g. {@code /ecoenchants} would be a base command, with each
* subsequent argument functioning as subcommands.
* PluginCommands are the class to be used instead of CommandExecutor, they function as the base
* command, e.g. {@code /ecoenchants} would be a base command, with each subsequent argument
* functioning as subcommands.
* <p>
* The command will not be registered until register() is called.
* <p>
* The name cannot be the same as an existing command as this will conflict.
*/
public abstract class PluginCommand extends HandledCommand implements CommandExecutor, TabCompleter {
public abstract class PluginCommand implements PluginCommandBase {
/**
* The delegate command.
*/
private final PluginCommandBase delegate;
/**
* Create a new command.
*
@@ -37,125 +35,51 @@ public abstract class PluginCommand extends HandledCommand implements CommandExe
@NotNull final String name,
@NotNull final String permission,
final boolean playersOnly) {
super(plugin, name, permission, playersOnly);
this.delegate = Eco.get().createPluginCommand(this, plugin, name, permission, playersOnly);
}
/**
* Registers the command with the server,
*/
public final void register() {
org.bukkit.command.PluginCommand command = Bukkit.getPluginCommand(this.getName());
if (command != null) {
command.setExecutor(this);
command.setTabCompleter(this);
if (this.getDescription() != null) {
command.setDescription(this.getDescription());
}
List<String> aliases = new ArrayList<>(command.getAliases());
aliases.addAll(this.getAliases());
command.setAliases(aliases);
} else {
this.unregister();
CommandMap commandMap = getCommandMap();
commandMap.register(this.getPlugin().getName().toLowerCase(), new DelegatedBukkitCommand(this));
}
Eco.get().syncCommands();
}
/**
* Unregisters the command from the server.
*/
public final void unregister() {
CommandMap commandMap = getCommandMap();
Eco.get().unregisterCommand(this);
Eco.get().syncCommands();
}
/**
* Get aliases. Leave null if this command is from plugin.yml.
*
* @return The aliases.
*/
@NotNull
public List<String> getAliases() {
return new ArrayList<>();
}
/**
* Get description.
*
* @return The description.
*/
@Nullable
public String getDescription() {
return null;
}
/**
* Internal implementation used to clean up boilerplate.
* Used for parity with {@link CommandExecutor#onCommand(CommandSender, Command, String, String[])}.
*
* @param sender The executor of the command.
* @param command The bukkit command.
* @param label The name of the executed command.
* @param args The arguments of the command (anything after the physical command name)
* @return If the command was processed by the linked {@link EcoPlugin}
*/
@Override
public final boolean onCommand(@NotNull final CommandSender sender,
@NotNull final Command command,
@NotNull final String label,
@NotNull final String[] args) {
if (!command.getName().equalsIgnoreCase(this.getName())) {
return false;
}
this.handle(sender, args);
return true;
public @NotNull String getName() {
return delegate.getName();
}
/**
* Internal implementation used to clean up boilerplate.
* Used for parity with {@link TabCompleter#onTabComplete(CommandSender, Command, String, String[])}.
*
* @param sender The executor of the command.
* @param command The bukkit command.
* @param label The name of the executed command.
* @param args The arguments of the command (anything after the physical command name).
* @return The list of tab-completions.
*/
@Override
public @Nullable List<String> onTabComplete(@NotNull final CommandSender sender,
@NotNull final Command command,
@NotNull final String label,
@NotNull final String[] args) {
if (!command.getName().equalsIgnoreCase(this.getName())) {
return null;
}
return this.handleTabCompletion(sender, args);
public @NotNull String getPermission() {
return delegate.getPermission();
}
/**
* 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!");
}
@Override
public boolean isPlayersOnly() {
return delegate.isPlayersOnly();
}
@Override
public @NotNull CommandBase addSubcommand(@NotNull CommandBase command) {
return delegate.addSubcommand(command);
}
@Override
public @NotNull List<CommandBase> getSubcommands() {
return delegate.getSubcommands();
}
@Override
public @NotNull CommandBase getWrapped() {
return this;
}
@Override
public void register() {
delegate.register();
}
@Override
public void unregister() {
delegate.unregister();
}
@Override
public EcoPlugin getPlugin() {
return delegate.getPlugin();
}
}

View File

@@ -1,13 +1,21 @@
package com.willfp.eco.core.command.impl;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.command.CommandBase;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* Subcommands can be added to PluginCommands or to other Subcommands.
* A command implementation that must exist as a subcommand (i.e. cannot be registered directly).
*/
public abstract class Subcommand extends HandledCommand {
public abstract class Subcommand implements CommandBase {
/**
* The delegate command.
*/
private final CommandBase delegate;
/**
* Create subcommand.
*
@@ -20,7 +28,7 @@ public abstract class Subcommand extends HandledCommand {
@NotNull final String name,
@NotNull final String permission,
final boolean playersOnly) {
super(plugin, name, permission, playersOnly);
this.delegate = Eco.get().createSubcommand(this, plugin, name, permission, playersOnly);
}
/**
@@ -33,6 +41,41 @@ public abstract class Subcommand extends HandledCommand {
protected Subcommand(@NotNull final EcoPlugin plugin,
@NotNull final String name,
@NotNull final CommandBase parent) {
super(plugin, name, parent.getPermission(), parent.isPlayersOnly());
this(plugin, name, parent.getPermission(), parent.isPlayersOnly());
}
@Override
public @NotNull String getName() {
return delegate.getName();
}
@Override
public @NotNull String getPermission() {
return delegate.getPermission();
}
@Override
public boolean isPlayersOnly() {
return delegate.isPlayersOnly();
}
@Override
public @NotNull CommandBase addSubcommand(@NotNull CommandBase command) {
return delegate.addSubcommand(command);
}
@Override
public @NotNull List<CommandBase> getSubcommands() {
return delegate.getSubcommands();
}
@Override
public @NotNull CommandBase getWrapped() {
return this;
}
@Override
public EcoPlugin getPlugin() {
return delegate.getPlugin();
}
}

View File

@@ -6,10 +6,32 @@ import com.willfp.eco.core.config.ConfigType;
import com.willfp.eco.util.StringUtils;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* Default plugin lang.yml.
*/
public class LangYml extends BaseConfig {
/**
* The messages key.
*/
public static final String KEY_MESSAGES = "messages";
/**
* The prefix key.
*/
public static final String KEY_PREFIX = "messages.prefix";
/**
* The no permission key.
*/
public static final String KEY_NO_PERMISSION = "messages.no-permission";
/**
* The not player key.
*/
public static final String KEY_NOT_PLAYER = "messages.not-player";
/**
* Lang.yml.
*
@@ -19,13 +41,31 @@ public class LangYml extends BaseConfig {
super("lang", plugin, false, ConfigType.YAML);
}
/**
* lang.yml requires certain keys to be present.
* <p>
* If the lang.yml does not contain these keys, it is considered to be
* invalid and thus will show a warning in console.
*
* @return If valid.
*/
public boolean isValid() {
for (String key : List.of(KEY_MESSAGES, KEY_PREFIX, KEY_NO_PERMISSION, KEY_NOT_PLAYER)) {
if (!this.has(key)) {
return false;
}
}
return true;
}
/**
* Get the prefix for messages in chat.
*
* @return The prefix.
*/
public String getPrefix() {
return this.getFormattedString("messages.prefix");
return this.getFormattedString(KEY_PREFIX);
}
/**
@@ -34,7 +74,7 @@ public class LangYml extends BaseConfig {
* @return The message.
*/
public String getNoPermission() {
return getPrefix() + this.getFormattedString("messages.no-permission");
return getPrefix() + this.getFormattedString(KEY_NO_PERMISSION);
}
/**
@@ -56,6 +96,6 @@ public class LangYml extends BaseConfig {
*/
public String getMessage(@NotNull final String message,
@NotNull final StringUtils.FormatOption option) {
return getPrefix() + this.getFormattedString("messages." + message, option);
return getPrefix() + this.getFormattedString(KEY_MESSAGES + "." + message, option);
}
}

View File

@@ -134,7 +134,7 @@ public final class PersistentDataKey<T> {
if (this == o) {
return true;
}
if (!(o instanceof PersistentDataKey that)) {
if (!(o instanceof PersistentDataKey<?> that)) {
return false;
}
return Objects.equals(this.getKey(), that.getKey());

View File

@@ -99,7 +99,7 @@ public final class PersistentDataKeyType<T> {
if (this == that) {
return true;
}
if (!(that instanceof PersistentDataKeyType type)) {
if (!(that instanceof PersistentDataKeyType<?> type)) {
return false;
}
return Objects.equals(this.name, type.name);

View File

@@ -33,10 +33,15 @@ public final class McmmoManager {
* @return The bonus drop count.
*/
public static int getBonusDropCount(@NotNull final Block block) {
int finalValue = 0;
for (McmmoIntegration mcmmoIntegration : REGISTERED) {
return mcmmoIntegration.getBonusDropCount(block);
finalValue += mcmmoIntegration.getBonusDropCount(block);
}
return 0;
return finalValue;
}
/**
@@ -47,7 +52,10 @@ public final class McmmoManager {
*/
public static boolean isFake(@NotNull final Event event) {
for (McmmoIntegration mcmmoIntegration : REGISTERED) {
return mcmmoIntegration.isFake(event);
if (mcmmoIntegration.isFake(event)) {
return true;
}
}
return false;
}

View File

@@ -1,13 +1,16 @@
package com.willfp.eco.core.integrations.placeholder;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.placeholder.AdditionalPlayer;
import com.willfp.eco.core.placeholder.DynamicPlaceholder;
import com.willfp.eco.core.placeholder.InjectablePlaceholder;
import com.willfp.eco.core.placeholder.Placeholder;
import com.willfp.eco.core.placeholder.PlaceholderInjectable;
import com.willfp.eco.core.placeholder.PlayerDynamicPlaceholder;
import com.willfp.eco.core.placeholder.PlayerPlaceholder;
import com.willfp.eco.core.placeholder.PlayerStaticPlaceholder;
import com.willfp.eco.core.placeholder.PlayerlessPlaceholder;
@@ -24,6 +27,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
@@ -37,13 +41,20 @@ public final class PlaceholderManager {
/**
* All registered placeholders.
*/
private static final Map<EcoPlugin, Map<String, Placeholder>> REGISTERED_PLACEHOLDERS = new HashMap<>();
private static final Map<EcoPlugin, Map<Pattern, Placeholder>> REGISTERED_PLACEHOLDERS = new HashMap<>();
/**
* All registered placeholder integrations.
*/
private static final Set<PlaceholderIntegration> REGISTERED_INTEGRATIONS = new HashSet<>();
/**
* Placeholder Lookup Cache.
*/
private static final Cache<PlaceholderLookup, Optional<Placeholder>> PLACEHOLDER_LOOKUP_CACHE = Caffeine.newBuilder()
.expireAfterWrite(1, TimeUnit.SECONDS)
.build();
/**
* Placeholder Cache.
*/
@@ -51,10 +62,17 @@ public final class PlaceholderManager {
.expireAfterWrite(50, TimeUnit.MILLISECONDS)
.build(key -> key.entry.getValue(key.player));
/**
* Dynamic Placeholder Cache.
*/
private static final LoadingCache<DynamicEntryWithPlayer, String> DYNAMIC_PLACEHOLDER_CACHE = Caffeine.newBuilder()
.expireAfterWrite(50, TimeUnit.MILLISECONDS)
.build(key -> key.entry.getValue(key.args, key.player));
/**
* The default PlaceholderAPI pattern; brought in for compatibility.
*/
private static final Pattern PATTERN = Pattern.compile("[%]([^% ]+)[%]");
private static final Pattern PATTERN = Pattern.compile("%([^% ]+)%");
/**
* Empty injectable object.
@@ -88,14 +106,17 @@ public final class PlaceholderManager {
* @param placeholder The placeholder to register.
*/
public static void registerPlaceholder(@NotNull final Placeholder placeholder) {
if (placeholder instanceof StaticPlaceholder) {
if (placeholder instanceof StaticPlaceholder || placeholder instanceof PlayerStaticPlaceholder) {
throw new IllegalArgumentException("Static placeholders cannot be registered!");
}
EcoPlugin plugin = placeholder.getPlugin() == null ? Eco.get().getEcoPlugin() : placeholder.getPlugin();
Map<String, Placeholder> pluginPlaceholders = REGISTERED_PLACEHOLDERS
Map<Pattern, Placeholder> pluginPlaceholders = REGISTERED_PLACEHOLDERS
.getOrDefault(plugin, new HashMap<>());
pluginPlaceholders.put(placeholder.getIdentifier(), placeholder);
pluginPlaceholders.put(placeholder.getPattern(), placeholder);
REGISTERED_PLACEHOLDERS.put(plugin, pluginPlaceholders);
}
@@ -136,21 +157,47 @@ public final class PlaceholderManager {
public static String getResult(@Nullable final Player player,
@NotNull final String identifier,
@Nullable final EcoPlugin plugin) {
EcoPlugin owner = plugin == null ? Eco.get().getEcoPlugin() : plugin;
Placeholder placeholder = REGISTERED_PLACEHOLDERS.getOrDefault(owner, new HashMap<>()).get(identifier.toLowerCase());
// This is really janky, and it sucks, but it works so?
// Compensating for regex being slow so that's why we get it.
Placeholder placeholder = PLACEHOLDER_LOOKUP_CACHE.get(
new PlaceholderLookup(identifier, plugin),
(it) -> {
EcoPlugin owner = plugin == null ? Eco.get().getEcoPlugin() : plugin;
if (placeholder == null && plugin != null) {
Placeholder alternate = REGISTERED_PLACEHOLDERS.getOrDefault(Eco.get().getEcoPlugin(), new HashMap<>())
.get(identifier.toLowerCase());
if (alternate != null) {
placeholder = alternate;
}
}
// I hate the streams API.
Optional<Placeholder> found = REGISTERED_PLACEHOLDERS
.getOrDefault(owner, new HashMap<>())
.entrySet()
.stream().filter(entry -> entry.getKey().matcher(identifier).matches())
.map(Map.Entry::getValue)
.findFirst();
if (found.isEmpty() && plugin != null) {
// Here we go again! Something about legacy support? I don't remember.
// I won't touch it though, I'm scared of the placeholder system.
found = REGISTERED_PLACEHOLDERS
.getOrDefault(Eco.get().getEcoPlugin(), new HashMap<>())
.entrySet()
.stream().filter(entry -> entry.getKey().matcher(identifier).matches())
.map(Map.Entry::getValue)
.findFirst();
}
return found;
}
).orElse(null);
if (placeholder == null) {
return "";
}
/*
This code here is *really* not very good. It's mega externalized logic hacked
together and made worse by the addition of dynamic placeholders. But it works,
and it means I don't have to rewrite the whole placeholder system. So it's
good enough for me.
*/
if (placeholder instanceof PlayerPlaceholder playerPlaceholder) {
if (player == null) {
return "";
@@ -159,6 +206,14 @@ public final class PlaceholderManager {
}
} else if (placeholder instanceof PlayerlessPlaceholder playerlessPlaceholder) {
return playerlessPlaceholder.getValue();
} else if (placeholder instanceof PlayerDynamicPlaceholder playerDynamicPlaceholder) {
if (player == null) {
return "";
} else {
return DYNAMIC_PLACEHOLDER_CACHE.get(new DynamicEntryWithPlayer(playerDynamicPlaceholder, identifier, player));
}
} else if (placeholder instanceof DynamicPlaceholder dynamicPlaceholder) {
return dynamicPlaceholder.getValue(identifier);
} else {
return "";
}
@@ -245,8 +300,10 @@ public final class PlaceholderManager {
for (PlaceholderIntegration integration : REGISTERED_INTEGRATIONS) {
processed = integration.translate(processed, player);
}
for (InjectablePlaceholder injection : context.getPlaceholderInjections()) {
// Do I know this is a bad way of doing this? Yes.
// I know it's deprecated, but it's fast.
if (injection instanceof StaticPlaceholder placeholder) {
processed = processed.replace("%" + placeholder.getIdentifier() + "%", placeholder.getValue());
} else if (injection instanceof PlayerStaticPlaceholder placeholder && player != null) {
@@ -280,11 +337,22 @@ public final class PlaceholderManager {
return new ArrayList<>(found);
}
private record PlaceholderLookup(@NotNull String identifier,
@Nullable EcoPlugin plugin) {
}
private record EntryWithPlayer(@NotNull PlayerPlaceholder entry,
@NotNull Player player) {
}
private record DynamicEntryWithPlayer(@NotNull PlayerDynamicPlaceholder entry,
@NotNull String args,
@NotNull Player player) {
}
private PlaceholderManager() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}

View File

@@ -0,0 +1,102 @@
package com.willfp.eco.core.placeholder;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
import java.util.function.Function;
import java.util.regex.Pattern;
/**
* A placeholder that does not require a player and supports dynamic styles.
*/
public final class DynamicPlaceholder implements Placeholder {
/**
* The placeholder pattern.
*/
private final Pattern pattern;
/**
* The function to retrieve the output of the placeholder.
*/
private final Function<String, String> function;
/**
* The plugin for the placeholder.
*/
private final EcoPlugin plugin;
/**
* Create a new dynamic placeholder.
*
* @param plugin The plugin.
* @param pattern The pattern.
* @param function The function to retrieve the value.
*/
public DynamicPlaceholder(@NotNull final EcoPlugin plugin,
@NotNull final Pattern pattern,
@NotNull final Function<String, String> function) {
this.plugin = plugin;
this.pattern = pattern;
this.function = function;
}
/**
* Get the value of the placeholder.
*
* @param args The args.
* @return The value.
*/
@NotNull
public String getValue(@NotNull final String args) {
return function.apply(args);
}
/**
* Register the placeholder.
*
* @return The placeholder.
*/
public DynamicPlaceholder register() {
PlaceholderManager.registerPlaceholder(this);
return this;
}
@Override
public @NotNull EcoPlugin getPlugin() {
return this.plugin;
}
@Override
@Deprecated
public @NotNull String getIdentifier() {
return "dynamic";
}
@NotNull
@Override
public Pattern getPattern() {
return this.pattern;
}
@Override
public boolean equals(@Nullable final Object o) {
if (this == o) {
return true;
}
if (!(o instanceof DynamicPlaceholder that)) {
return false;
}
return Objects.equals(this.getPattern(), that.getPattern())
&& Objects.equals(this.getPlugin(), that.getPlugin());
}
@Override
public int hashCode() {
return Objects.hash(this.getIdentifier(), this.getPlugin());
}
}

View File

@@ -2,13 +2,14 @@ package com.willfp.eco.core.placeholder;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.EcoPlugin;
import org.jetbrains.annotations.NotNull;
/**
* Placeholders that can be injected into {@link PlaceholderInjectable} objects.
*/
public sealed interface InjectablePlaceholder extends Placeholder permits PlayerStaticPlaceholder, StaticPlaceholder {
@Override
default EcoPlugin getPlugin() {
default @NotNull EcoPlugin getPlugin() {
return Eco.get().getEcoPlugin();
}
}

View File

@@ -1,16 +1,22 @@
package com.willfp.eco.core.placeholder;
import com.willfp.eco.core.EcoPlugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.regex.Pattern;
/**
* A placeholder represents a string that can hold a value.
*/
public sealed interface Placeholder permits PlayerPlaceholder, PlayerlessPlaceholder, InjectablePlaceholder {
public sealed interface Placeholder permits PlayerPlaceholder, PlayerlessPlaceholder,
DynamicPlaceholder, PlayerDynamicPlaceholder, InjectablePlaceholder {
/**
* Get the plugin that holds the placeholder.
*
* @return The plugin.
*/
@Nullable
EcoPlugin getPlugin();
/**
@@ -18,5 +24,16 @@ public sealed interface Placeholder permits PlayerPlaceholder, PlayerlessPlaceho
*
* @return The identifier.
*/
@NotNull
String getIdentifier();
/**
* Get the pattern for the placeholder.
*
* @return The pattern.
*/
@NotNull
default Pattern getPattern() {
return Pattern.compile(this.getIdentifier());
}
}

View File

@@ -0,0 +1,105 @@
package com.willfp.eco.core.placeholder;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.regex.Pattern;
/**
* A placeholder that does not require a player and supports dynamic styles.
*/
public final class PlayerDynamicPlaceholder implements Placeholder {
/**
* The placeholder pattern.
*/
private final Pattern pattern;
/**
* The function to retrieve the output of the placeholder.
*/
private final BiFunction<String, Player, String> function;
/**
* The plugin for the placeholder.
*/
private final EcoPlugin plugin;
/**
* Create a new dynamic placeholder.
*
* @param plugin The plugin.
* @param pattern The pattern.
* @param function The function to retrieve the value.
*/
public PlayerDynamicPlaceholder(@NotNull final EcoPlugin plugin,
@NotNull final Pattern pattern,
@NotNull final BiFunction<String, Player, String> function) {
this.plugin = plugin;
this.pattern = pattern;
this.function = function;
}
/**
* Get the value of the placeholder.
*
* @param args The args.
* @param player The player.
* @return The value.
*/
@NotNull
public String getValue(@NotNull final String args,
@NotNull final Player player) {
return function.apply(args, player);
}
/**
* Register the placeholder.
*
* @return The placeholder.
*/
public PlayerDynamicPlaceholder register() {
PlaceholderManager.registerPlaceholder(this);
return this;
}
@Override
public @NotNull EcoPlugin getPlugin() {
return this.plugin;
}
@Override
@Deprecated
public @NotNull String getIdentifier() {
return "dynamic";
}
@NotNull
@Override
public Pattern getPattern() {
return this.pattern;
}
@Override
public boolean equals(@Nullable final Object o) {
if (this == o) {
return true;
}
if (!(o instanceof PlayerDynamicPlaceholder that)) {
return false;
}
return Objects.equals(this.getPattern(), that.getPattern())
&& Objects.equals(this.getPlugin(), that.getPlugin());
}
@Override
public int hashCode() {
return Objects.hash(this.getIdentifier(), this.getPlugin());
}
}

View File

@@ -8,6 +8,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.Objects;
import java.util.function.Function;
import java.util.regex.Pattern;
/**
* A placeholder that requires a player.
@@ -18,6 +19,11 @@ public final class PlayerPlaceholder implements Placeholder {
*/
private final String identifier;
/**
* The placeholder pattern.
*/
private final Pattern pattern;
/**
* The function to retrieve the output of the placeholder given a player.
*/
@@ -40,6 +46,7 @@ public final class PlayerPlaceholder implements Placeholder {
@NotNull final Function<Player, String> function) {
this.plugin = plugin;
this.identifier = identifier;
this.pattern = Pattern.compile(identifier);
this.function = function;
}
@@ -49,6 +56,7 @@ public final class PlayerPlaceholder implements Placeholder {
* @param player The player.
* @return The value.
*/
@NotNull
public String getValue(@NotNull final Player player) {
return function.apply(player);
}
@@ -64,15 +72,21 @@ public final class PlayerPlaceholder implements Placeholder {
}
@Override
public EcoPlugin getPlugin() {
public @NotNull EcoPlugin getPlugin() {
return this.plugin;
}
@Override
public String getIdentifier() {
public @NotNull String getIdentifier() {
return this.identifier;
}
@NotNull
@Override
public Pattern getPattern() {
return this.pattern;
}
@Override
public boolean equals(@Nullable final Object o) {
if (this == o) {

View File

@@ -6,6 +6,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.Objects;
import java.util.function.Function;
import java.util.regex.Pattern;
/**
* A placeholder that cannot be registered, and exists purely in injection.
@@ -16,6 +17,11 @@ public final class PlayerStaticPlaceholder implements InjectablePlaceholder {
*/
private final String identifier;
/**
* The placeholder pattern.
*/
private final Pattern pattern;
/**
* The function to retrieve the output of the placeholder.
*/
@@ -30,6 +36,7 @@ public final class PlayerStaticPlaceholder implements InjectablePlaceholder {
public PlayerStaticPlaceholder(@NotNull final String identifier,
@NotNull final Function<Player, String> function) {
this.identifier = identifier;
this.pattern = Pattern.compile(identifier);
this.function = function;
}
@@ -39,15 +46,22 @@ public final class PlayerStaticPlaceholder implements InjectablePlaceholder {
* @param player The player.
* @return The value.
*/
@NotNull
public String getValue(@NotNull final Player player) {
return function.apply(player);
}
@Override
public String getIdentifier() {
public @NotNull String getIdentifier() {
return this.identifier;
}
@NotNull
@Override
public Pattern getPattern() {
return this.pattern;
}
@Override
public boolean equals(@Nullable final Object o) {
if (this == o) {

View File

@@ -7,16 +7,22 @@ import org.jetbrains.annotations.Nullable;
import java.util.Objects;
import java.util.function.Supplier;
import java.util.regex.Pattern;
/**
* A placeholder that does not require a player.
*/
public final class PlayerlessPlaceholder implements Placeholder {
/**
* The name of the placeholder.
* The placeholder identifier.
*/
private final String identifier;
/**
* The placeholder pattern.
*/
private final Pattern pattern;
/**
* The function to retrieve the output of the placeholder.
*/
@@ -39,6 +45,7 @@ public final class PlayerlessPlaceholder implements Placeholder {
@NotNull final Supplier<String> function) {
this.plugin = plugin;
this.identifier = identifier;
this.pattern = Pattern.compile(identifier);
this.function = function;
}
@@ -62,15 +69,21 @@ public final class PlayerlessPlaceholder implements Placeholder {
}
@Override
public EcoPlugin getPlugin() {
public @NotNull EcoPlugin getPlugin() {
return this.plugin;
}
@Override
public String getIdentifier() {
public @NotNull String getIdentifier() {
return this.identifier;
}
@NotNull
@Override
public Pattern getPattern() {
return this.pattern;
}
@Override
public boolean equals(@Nullable final Object o) {
if (this == o) {

View File

@@ -5,6 +5,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.Objects;
import java.util.function.Supplier;
import java.util.regex.Pattern;
/**
* A placeholder that cannot be registered, and exists purely in injection.
@@ -15,6 +16,11 @@ public final class StaticPlaceholder implements InjectablePlaceholder {
*/
private final String identifier;
/**
* The placeholder pattern.
*/
private final Pattern pattern;
/**
* The function to retrieve the output of the placeholder.
*/
@@ -29,6 +35,7 @@ public final class StaticPlaceholder implements InjectablePlaceholder {
public StaticPlaceholder(@NotNull final String identifier,
@NotNull final Supplier<String> function) {
this.identifier = identifier;
this.pattern = Pattern.compile(identifier);
this.function = function;
}
@@ -37,15 +44,22 @@ public final class StaticPlaceholder implements InjectablePlaceholder {
*
* @return The value.
*/
@NotNull
public String getValue() {
return function.get();
}
@Override
public String getIdentifier() {
public @NotNull String getIdentifier() {
return this.identifier;
}
@NotNull
@Override
public Pattern getPattern() {
return this.pattern;
}
@Override
public boolean equals(@Nullable final Object o) {
if (this == o) {

View File

@@ -71,10 +71,6 @@ public final class Prices {
ctx
);
if (function.apply(context) <= 0) {
return new PriceFree();
}
// Default to economy
if (priceName == null) {
return new PriceEconomy(context, function);

View File

@@ -85,8 +85,8 @@ public record PlayableSound(@NotNull Sound sound,
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);
double pitch = Objects.requireNonNullElse(config.getDoubleOrNull("pitch"), 1.0);
double volume = Objects.requireNonNullElse(config.getDoubleOrNull("volume"), 1.0);
return new PlayableSound(
sound,

View File

@@ -16,6 +16,8 @@ import org.jetbrains.annotations.NotNull;
/**
* Utilities / API methods for item durability.
*/
// Have to suppress casts to ItemMeta because the methods don't exist for some older versions that eco supports.
@SuppressWarnings("RedundantCast")
public final class DurabilityUtils {
/**
* Damage an item in a player's inventory.

View File

@@ -579,7 +579,7 @@ public final class StringUtils {
*
* @param lookup The lookup string.
* @return An array of tokens to be processed.
* @author Shawn (https://stackoverflow.com/questions/70606170/split-a-list-on-spaces-and-group-quoted-characters/70606653#70606653)
* @author Shawn (<a href="https://stackoverflow.com/questions/70606170/split-a-list-on-spaces-and-group-quoted-characters/70606653#70606653">...</a>)
*/
@NotNull
public static String[] parseTokens(@NotNull final String lookup) {

View File

@@ -4,9 +4,14 @@ package com.willfp.eco.core.commands
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.command.CommandBase
import com.willfp.eco.core.command.NotificationException
import com.willfp.eco.core.command.impl.PluginCommand
import com.willfp.eco.core.command.impl.Subcommand
import org.bukkit.Bukkit
import org.bukkit.OfflinePlayer
import org.bukkit.command.CommandSender
import org.bukkit.entity.Player
import java.util.function.Predicate
/**
* Helper class for creating commands with builders.
@@ -140,3 +145,92 @@ fun CommandBase.addSubcommand(
init(command)
return command
}
/**
* Throws an exception containing a langYml key if obj is null.
* <p>The {@link CommandBase#onExecute(CommandSender, List) onExecute } in PluginCommand and Subcommand
* automatically handles sending the message to the sender.</p>
* <br>
* @param key key of notification message in langYml
* @return Returns the object given or throws an exception
* @throws NotificationException exception thrown when null
*/
fun <T> T.notifyNull(key: String): T {
return this ?: throw NotificationException(key)
}
/**
* Throws an exception containing a langYml key if predicate tests false
* <p>The {@link CommandBase#onExecute(CommandSender, List) onExecute } in PluginCommand and Subcommand
* automatically handles sending the message to the sender.</p>
* <br>
* @param predicate predicate to test
* @param key key of notification message in langYml
* @param <T> the generic type of object
* @return Returns the object given or throws an exception
*/
fun <T> T.notifyFalse(predicate: Predicate<T>, key: String): T {
predicate.test(this).notifyFalse(key)
return this
}
/**
* Throws an exception containing a langYml key if condition is false.
* <p>The {@link CommandBase#onExecute(CommandSender, List) onExecute } in PluginCommand and Subcommand
* automatically handles sending the message to the sender.</p>
* <br>
* @param key value in the langYml
* @return Returns the condition given or throws an exception
* @throws NotificationException exception thrown when false
*/
fun Boolean?.notifyFalse(key: String): Boolean {
return if (this == true) true else throw NotificationException(key)
}
/**
* Throws an exception containing a langYml key if Bukkit.getPlayer(playerName) is null.
* <p>The {@link CommandBase#onExecute(CommandSender, List) onExecute } in PluginCommand and Subcommand
* automatically handles sending the message to the sender.</p>
* <br>
* @param key value in the langYml
* @return Returns the player
* @throws NotificationException exception thrown when invalid playerName
*/
fun String?.notifyPlayerRequired(key: String): Player {
return Bukkit.getPlayer(this ?: "") ?: throw NotificationException(key)
}
/**
* Throws an exception containing a langYml key if Bukkit.getPlayer(playerName) is null.
* <p>The {@link CommandBase#onExecute(CommandSender, List) onExecute } in PluginCommand and Subcommand
* automatically handles sending the message to the sender.</p>
* <br>
* @param key value in the langYml
* @return Returns the player
* @throws NotificationException exception thrown when invalid playerName
*/
fun String?.notifyOfflinePlayerRequired(key: String): OfflinePlayer {
@Suppress("DEPRECATION")
val player = Bukkit.getOfflinePlayer(this ?: "")
if (!player.hasPlayedBefore() && !player.isOnline) {
throw NotificationException(key)
}
return player
}
/**
* Throws an exception containing a langYml key if player doesn't have permission.
* <p>The {@link CommandBase#onExecute(CommandSender, List) onExecute } in PluginCommand and Subcommand
* automatically handles sending the message to the sender.</p>
* <br>
* @param permission the permission
* @param key value in the langYml
* @return Returns the player
* @throws NotificationException exception thrown when player doesn't have permission
*/
fun Player?.notifyPermissionRequired(permission: String, key: String): Player {
this ?: throw NotificationException(key)
return this.notifyFalse({ this.hasPermission(permission) }, key)
}

View File

@@ -83,7 +83,6 @@ fun SlotBuilder.setCaptiveFilter(test: (Player, Menu, ItemStack?) -> Boolean): S
* @deprecated Use SlotUpdater instead.
*/
@Deprecated("Use SlotUpdater instead")
@Suppress("DEPRECATION")
fun SlotBuilder.setModifier(action: (Player, Menu, item: ItemStack) -> Unit): SlotBuilder =
this.setUpdater { a, b, c -> c.apply { action(a, b, c) } }

View File

@@ -0,0 +1,28 @@
package com.willfp.eco.internal.command
import org.bukkit.command.Command
import org.bukkit.command.CommandSender
import org.bukkit.command.PluginIdentifiableCommand
import org.bukkit.command.TabCompleter
class DelegatedBukkitCommand(
private val delegate: EcoPluginCommand
) : Command(delegate.name), TabCompleter, PluginIdentifiableCommand {
override fun execute(sender: CommandSender, label: String, args: Array<out String>?): Boolean {
return false
}
override fun onTabComplete(
sender: CommandSender,
command: Command,
label: String,
args: Array<out String>?
): List<String> {
return mutableListOf() // Mutable in case bukkit requires this (I haven't checked.)
}
override fun getPlugin() = delegate.plugin
override fun getPermission() = delegate.permission
override fun getDescription() = delegate.description ?: ""
override fun getAliases(): List<String> = delegate.aliases
}

View File

@@ -0,0 +1,52 @@
package com.willfp.eco.internal.command
import com.willfp.eco.core.Eco
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.command.CommandBase
import com.willfp.eco.core.command.PluginCommandBase
import org.bukkit.Bukkit
class EcoPluginCommand(
parentDelegate: CommandBase,
plugin: EcoPlugin,
name: String,
permission: String,
playersOnly: Boolean
) : HandledCommand(parentDelegate, plugin, name, permission, playersOnly),
PluginCommandBase {
override fun register() {
val command = Bukkit.getPluginCommand(name)
if (command == null) {
unregister()
commandMap.register(plugin.name.lowercase(), DelegatedBukkitCommand(this))
} else {
command.setExecutor(this)
description?.let {
command.setDescription(it)
}
if (aliases.isNotEmpty()) {
command.aliases = aliases
}
}
}
override fun unregister() {
val found = commandMap.getCommand(name)
found?.unregister(commandMap)
Eco.get().syncCommands()
}
}
class EcoSubcommand(
parentDelegate: CommandBase,
plugin: EcoPlugin,
name: String,
permission: String,
playersOnly: Boolean
) : HandledCommand(parentDelegate, plugin, name, permission, playersOnly)

View File

@@ -0,0 +1,183 @@
package com.willfp.eco.internal.command
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.command.CommandBase
import com.willfp.eco.core.command.NotificationException
import com.willfp.eco.core.config.base.LangYml
import org.bukkit.Bukkit
import org.bukkit.command.Command
import org.bukkit.command.CommandExecutor
import org.bukkit.command.CommandMap
import org.bukkit.command.CommandSender
import org.bukkit.command.TabCompleter
import org.bukkit.entity.Player
import org.bukkit.util.StringUtil
/**
* Abstract class for commands that can be handled.
* <p>
* Handled commands have a method to pass in raw input from bukkit commands
* in order to execute the command-specific code.
*/
abstract class HandledCommand(
private val parentDelegate: CommandBase,
private val plugin: EcoPlugin,
private val name: String,
private val permission: String,
private val playersOnly: Boolean
) : CommandBase, CommandExecutor, TabCompleter {
private val subcommands = mutableListOf<CommandBase>()
override fun onCommand(
sender: CommandSender,
command: Command,
label: String,
args: Array<out String>?
): Boolean {
if (!command.name.equals(name, true)) {
return false
}
if (args != null) {
handleExecution(sender, args.toList())
}
return true
}
override fun onTabComplete(
sender: CommandSender,
command: Command,
label: String,
args: Array<out String>?
): MutableList<String>? {
return handleTabComplete(sender, args?.toList() ?: listOf()).toMutableList()
}
override fun getPlugin() = this.plugin
override fun getName() = this.name
override fun getPermission() = this.permission
override fun isPlayersOnly() = this.playersOnly
override fun getSubcommands() = this.subcommands
override fun getWrapped() = this.parentDelegate
override fun addSubcommand(command: CommandBase): CommandBase {
subcommands.add(command)
return this
}
override fun onExecute(sender: CommandSender, args: List<String>) = parentDelegate.onExecute(sender, args)
override fun onExecute(sender: Player, args: List<String>) = parentDelegate.onExecute(sender, args)
override fun tabComplete(sender: CommandSender, args: List<String>): List<String> =
parentDelegate.tabComplete(sender, args)
override fun tabComplete(sender: Player, args: List<String>): List<String> =
parentDelegate.tabComplete(sender, args)
/**
* Handle the command.
*
* @param sender The sender.
* @param args The arguments.
*/
private fun CommandBase.handleExecution(sender: CommandSender, args: List<String>) {
if (!sender.canExecute(this, plugin)) {
return
}
if (args.isNotEmpty()) {
for (subcommand in subcommands) {
if (subcommand.name.equals(args[0], true)) {
if (!sender.canExecute(subcommand, plugin)) {
return
}
subcommand.handleExecution(sender, args.subList(1, args.size))
return
}
}
}
try {
notifyFalse(!isPlayersOnly || sender is Player, LangYml.KEY_NOT_PLAYER)
onExecute(sender, args)
if (sender is Player) {
onExecute(sender, args)
}
} catch (e: NotificationException) {
sender.sendMessage(plugin.langYml.getMessage(e.key))
return
}
}
/**
* Handle the tab completion.
*
* @param sender The sender.
* @param args The arguments.
* @return The tab completion results.
*/
private fun CommandBase.handleTabComplete(sender: CommandSender, args: List<String>): List<String> {
if (!sender.hasPermission(permission)) return emptyList()
if (args.size == 1) {
val completions = subcommands.filter { sender.hasPermission(it.permission) }.map { it.name }
val list = mutableListOf<String>()
StringUtil.copyPartialMatches(args[0], completions, list)
if (completions.isNotEmpty()) {
return completions
}
}
if (args.size >= 2) {
val matchingCommand =
subcommands.firstOrNull {
sender.hasPermission(it.permission) && it.name.equals(args[0], true)
}
if (matchingCommand != null) {
val completions = matchingCommand.handleTabComplete(sender, args.subList(1, args.size)).toMutableList()
if (sender is Player) {
completions.addAll(matchingCommand.handleTabComplete(sender, args.subList(1, args.size)))
}
return completions
}
}
val completions = tabComplete(sender, args).toMutableList()
if (sender is Player) {
completions.addAll(tabComplete(sender, args))
}
return completions.sorted()
}
}
val commandMap: CommandMap
get() = try {
val fCommandMap = Bukkit.getServer().javaClass.getDeclaredField("commandMap")
fCommandMap.trySetAccessible()
fCommandMap.get(Bukkit.getServer()) as CommandMap
} catch (e: Exception) {
throw NullPointerException("Command map wasn't found!")
}
fun CommandSender.canExecute(command: CommandBase, plugin: EcoPlugin): Boolean {
if (!this.hasPermission(command.permission) && this is Player) {
this.sendMessage(plugin.langYml.noPermission)
return false
}
return true
}

View File

@@ -77,7 +77,7 @@ private abstract class ConfigTypeHandler(
val type: ConfigType
) {
fun toMap(input: String?): Map<String, Any?> {
if (input == null || input.isBlank()) {
if (input.isNullOrBlank()) {
return emptyMap()
}

View File

@@ -3,7 +3,6 @@ package com.willfp.eco.internal.config
import com.willfp.eco.core.config.ConfigType
import com.willfp.eco.core.placeholder.InjectablePlaceholder
@Suppress("UNCHECKED_CAST")
class EcoConfigSection(
type: ConfigType,
values: Map<String, Any?> = emptyMap(),

View File

@@ -3,6 +3,7 @@ package com.willfp.eco.internal.gui
import com.willfp.eco.core.gui.menu.Menu
import org.bukkit.entity.Player
// To check in the future, I'm not sure if this is necessary functionality at all.
class MergedStateMenu(
private val base: Menu,
private val additional: Menu

View File

@@ -21,7 +21,8 @@ object ArgParserName : LookupArgParser {
val formatted = StringUtils.format(name)
@Suppress("UsePropertyAccessSyntax")
// I don't know why it says it's redundant, the compiler yells at me
@Suppress("UsePropertyAccessSyntax", "RedundantSuppression")
meta.setDisplayName(formatted)
return Predicate {

View File

@@ -25,18 +25,19 @@ object PriceFactoryXP : PriceFactory {
) : Price {
private val multipliers = mutableMapOf<UUID, Double>()
override fun canAfford(player: Player) = player.totalExperience >= getValue(player)
override fun canAfford(player: Player, multiplier: Double): Boolean =
player.totalExperience >= getValue(player, multiplier)
override fun pay(player: Player) {
player.totalExperience -= getValue(player).roundToInt()
override fun pay(player: Player, multiplier: Double) {
player.totalExperience -= getValue(player, multiplier).roundToInt()
}
override fun giveTo(player: Player) {
player.totalExperience += getValue(player).roundToInt()
override fun giveTo(player: Player, multiplier: Double) {
player.totalExperience += getValue(player, multiplier).roundToInt()
}
override fun getValue(player: Player): Double {
return xp(MathContext.copyWithPlayer(baseContext, player)) * getMultiplier(player)
override fun getValue(player: Player, multiplier: Double): Double {
return xp(MathContext.copyWithPlayer(baseContext, player)) * getMultiplier(player) * multiplier
}
override fun getMultiplier(player: Player): Double {

View File

@@ -26,18 +26,18 @@ object PriceFactoryXPLevels : PriceFactory {
) : Price {
private val multipliers = mutableMapOf<UUID, Double>()
override fun canAfford(player: Player) = player.level >= getValue(player)
override fun canAfford(player: Player, multiplier: Double) = player.level >= getValue(player, multiplier)
override fun pay(player: Player) {
player.level -= getValue(player).roundToInt()
override fun pay(player: Player, multiplier: Double) {
player.level -= getValue(player, multiplier).roundToInt()
}
override fun giveTo(player: Player) {
player.level += getValue(player).roundToInt()
override fun giveTo(player: Player, multiplier: Double) {
player.level += getValue(player, multiplier).roundToInt()
}
override fun getValue(player: Player): Double {
return level(MathContext.copyWithPlayer(baseContext, player)) * getMultiplier(player)
override fun getValue(player: Player, multiplier: Double): Double {
return level(MathContext.copyWithPlayer(baseContext, player)) * getMultiplier(player) * multiplier
}
override fun getMultiplier(player: Player): Double {

View File

@@ -4,7 +4,6 @@ import com.willfp.eco.core.entities.ai.EntityGoal
import com.willfp.eco.core.entities.ai.TargetGoal
import com.willfp.eco.internal.spigot.proxy.common.ai.EntityGoalFactory
import com.willfp.eco.internal.spigot.proxy.common.ai.TargetGoalFactory
import net.minecraft.core.Registry
import net.minecraft.nbt.CompoundTag
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.LivingEntity
@@ -48,17 +47,14 @@ private val MATERIAL_TO_ITEM = mutableMapOf<Material, Item>()
fun Material.toItem(): Item =
MATERIAL_TO_ITEM.getOrPut(this) {
Registry.ITEM.getOptional(this.key.toResourceLocation())
.orElseThrow { IllegalArgumentException("Material is not item!") }
impl.materialToItem(this)
}
private val ITEM_TO_MATERIAL = mutableMapOf<Item, Material>()
fun Item.toMaterial(): Material =
ITEM_TO_MATERIAL.getOrPut(this) {
val material = Material.getMaterial(Registry.ITEM.getKey(this).path.uppercase())
?: throw IllegalArgumentException("Invalid material!")
material
impl.itemToMaterial(this)
}
fun CompoundTag.makePdc(base: Boolean = false): PersistentDataContainer =
@@ -94,6 +90,10 @@ interface CommonsProvider {
return null
}
fun materialToItem(material: Material): Item
fun itemToMaterial(item: Item): Material
companion object {
fun setIfNeeded(provider: CommonsProvider) {
if (::impl.isInitialized) {

View File

@@ -39,7 +39,6 @@ private class EnhancedInteractGoal(
return if (mob.random.nextFloat() >= probability) {
false
} else {
@Suppress("SENSELESS_COMPARISON")
if (mob.target != null) {
lookAt = mob.target
}

View File

@@ -32,7 +32,6 @@ private class EnhancedHurtByTargetGoal(
return false
}
@Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS")
return super.canAttack(target, targetPredicate)
}
}

View File

@@ -192,7 +192,7 @@ class EcoFastItemStack(
apply()
}
@Suppress("UNNECESSARY_NOT_NULL_ASSERTION")
@Suppress("RedundantSuppression", "UNNECESSARY_NOT_NULL_ASSERTION")
private var flagBits: Byte
get() =
if (handle.hasTag() && handle.getTag()!!.contains(
@@ -255,7 +255,7 @@ class EcoFastItemStack(
}
override fun hashCode(): Int {
@Suppress("UNNECESSARY_SAFE_CALL")
@Suppress("RedundantSuppression", "UNNECESSARY_SAFE_CALL")
return handle.getTag()?.hashCode() ?: (0b00010101 * 31 + Item.getId(handle.getItem()))
}

View File

@@ -2,11 +2,15 @@ package com.willfp.eco.internal.spigot.proxy.v1_17_R1
import com.willfp.eco.internal.spigot.proxy.CommonsInitializerProxy
import com.willfp.eco.internal.spigot.proxy.common.CommonsProvider
import com.willfp.eco.internal.spigot.proxy.common.toResourceLocation
import net.minecraft.core.Registry
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.Tag
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.item.Item
import org.bukkit.Bukkit
import org.bukkit.Material
import org.bukkit.NamespacedKey
import org.bukkit.craftbukkit.v1_17_R1.CraftServer
import org.bukkit.craftbukkit.v1_17_R1.entity.CraftEntity
@@ -131,5 +135,13 @@ class CommonsInitializer : CommonsInitializerProxy {
}
}
}
override fun materialToItem(material: Material): Item =
Registry.ITEM.getOptional(material.key.toResourceLocation())
.orElseThrow { IllegalArgumentException("Material is not item!") }
override fun itemToMaterial(item: Item) =
Material.getMaterial(Registry.ITEM.getKey(item).path.uppercase())
?: throw IllegalArgumentException("Invalid material!")
}
}

View File

@@ -12,7 +12,6 @@ import org.bukkit.persistence.PersistentDataContainer
import org.bukkit.persistence.PersistentDataType
class ExtendedPersistentDataContainerFactory : ExtendedPersistentDataContainerFactoryProxy {
@Suppress("UNCHECKED_CAST")
private val registry: CraftPersistentDataTypeRegistry
init {
@@ -38,7 +37,7 @@ class ExtendedPersistentDataContainerFactory : ExtendedPersistentDataContainerFa
}
inner class EcoPersistentDataContainer(
val handle: CraftPersistentDataContainer
private val handle: CraftPersistentDataContainer
) : ExtendedPersistentDataContainer {
@Suppress("UNCHECKED_CAST")
private val customDataTags: MutableMap<String, Tag> =

View File

@@ -2,11 +2,15 @@ package com.willfp.eco.internal.spigot.proxy.v1_18_R1
import com.willfp.eco.internal.spigot.proxy.CommonsInitializerProxy
import com.willfp.eco.internal.spigot.proxy.common.CommonsProvider
import com.willfp.eco.internal.spigot.proxy.common.toResourceLocation
import net.minecraft.core.Registry
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.Tag
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.item.Item
import org.bukkit.Bukkit
import org.bukkit.Material
import org.bukkit.NamespacedKey
import org.bukkit.craftbukkit.v1_18_R1.CraftServer
import org.bukkit.craftbukkit.v1_18_R1.entity.CraftEntity
@@ -131,5 +135,13 @@ class CommonsInitializer : CommonsInitializerProxy {
}
}
}
override fun materialToItem(material: Material): Item =
Registry.ITEM.getOptional(material.key.toResourceLocation())
.orElseThrow { IllegalArgumentException("Material is not item!") }
override fun itemToMaterial(item: Item) =
Material.getMaterial(Registry.ITEM.getKey(item).path.uppercase())
?: throw IllegalArgumentException("Invalid material!")
}
}

View File

@@ -12,7 +12,6 @@ import org.bukkit.persistence.PersistentDataContainer
import org.bukkit.persistence.PersistentDataType
class ExtendedPersistentDataContainerFactory : ExtendedPersistentDataContainerFactoryProxy {
@Suppress("UNCHECKED_CAST")
private val registry: CraftPersistentDataTypeRegistry
init {
@@ -38,7 +37,7 @@ class ExtendedPersistentDataContainerFactory : ExtendedPersistentDataContainerFa
}
inner class EcoPersistentDataContainer(
val handle: CraftPersistentDataContainer
private val handle: CraftPersistentDataContainer
) : ExtendedPersistentDataContainer {
@Suppress("UNCHECKED_CAST")
private val customDataTags: MutableMap<String, Tag> =

View File

@@ -2,11 +2,15 @@ package com.willfp.eco.internal.spigot.proxy.v1_18_R2
import com.willfp.eco.internal.spigot.proxy.CommonsInitializerProxy
import com.willfp.eco.internal.spigot.proxy.common.CommonsProvider
import com.willfp.eco.internal.spigot.proxy.common.toResourceLocation
import net.minecraft.core.Registry
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.Tag
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.item.Item
import org.bukkit.Bukkit
import org.bukkit.Material
import org.bukkit.NamespacedKey
import org.bukkit.craftbukkit.v1_18_R2.CraftServer
import org.bukkit.craftbukkit.v1_18_R2.entity.CraftEntity
@@ -131,5 +135,13 @@ class CommonsInitializer : CommonsInitializerProxy {
}
}
}
override fun materialToItem(material: Material): Item =
Registry.ITEM.getOptional(material.key.toResourceLocation())
.orElseThrow { IllegalArgumentException("Material is not item!") }
override fun itemToMaterial(item: Item) =
Material.getMaterial(Registry.ITEM.getKey(item).path.uppercase())
?: throw IllegalArgumentException("Invalid material!")
}
}

View File

@@ -12,7 +12,6 @@ import org.bukkit.persistence.PersistentDataContainer
import org.bukkit.persistence.PersistentDataType
class ExtendedPersistentDataContainerFactory : ExtendedPersistentDataContainerFactoryProxy {
@Suppress("UNCHECKED_CAST")
private val registry: CraftPersistentDataTypeRegistry
init {
@@ -38,7 +37,7 @@ class ExtendedPersistentDataContainerFactory : ExtendedPersistentDataContainerFa
}
inner class EcoPersistentDataContainer(
val handle: CraftPersistentDataContainer
private val handle: CraftPersistentDataContainer
) : ExtendedPersistentDataContainer {
@Suppress("UNCHECKED_CAST")
private val customDataTags: MutableMap<String, Tag> =

View File

@@ -2,11 +2,15 @@ package com.willfp.eco.internal.spigot.proxy.v1_19_R1
import com.willfp.eco.internal.spigot.proxy.CommonsInitializerProxy
import com.willfp.eco.internal.spigot.proxy.common.CommonsProvider
import com.willfp.eco.internal.spigot.proxy.common.toResourceLocation
import net.minecraft.core.Registry
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.Tag
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.item.Item
import org.bukkit.Bukkit
import org.bukkit.Material
import org.bukkit.NamespacedKey
import org.bukkit.craftbukkit.v1_19_R1.CraftServer
import org.bukkit.craftbukkit.v1_19_R1.entity.CraftEntity
@@ -131,5 +135,13 @@ class CommonsInitializer : CommonsInitializerProxy {
}
}
}
override fun materialToItem(material: Material): Item =
Registry.ITEM.getOptional(material.key.toResourceLocation())
.orElseThrow { IllegalArgumentException("Material is not item!") }
override fun itemToMaterial(item: Item) =
Material.getMaterial(Registry.ITEM.getKey(item).path.uppercase())
?: throw IllegalArgumentException("Invalid material!")
}
}

View File

@@ -12,7 +12,6 @@ import org.bukkit.persistence.PersistentDataContainer
import org.bukkit.persistence.PersistentDataType
class ExtendedPersistentDataContainerFactory : ExtendedPersistentDataContainerFactoryProxy {
@Suppress("UNCHECKED_CAST")
private val registry: CraftPersistentDataTypeRegistry
init {
@@ -38,7 +37,7 @@ class ExtendedPersistentDataContainerFactory : ExtendedPersistentDataContainerFa
}
inner class EcoPersistentDataContainer(
val handle: CraftPersistentDataContainer
private val handle: CraftPersistentDataContainer
) : ExtendedPersistentDataContainer {
@Suppress("UNCHECKED_CAST")
private val customDataTags: MutableMap<String, Tag> =

View File

@@ -2,11 +2,15 @@ package com.willfp.eco.internal.spigot.proxy.v1_19_R2
import com.willfp.eco.internal.spigot.proxy.CommonsInitializerProxy
import com.willfp.eco.internal.spigot.proxy.common.CommonsProvider
import com.willfp.eco.internal.spigot.proxy.common.toResourceLocation
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.Tag
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.item.Item
import org.bukkit.Bukkit
import org.bukkit.Material
import org.bukkit.NamespacedKey
import org.bukkit.craftbukkit.v1_19_R2.CraftServer
import org.bukkit.craftbukkit.v1_19_R2.entity.CraftEntity
@@ -121,7 +125,7 @@ class CommonsInitializer : CommonsInitializerProxy {
tag.merge(container.toTag())
} else {
item.setTag(null)
item.tag = null
}
} else {
if (container != null && !container.isEmpty) {
@@ -131,5 +135,13 @@ class CommonsInitializer : CommonsInitializerProxy {
}
}
}
override fun materialToItem(material: Material): Item =
BuiltInRegistries.ITEM.getOptional(material.key.toResourceLocation())
.orElseThrow { IllegalArgumentException("Material is not item!") }
override fun itemToMaterial(item: Item) =
Material.getMaterial(BuiltInRegistries.ITEM.getKey(item).path.uppercase())
?: throw IllegalArgumentException("Invalid material!")
}
}

View File

@@ -12,7 +12,6 @@ import org.bukkit.persistence.PersistentDataContainer
import org.bukkit.persistence.PersistentDataType
class ExtendedPersistentDataContainerFactory : ExtendedPersistentDataContainerFactoryProxy {
@Suppress("UNCHECKED_CAST")
private val registry: CraftPersistentDataTypeRegistry
init {
@@ -38,7 +37,7 @@ class ExtendedPersistentDataContainerFactory : ExtendedPersistentDataContainerFa
}
inner class EcoPersistentDataContainer(
val handle: CraftPersistentDataContainer
private val handle: CraftPersistentDataContainer
) : ExtendedPersistentDataContainer {
@Suppress("UNCHECKED_CAST")
private val customDataTags: MutableMap<String, Tag> =

View File

@@ -31,7 +31,8 @@ dependencies {
compileOnly('com.github.TownyAdvanced:Towny:0.97.2.6') {
exclude group: 'com.zaxxer', module: 'HikariCP'
}
compileOnly 'com.github.angeschossen:LandsAPI:5.15.2'
compileOnly 'com.github.angeschossen:LandsAPI:6.26.18'
compileOnly 'com.github.angeschossen:PluginFrameworkAPI:1.0.0'
compileOnly 'fr.neatmonster:nocheatplus:3.16.1-SNAPSHOT'
compileOnly 'com.github.jiangdashao:matrix-api-repo:317d4635fd'
compileOnly 'com.gmail.nossr50.mcMMO:mcMMO:2.1.202'
@@ -47,14 +48,15 @@ dependencies {
compileOnly 'com.github.WhipDevelopment:CrashClaim:f9cd7d92eb'
compileOnly 'com.wolfyscript.wolfyutilities:wolfyutilities:3.16.0.0'
compileOnly 'com.github.decentsoftware-eu:decentholograms:2.1.2'
compileOnly 'com.github.Gypopo:EconomyShopGUI-API:1.1.0'
compileOnly 'com.github.Gypopo:EconomyShopGUI-API:1.4.6'
compileOnly 'com.github.N0RSKA:ScytherAPI:55a'
compileOnly 'com.ticxo.modelengine:api:R3.0.1'
compileOnly 'me.TechsCode:UltraEconomyAPI:1.0.0'
compileOnly 'com.github.Ssomar-Developement:SCore:3.4.7'
// MythicMobs
compileOnly 'io.lumine:Mythic:5.0.1'
compileOnly 'io.lumine:LumineUtils:1.16.1-SNAPSHOT'
compileOnly 'io.lumine:Mythic:5.2.1'
compileOnly 'io.lumine:LumineUtils:1.19-SNAPSHOT'
// CombatLogX V10 + NewbieHelper Expansion
compileOnly 'com.SirBlobman.combatlogx:CombatLogX-API:10.0.0.0-SNAPSHOT'

View File

@@ -4,6 +4,7 @@ import com.willfp.eco.core.Eco
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.PluginLike
import com.willfp.eco.core.PluginProps
import com.willfp.eco.core.command.CommandBase
import com.willfp.eco.core.command.impl.PluginCommand
import com.willfp.eco.core.config.ConfigType
import com.willfp.eco.core.config.interfaces.Config
@@ -14,11 +15,9 @@ import com.willfp.eco.core.gui.slot.functional.SlotProvider
import com.willfp.eco.core.items.Items
import com.willfp.eco.core.math.MathContext
import com.willfp.eco.internal.EcoPropsParser
import com.willfp.eco.internal.config.EcoConfigHandler
import com.willfp.eco.internal.config.EcoConfigSection
import com.willfp.eco.internal.config.EcoLoadableConfig
import com.willfp.eco.internal.config.EcoUpdatableConfig
import com.willfp.eco.internal.config.toMap
import com.willfp.eco.internal.command.EcoPluginCommand
import com.willfp.eco.internal.command.EcoSubcommand
import com.willfp.eco.internal.config.*
import com.willfp.eco.internal.drops.EcoDropQueue
import com.willfp.eco.internal.drops.EcoFastCollatedDropQueue
import com.willfp.eco.internal.events.EcoEventManager
@@ -43,6 +42,7 @@ import com.willfp.eco.internal.spigot.data.ProfileHandler
import com.willfp.eco.internal.spigot.data.storage.HandlerType
import com.willfp.eco.internal.spigot.integrations.bstats.MetricHandler
import com.willfp.eco.internal.spigot.math.evaluateExpression
import com.willfp.eco.internal.spigot.proxy.BukkitCommandsProxy
import com.willfp.eco.internal.spigot.proxy.CommonsInitializerProxy
import com.willfp.eco.internal.spigot.proxy.DummyEntityFactoryProxy
import com.willfp.eco.internal.spigot.proxy.EntityControllerFactoryProxy
@@ -51,11 +51,9 @@ import com.willfp.eco.internal.spigot.proxy.FastItemStackFactoryProxy
import com.willfp.eco.internal.spigot.proxy.MiniMessageTranslatorProxy
import com.willfp.eco.internal.spigot.proxy.SNBTConverterProxy
import com.willfp.eco.internal.spigot.proxy.SkullProxy
import com.willfp.eco.internal.spigot.proxy.BukkitCommandsProxy
import com.willfp.eco.internal.spigot.proxy.TPSProxy
import org.bukkit.Location
import org.bukkit.NamespacedKey
import org.bukkit.command.Command
import org.bukkit.command.CommandMap
import org.bukkit.configuration.ConfigurationSection
import org.bukkit.entity.Entity
@@ -65,7 +63,7 @@ import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.SkullMeta
import org.bukkit.persistence.PersistentDataContainer
import java.net.URLClassLoader
import java.util.UUID
import java.util.*
private val loadedEcoPlugins = mutableMapOf<String, EcoPlugin>()
@@ -169,8 +167,37 @@ class EcoImpl : EcoSpigotPlugin(), Eco {
return config
}
override fun createDropQueue(player: Player) = if (this.configYml.getBool("use-fast-collated-drops"))
EcoFastCollatedDropQueue(player) else EcoDropQueue(player)
override fun createPluginCommand(
parentDelegate: CommandBase,
plugin: EcoPlugin,
name: String,
permission: String,
playersOnly: Boolean
) = EcoPluginCommand(
parentDelegate,
plugin,
name,
permission,
playersOnly
)
override fun createSubcommand(
parentDelegate: CommandBase,
plugin: EcoPlugin,
name: String,
permission: String,
playersOnly: Boolean
) = EcoSubcommand(
parentDelegate,
plugin,
name,
permission,
playersOnly
)
override fun createDropQueue(player: Player) =
if (this.configYml.getBool("use-fast-collated-drops"))
EcoFastCollatedDropQueue(player) else EcoDropQueue(player)
override fun getRegisteredPersistentDataKeys() =
KeyRegistry.getRegisteredKeys()

View File

@@ -87,6 +87,7 @@ import com.willfp.eco.internal.spigot.integrations.antigrief.AntigriefCombatLogX
import com.willfp.eco.internal.spigot.integrations.antigrief.AntigriefCombatLogXV11
import com.willfp.eco.internal.spigot.integrations.antigrief.AntigriefCrashClaim
import com.willfp.eco.internal.spigot.integrations.antigrief.AntigriefDeluxeCombat
import com.willfp.eco.internal.spigot.integrations.antigrief.AntigriefFabledSkyBlock
import com.willfp.eco.internal.spigot.integrations.antigrief.AntigriefFactionsUUID
import com.willfp.eco.internal.spigot.integrations.antigrief.AntigriefGriefPrevention
import com.willfp.eco.internal.spigot.integrations.antigrief.AntigriefIridiumSkyblock
@@ -285,6 +286,7 @@ abstract class EcoSpigotPlugin : EcoPlugin() {
}
},
IntegrationLoader("PvPManager") { AntigriefManager.register(AntigriefPvPManager()) },
IntegrationLoader("FabledSkyblock") { AntigriefManager.register(AntigriefFabledSkyBlock()) },
// Anticheat
IntegrationLoader("AAC5") { AnticheatManager.register(AnticheatAAC()) },
@@ -314,6 +316,7 @@ abstract class EcoSpigotPlugin : EcoPlugin() {
IntegrationLoader("zShop") { ShopManager.register(ShopZShop()) },
IntegrationLoader("DeluxeSellwands") { ShopManager.register(ShopDeluxeSellwands()) },
IntegrationLoader("EconomyShopGUI") { ShopManager.register(ShopEconomyShopGUI()) },
IntegrationLoader("EconomyShopGUI-Premium") { ShopManager.register(ShopEconomyShopGUI()) },
// Hologram
IntegrationLoader("HolographicDisplays") { HologramManager.register(HologramHolographicDisplays(this)) },

View File

@@ -9,7 +9,6 @@ import org.bukkit.entity.Monster
import org.bukkit.entity.Player
class AntigriefFabledSkyBlock : AntigriefIntegration {
override fun getPluginName(): String {
return "FabledSkyBlock"
}

View File

@@ -2,9 +2,10 @@ package com.willfp.eco.internal.spigot.integrations.antigrief
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.integrations.antigrief.AntigriefIntegration
import me.angeschossen.lands.api.flags.Flags
import me.angeschossen.lands.api.integration.LandsIntegration
import me.angeschossen.lands.api.LandsIntegration
import me.angeschossen.lands.api.flags.type.Flags
import org.bukkit.Location
import org.bukkit.Material
import org.bukkit.block.Block
import org.bukkit.entity.Animals
import org.bukkit.entity.LivingEntity
@@ -12,29 +13,30 @@ import org.bukkit.entity.Monster
import org.bukkit.entity.Player
class AntigriefLands(private val plugin: EcoPlugin) : AntigriefIntegration {
private val landsIntegration = LandsIntegration(this.plugin)
private val landsIntegration = LandsIntegration.of(this.plugin)
override fun canBreakBlock(
player: Player,
block: Block
): Boolean {
val area = landsIntegration.getAreaByLoc(block.location) ?: return true
return area.hasFlag(player, Flags.BLOCK_BREAK, false)
val area = landsIntegration.getArea(block.location) ?: return true
return area.hasRoleFlag(player, Flags.BLOCK_BREAK, block.type, false)
}
override fun canCreateExplosion(
player: Player,
location: Location
): Boolean {
val area = landsIntegration.getAreaByLoc(location) ?: return true
return area.hasFlag(player, Flags.ATTACK_PLAYER, false) && area.hasFlag(player, Flags.ATTACK_ANIMAL, false)
val area = landsIntegration.getArea(location) ?: return true
return area.hasRoleFlag(player, Flags.ATTACK_PLAYER, Material.AIR, false)
&& area.hasRoleFlag(player, Flags.ATTACK_ANIMAL, Material.AIR, false)
}
override fun canPlaceBlock(
player: Player,
block: Block
): Boolean {
val area = landsIntegration.getAreaByLoc(block.location) ?: return true
return area.hasFlag(player, Flags.BLOCK_PLACE, false)
val area = landsIntegration.getArea(block.location) ?: return true
return area.hasRoleFlag(player, Flags.BLOCK_PLACE, Material.AIR, false)
}
override fun canInjure(
@@ -42,19 +44,19 @@ class AntigriefLands(private val plugin: EcoPlugin) : AntigriefIntegration {
victim: LivingEntity
): Boolean {
val area = landsIntegration.getAreaByLoc(victim.location) ?: return true
val area = landsIntegration.getArea(victim.location) ?: return true
return when(victim) {
is Player -> area.hasFlag(player, Flags.ATTACK_PLAYER, false)
is Monster -> area.hasFlag(player, Flags.ATTACK_MONSTER, false)
is Animals -> area.hasFlag(player, Flags.ATTACK_MONSTER, false)
is Player -> area.hasRoleFlag(player, Flags.ATTACK_PLAYER, Material.AIR, false)
is Monster -> area.hasRoleFlag(player, Flags.ATTACK_MONSTER, Material.AIR, false)
is Animals -> area.hasRoleFlag(player, Flags.ATTACK_ANIMAL, Material.AIR, false)
else -> area.isTrusted(player.uniqueId)
}
}
override fun canPickupItem(player: Player, location: Location): Boolean {
val area = landsIntegration.getAreaByLoc(location) ?: return true
return area.hasFlag(player, Flags.ITEM_PICKUP, false)
val area = landsIntegration.getArea(location) ?: return true
return area.hasRoleFlag(player, Flags.ITEM_PICKUP, Material.AIR, false)
}
override fun getPluginName(): String {

View File

@@ -1,11 +1,11 @@
package com.willfp.eco.internal.spigot.integrations.antigrief
import com.willfp.eco.core.integrations.antigrief.AntigriefIntegration
import me.NoChance.PvPManager.PvPlayer
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 {
@@ -27,7 +27,7 @@ class AntigriefPvPManager: AntigriefIntegration {
override fun canInjure(player: Player, victim: LivingEntity): Boolean {
return when(victim) {
is Player -> {
(PvPlayer.get(victim).isInCombat())}
(PvPlayer.get(victim).isInCombat)}
else -> true
}
}

View File

@@ -19,7 +19,6 @@ import java.util.logging.Level
import java.util.zip.GZIPOutputStream
import javax.net.ssl.HttpsURLConnection
@Suppress("UNCHECKED_CAST")
class Metrics(private val plugin: EcoPlugin) {
private val metricsBase: MetricsBase

View File

@@ -17,7 +17,7 @@ class CustomEntitiesMythicMobs : CustomEntitiesIntegration {
CustomEntity(
key,
{
val entityId = api.getMythicMobInstance(it)?.type?.entityType ?: return@CustomEntity false
val entityId = api.getMythicMobInstance(it)?.type?.entityType?.name ?: return@CustomEntity false
entityId.equals(id, ignoreCase = true)
},
{

View File

@@ -1,6 +1,6 @@
package com.willfp.eco.internal.spigot.integrations.customitems
import com.ssomar.executableitems.api.ExecutableItemsAPI
import com.ssomar.score.api.executableitems.ExecutableItemsAPI
import com.willfp.eco.core.integrations.customitems.CustomItemsIntegration
import com.willfp.eco.core.items.CustomItem
import com.willfp.eco.core.items.Items
@@ -8,6 +8,7 @@ import com.willfp.eco.core.items.TestableItem
import com.willfp.eco.core.items.provider.ItemProvider
import com.willfp.eco.util.NamespacedKeyUtils
import org.bukkit.inventory.ItemStack
import java.util.Optional
import java.util.function.Predicate
class CustomItemsExecutableItems : CustomItemsIntegration {
@@ -21,15 +22,15 @@ class CustomItemsExecutableItems : CustomItemsIntegration {
private class ExecutableItemsProvider : ItemProvider("executableitems") {
override fun provideForKey(key: String): TestableItem? {
val item = ExecutableItemsAPI.getExecutableItem(key) ?: return null
val item = ExecutableItemsAPI.getExecutableItemsManager().getExecutableItem(key).orElse(null) ?: return null
val namespacedKey = NamespacedKeyUtils.create("executableitems", key)
return CustomItem(
namespacedKey,
Predicate { test: ItemStack ->
val customStack = ExecutableItemsAPI.getExecutableItemConfig(test) ?: return@Predicate false
val customStack = ExecutableItemsAPI.getExecutableItemsManager().getExecutableItem(test).orElse(null) ?: return@Predicate false
customStack.id.equals(key, ignoreCase = true)
},
item
item.buildItem(1, Optional.empty())
)
}
}

View File

@@ -41,11 +41,7 @@ object EntityLookupModelEngine : Integration {
return EntityArgParseResult(
{
val modelled = ModelEngineAPI.getModeledEntity(it.uniqueId)
if (modelled == null) {
return@EntityArgParseResult false
}
val modelled = ModelEngineAPI.getModeledEntity(it.uniqueId) ?: return@EntityArgParseResult false
modelled.models.containsKey(id)
},

View File

@@ -6,7 +6,6 @@ import eu.decentsoftware.holograms.api.DHAPI
import org.bukkit.Location
import java.util.UUID
@Suppress("DEPRECATION")
class HologramDecentHolograms : HologramIntegration {
override fun createHologram(location: Location, contents: MutableList<String>): Hologram {
val id = UUID.randomUUID().toString()

View File

@@ -6,7 +6,6 @@ import me.gholo.api.GHoloAPI
import org.bukkit.Location
import java.util.UUID
@Suppress("DEPRECATION")
class HologramGHolo : HologramIntegration {
companion object {
private val api = GHoloAPI()

View File

@@ -4,8 +4,10 @@ import com.willfp.eco.core.integrations.shop.ShopIntegration
import com.willfp.eco.core.integrations.shop.ShopSellEvent
import com.willfp.eco.core.price.Price
import com.willfp.eco.core.price.impl.PriceEconomy
import com.willfp.eco.core.price.impl.PriceFree
import me.gypopo.economyshopgui.api.EconomyShopGUIHook
import me.gypopo.economyshopgui.api.events.PreTransactionEvent
import me.gypopo.economyshopgui.util.Transaction
import org.bukkit.Bukkit
import org.bukkit.entity.Player
import org.bukkit.event.EventHandler
@@ -18,27 +20,63 @@ class ShopEconomyShopGUI : ShopIntegration {
}
override fun getUnitValue(itemStack: ItemStack, player: Player): Price {
val shopItem = EconomyShopGUIHook.getShopItem(itemStack) ?: return PriceFree()
return PriceEconomy(
EconomyShopGUIHook.getItemSellPrice(player, itemStack.clone().apply {
EconomyShopGUIHook.getItemSellPrice(shopItem, itemStack.clone().apply {
amount = 1
})
}, player)
)
}
override fun isSellable(itemStack: ItemStack, player: Player): Boolean {
return EconomyShopGUIHook.getItemSellPrice(player, itemStack) > 0
val shopItem = EconomyShopGUIHook.getShopItem(itemStack) ?: return false
return EconomyShopGUIHook.getItemSellPrice(shopItem, itemStack, player) > 0
}
object EconomyShopGUISellEventListeners : Listener {
private val sellTypes = listOf(
"SELL_GUI_SCREEN",
"SELL_SCREEN",
"SELL_ALL_SCREEN",
"SELL_ALL_COMMAND",
"QUICK_SELL",
"AUTO_SELL_CHEST",
)
private val sellAllTypes = listOf(
"SELL_GUI_SCREEN",
"SELL_ALL_COMMAND",
)
@EventHandler
fun shopEventToEcoEvent(event: PreTransactionEvent) {
if (event.transactionType.name.uppercase() !in sellTypes) {
return
}
if (event.isCancelled) {
return
}
val ecoEvent = ShopSellEvent(event.player, PriceEconomy(event.price), event.itemStack)
Bukkit.getPluginManager().callEvent(ecoEvent)
event.price = ecoEvent.value.getValue(event.player) * ecoEvent.multiplier
val prices = if (event.transactionType.name.uppercase() in sellAllTypes) {
event.items!!
} else {
mapOf(event.shopItem to event.amount)
}
var total = 0.0
for ((shopItem, amount) in prices) {
val price = EconomyShopGUIHook.getItemSellPrice(shopItem, shopItem.itemToGive
.apply { this.amount = amount }, event.player)
val ecoEvent = ShopSellEvent(event.player, PriceEconomy(price), shopItem.itemToGive
.apply { this.amount = amount })
Bukkit.getPluginManager().callEvent(ecoEvent)
total += ecoEvent.value.getValue(event.player) * ecoEvent.multiplier
}
event.price = total
}
}

View File

@@ -42,7 +42,6 @@ class StackedRecipeListener(
val recipe = Recipes.getMatch(matrix) ?: return
// Get the handler for the type of recipe
@Suppress("UNCHECKED_CAST")
val handler = handlers.firstOrNull { recipe::class.java.isAssignableFrom(it.recipeType) } ?: return
var isStackedRecipe = false

View File

@@ -45,6 +45,7 @@ softdepend:
- ExecutableItems
- RPGHorses
- EconomyShopGUI
- EconomyShopGUI-Premium
- zShop
- DeluxeSellwands
- Scyther

View File

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

Binary file not shown.

Binary file not shown.