Compare commits

..

79 Commits

Author SHA1 Message Date
Auxilor
08c14f4def Cleaned up BentoBox 2021-11-02 19:28:19 +00:00
Auxilor
0fa6f972c0 Fixed BentoBox 2021-11-02 19:26:34 +00:00
Auxilor
8837fecfa0 Added support for BentoBox 2021-11-02 19:24:34 +00:00
Auxilor
af1841770e (Hopefully) fixed HologramCMI 2021-11-02 18:54:21 +00:00
Auxilor
1d85dbf08d Altered rate limit settings 2021-11-02 18:52:57 +00:00
Auxilor
ba9812534a Increased timeframe to 2 2021-11-02 18:46:28 +00:00
Auxilor
1f95a33385 Added log errors option for async display 2021-11-02 18:39:29 +00:00
Auxilor
bc652a8154 MySQLDataHandler changes 2021-11-02 18:37:01 +00:00
Auxilor
0c2e1f0cae Fixed ServerUtils 2021-11-02 18:29:17 +00:00
Auxilor
39d6eb7f9a Updated to 6.13.0 2021-11-02 18:17:57 +00:00
Auxilor
61bae6de55 Rearranged config.yml 2021-11-02 18:17:40 +00:00
Auxilor
aa4ac4c6d1 Added option to set rate limit timeframe 2021-11-02 17:44:57 +00:00
Auxilor
7141b12e95 Removed redundant explicit type 2021-11-02 17:42:27 +00:00
Auxilor
2382548629 Added in rate limiting for PacketWindowItems.kt 2021-11-02 17:39:41 +00:00
Auxilor
0c4bd182f7 Ignored async exceptions in PacketWindowItems.kt 2021-11-02 17:09:48 +00:00
Auxilor
e90e053b45 Added emergency async display and ServerUtils 2021-11-02 16:50:04 +00:00
Auxilor
742c1abb00 Cleaned up async display 2021-11-02 14:10:57 +00:00
Auxilor
c0686ca386 Added guava and asynchronous window items processing 2021-11-02 13:53:30 +00:00
Auxilor
db2ea7daa9 Updated to 6.12.2 2021-11-02 09:24:23 +00:00
Auxilor
ed4f0b2ab6 Fixed EconomyManager and ListUtils 2021-11-02 09:24:16 +00:00
Auxilor
6decc68d1b Updated to 6.12.1 2021-11-02 08:30:39 +00:00
Auxilor
1e061313a4 Codestyle 2021-11-02 08:30:27 +00:00
Auxilor
4762004e94 @NotNull'ed PlayerProfile#read 2021-11-02 08:29:00 +00:00
Auxilor
0d9889ef43 Fixed paste calling the callback three times 2021-11-01 21:14:56 +00:00
Auxilor
b2a23e80a1 Added missing nullable annotation 2021-11-01 21:06:50 +00:00
Auxilor
0e439db7b3 Auto-registered PersistentDataKeys 2021-11-01 21:01:57 +00:00
Auxilor
f615006b23 Added missing PlayerProfile javadoc 2021-11-01 21:00:58 +00:00
Auxilor
d952dbc61b Added ListUtils#listToFrequencyMap 2021-11-01 20:00:46 +00:00
Auxilor
d95a96f4c6 Added in support for economy via vault 2021-11-01 19:55:47 +00:00
Auxilor
33940f5778 Improved MySQLDataHandler.kt 2021-11-01 19:38:30 +00:00
Auxilor
173d04595f Removed duplicate uuid field from Players table 2021-11-01 19:04:16 +00:00
Auxilor
c7c4ca3777 Updated plugin.yml 2021-11-01 19:02:40 +00:00
Auxilor
cbf7316db9 Fixed public static variable in display 2021-11-01 18:55:01 +00:00
Auxilor
601cadd582 Fixed weird IntelliJ bug 2021-11-01 18:54:25 +00:00
Auxilor
877647db03 Fixed various codestyle issues 2021-11-01 18:53:36 +00:00
Auxilor
81196bb132 Added PotionUtils 2021-11-01 18:44:44 +00:00
Auxilor
ef701f1f86 Added BlockUtils#isPlayerPlaced 2021-11-01 18:38:58 +00:00
Auxilor
58d8f72cf5 Fixed javadoc 2021-11-01 16:21:44 +00:00
Auxilor
3abf916f18 Fixed SuperiorSkyblock2 integration 2021-11-01 16:18:31 +00:00
Auxilor
b459a746e3 Merge branch 'master' into develop
# Conflicts:
#	eco-core/core-plugin/build.gradle
2021-11-01 16:15:00 +00:00
Will FP
30c37078a2 Merge pull request #47
Added SuperiorSkyblock2 Antigrief integration
2021-11-01 16:14:35 +00:00
Auxilor
36ccfb5fc6 Fixed McmmoIntegrationImpl.kt 2021-11-01 16:13:51 +00:00
Auxilor
c84f1a060a Fixed KingdomsX version 2021-11-01 16:12:23 +00:00
Auxilor
d8d5609f6b Renamed consumer to callback 2021-11-01 16:09:23 +00:00
Auxilor
462abd46c6 Marked Handler as internal 2021-11-01 16:07:14 +00:00
Auxilor
e89008cf97 Fixed SkullUtils#getSkullTexture 2021-11-01 16:00:38 +00:00
Auxilor
d34e43797d Registering a duplicate key will now replace the existing key 2021-11-01 15:51:22 +00:00
Auxilor
63ee83c795 Added Essentials / CMI AFK integrations 2021-11-01 15:50:10 +00:00
Auxilor
2eec17698e Updated paste 2021-11-01 15:34:02 +00:00
Auxilor
ad52c2a086 Improved EcoPlayerProfileHandler.kt 2021-11-01 15:31:10 +00:00
Auxilor
6e482cccda Registered player name key 2021-11-01 15:24:11 +00:00
Auxilor
899b4297f7 Added saved display name utils with persistent meta 2021-11-01 14:17:29 +00:00
Auxilor
ceb95e20b5 Added in hologram integrations for CMI/GHolo/HolographicDisplays 2021-11-01 14:09:41 +00:00
Auxilor
bdfeb9a0ab Updated to 6.12.0 2021-11-01 13:29:03 +00:00
Auxilor
e0707e2afa Added missing handler implementations 2021-11-01 13:28:51 +00:00
Auxilor
e8048f5a0a Added in persistent data storage 2021-11-01 13:25:14 +00:00
0ft3n
a36018e31a Merge branch 'Auxilor:master' into master 2021-10-27 22:45:29 +03:00
_OfTeN_
a34c63161d Added SuperiorSkyblock2 Antigrief integration 2021-10-27 22:44:45 +03:00
Auxilor
2eb350977c Updated to 6.11.2 2021-10-26 19:51:21 +01:00
Auxilor
8a89a63c5f PacketSetCreativeSlot and PacketSetSlot now clear frames 2021-10-26 19:51:06 +01:00
Auxilor
5c26e6e782 DisplayFrames now include the ItemStacks as well as the hashes: larger memory footprint should be fine with ttl and frequent clearing 2021-10-26 19:49:49 +01:00
Auxilor
f88b914fa6 Fixed gradient modifier issues 2021-10-26 19:41:38 +01:00
Auxilor
15ff6b3ea3 Merge branch 'master' into develop 2021-10-26 19:28:37 +01:00
Auxilor
b14eed696c Added alice anticheat support 2021-10-26 19:21:34 +01:00
Auxilor
cffae33c87 StringUtils change 2021-10-26 19:11:01 +01:00
Auxilor
adf6d1c800 Updated to 6.11.1 2021-10-15 10:27:58 +01:00
Auxilor
2b7c8962e7 Merge remote-tracking branch 'origin/master' 2021-10-15 10:27:51 +01:00
Auxilor
b60cbfce2c Fixed unpredicatable config behaviour 2021-10-15 10:27:44 +01:00
Auxilor
910ad18703 Updated towny 2021-10-15 10:25:32 +01:00
Auxilor
8a4a71ebc1 Updated lands integration 2021-10-15 10:24:18 +01:00
Will FP
2e748b1723 Update README.md 2021-10-12 20:12:09 +01:00
Auxilor
cb28726bc3 Cleaned up Multiverse-Inventories integration 2021-10-12 12:04:52 +01:00
Auxilor
c0c20d63bb Merge remote-tracking branch 'origin/master' into develop 2021-10-12 11:59:26 +01:00
Will FP
3b11610c45 Merge pull request #44
Multiverse-Inventories integration
2021-10-12 11:58:41 +01:00
Auxilor
232048022e Added extra config constructors 2021-10-12 11:51:22 +01:00
Auxilor
9abfe0ab01 Updated to 6.11.0 2021-10-12 11:36:07 +01:00
Auxilor
e7ac05278c Added lots of display frame options 2021-10-12 11:31:02 +01:00
Auxilor
85ba40c279 Added PluginLike, allowing extensions to have their own configs 2021-10-12 11:17:58 +01:00
_OfTeN_
1acf86492e Added Multiverse-Inventories integration (additional ArmorChangeEvent call for all effect to reapply for new players inventory. 2021-10-12 11:09:09 +03:00
100 changed files with 2196 additions and 231 deletions

View File

@@ -1,6 +1,6 @@
<h1 align="center">
<br>
<img src="https://i.imgur.com/p5uR2Qp.png" alt="eco logo" width="256">
<img src="https://i.imgur.com/kU3ejCt.png" alt="eco logo" width="256">
<br>
</h1>
@@ -157,7 +157,7 @@ Here's a list of some (not all) of the features of eco:
<h1 align="center">
<br>
<a href="http://gamersupps.gg/discount/Auxilor?afmc=Auxilor" target="_blank">
<a href="https://gamersupps.gg/discount/Auxilor?afmc=Auxilor" target="_blank">
<img src="https://i.imgur.com/uFDpBAC.png" alt="supps banner">
</a>
<a href="https://dedimc.promo/Auxilor" target="_blank">

View File

@@ -25,11 +25,14 @@ allprojects {
mavenLocal()
maven { url 'https://jitpack.io' }
// SuperiorSkyblock2
maven { url 'https://repo.bg-software.com/repository/api/' }
// NMS (for jitpack compilation)
maven { url 'https://repo.codemc.org/repository/nms/' }
// bStats, mcMMO
maven { url 'https://repo.codemc.org/repository/maven-public' }
// bStats, mcMMO, BentoBox
maven { url 'https://repo.codemc.org/repository/maven-public/' }
// Spigot API
maven { url 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' }

View File

@@ -9,15 +9,15 @@ dependencies {
// Adventure
compileOnly 'net.kyori:adventure-platform-bukkit:4.0.0'
compileOnly 'net.kyori:adventure-text-minimessage:4.1.0-SNAPSHOT'
compileOnly 'net.kyori:adventure-api:4.9.1'
compileOnly 'net.kyori:adventure-api:4.9.2'
compileOnly 'net.kyori:adventure-text-serializer-gson:4.9.2'
compileOnly 'net.kyori:adventure-text-serializer-legacy:4.8.1'
compileOnly 'net.kyori:adventure-text-serializer-legacy:4.9.2'
// Other
compileOnly 'org.spigotmc:spigot-api:1.17.1-R0.1-SNAPSHOT'
compileOnly 'org.apache.maven:maven-artifact:3.0.3'
compileOnly 'org.apache.maven:maven-artifact:3.8.1'
compileOnly 'com.comphenix.protocol:ProtocolLib:4.7.1-SNAPSHOT'
compileOnly 'com.google.code.gson:gson:2.8.7'
compileOnly 'com.google.code.gson:gson:2.8.8'
}
java {

View File

@@ -1,6 +1,5 @@
package com.willfp.eco.core;
import lombok.Getter;
import lombok.experimental.UtilityClass;
import org.apache.commons.lang.Validate;
import org.jetbrains.annotations.ApiStatus;
@@ -15,20 +14,12 @@ import org.jetbrains.annotations.NotNull;
public class Eco {
/**
* Instance of eco handler.
* <p>
* The handler is, in essence, a way to interface between the eco-api
* frontend module, and the eco-backend implementations.
* <p>
* There shouldn't really be any reason to ever use the handler
* in your own plugins, but if you want to then you can - it's
* just a part of the API like any other.
*/
@Getter
@ApiStatus.Internal
private Handler handler;
/**
* Set the handler.
*
* @param handler The handler.
*/
@ApiStatus.Internal
@@ -37,4 +28,25 @@ public class Eco {
Eco.handler = handler;
}
/**
* Get the instance of the eco handler.
* <p>
* The handler is, in essence, a way to interface between the eco-api
* frontend module, and the eco-backend implementations.
* <p>
* There shouldn't really be any reason to ever use the handler
* in your own plugins, and you are likely to break things. All parts of
* the handler are abstracted into logically named parts of the API.
* <p>
* In versions of eco before 6.12.0, the handler was considered part of
* the eco API, however it has since been moved into an internal component
* that shouldn't be used in your plugins.
*
* @return The handler.
*/
@ApiStatus.Internal
public Handler getHandler() {
return handler;
}
}

View File

@@ -52,7 +52,7 @@ import java.util.stream.Collectors;
* be cancelled.</b>
*/
@SuppressWarnings("unused")
public abstract class EcoPlugin extends JavaPlugin {
public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
/**
* The polymart resource ID of the plugin.
*/

View File

@@ -2,6 +2,8 @@ package com.willfp.eco.core;
import com.willfp.eco.core.config.updating.ConfigHandler;
import com.willfp.eco.core.config.wrapper.ConfigFactory;
import com.willfp.eco.core.data.keys.KeyRegistry;
import com.willfp.eco.core.data.PlayerProfileHandler;
import com.willfp.eco.core.drops.DropQueueFactory;
import com.willfp.eco.core.events.EventManager;
import com.willfp.eco.core.extensions.ExtensionLoader;
@@ -17,6 +19,7 @@ import com.willfp.eco.core.requirement.RequirementFactory;
import com.willfp.eco.core.scheduling.Scheduler;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -24,8 +27,9 @@ import java.util.List;
import java.util.logging.Logger;
/**
* @see Eco
* @see Eco#getHandler()
*/
@ApiStatus.Internal
public interface Handler {
/**
* Create a scheduler.
@@ -212,4 +216,19 @@ public interface Handler {
*/
@Nullable
BukkitAudiences getAdventure();
/**
* Get the key registry.
*
* @return The registry.
*/
@NotNull
KeyRegistry getKeyRegistry();
/**
* Get the PlayerProfile handler.
*
* @return The handler.
*/
PlayerProfileHandler getPlayerProfileHandler();
}

View File

@@ -9,7 +9,7 @@ import org.jetbrains.annotations.NotNull;
*
* @param <T> The eco plugin type.
*/
public abstract class PluginDependent<@NotNull T extends EcoPlugin> {
public abstract class PluginDependent<T extends EcoPlugin> {
/**
* The {@link EcoPlugin} that is stored.
*/

View File

@@ -0,0 +1,29 @@
package com.willfp.eco.core;
import com.willfp.eco.core.config.updating.ConfigHandler;
import java.io.File;
/**
* Represents any class that acts like a plugin, for example {@link EcoPlugin}
* or {@link com.willfp.eco.core.extensions.Extension}. This exists to create
* things such as extension base configs rather than needing to pass an instance
* of the owning plugin.
*/
public interface PluginLike {
/**
* Get the data folder of the object.
* <p>
* Returns the plugin data folder for a plugin, or the extension's parent plugin's folder
*
* @return The data folder.
*/
File getDataFolder();
/**
* Get the handler class for updatable classes.
*
* @return The config handler.
*/
ConfigHandler getConfigHandler();
}

View File

@@ -34,7 +34,7 @@ public interface LoadableConfig {
File getConfigFile();
/**
* Get the config name (including extension)
* Get the config name (including extension).
*
* @return The name.
*/

View File

@@ -2,8 +2,8 @@ package com.willfp.eco.core.config.json;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.PluginLike;
import com.willfp.eco.core.config.json.wrapper.LoadableJSONConfigWrapper;
import com.willfp.eco.core.config.yaml.wrapper.LoadableYamlConfigWrapper;
import org.jetbrains.annotations.NotNull;
/**
@@ -20,7 +20,7 @@ public abstract class JSONBaseConfig extends LoadableJSONConfigWrapper {
*/
protected JSONBaseConfig(@NotNull final String configName,
final boolean removeUnused,
@NotNull final EcoPlugin plugin,
@NotNull final PluginLike plugin,
@NotNull final String... updateBlacklist) {
super(
Eco.getHandler().getConfigFactory().createUpdatableJSONConfig(
@@ -40,7 +40,7 @@ public abstract class JSONBaseConfig extends LoadableJSONConfigWrapper {
*/
protected JSONBaseConfig(@NotNull final String configName,
final boolean removeUnused,
@NotNull final EcoPlugin plugin) {
@NotNull final PluginLike plugin) {
super(
Eco.getHandler().getConfigFactory().createUpdatableJSONConfig(
configName,
@@ -51,4 +51,28 @@ public abstract class JSONBaseConfig extends LoadableJSONConfigWrapper {
)
);
}
/**
* @param configName The name of the config
* @param removeUnused Whether keys not present in the default config should be removed on update.
* @param plugin The plugin.
* @param updateBlacklist Substring of keys to not add/remove keys for.
*/
protected JSONBaseConfig(@NotNull final String configName,
final boolean removeUnused,
@NotNull final EcoPlugin plugin,
@NotNull final String... updateBlacklist) {
this(configName, removeUnused, (PluginLike) plugin, updateBlacklist);
}
/**
* @param configName The name of the config
* @param removeUnused Whether keys not present in the default config should be removed on update.
* @param plugin The plugin.
*/
protected JSONBaseConfig(@NotNull final String configName,
final boolean removeUnused,
@NotNull final EcoPlugin plugin) {
this(configName, removeUnused, (PluginLike) plugin);
}
}

View File

@@ -2,8 +2,8 @@ package com.willfp.eco.core.config.json;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.PluginLike;
import com.willfp.eco.core.config.json.wrapper.LoadableJSONConfigWrapper;
import com.willfp.eco.core.config.yaml.wrapper.LoadableYamlConfigWrapper;
import org.jetbrains.annotations.NotNull;
/**
@@ -26,7 +26,7 @@ public abstract class JSONExtendableConfig extends LoadableJSONConfigWrapper {
*/
protected JSONExtendableConfig(@NotNull final String configName,
final boolean removeUnused,
@NotNull final EcoPlugin plugin,
@NotNull final PluginLike plugin,
@NotNull final Class<?> source,
@NotNull final String subDirectoryPath,
@NotNull final String... updateBlacklist) {
@@ -41,4 +41,20 @@ public abstract class JSONExtendableConfig extends LoadableJSONConfigWrapper {
)
);
}
/**
* @param configName The name of the config
* @param removeUnused Whether keys not present in the default config should be removed on update.
* @param plugin The plugin.
* @param updateBlacklist Substring of keys to not add/remove keys for.
* @param subDirectoryPath The subdirectory path.
* @param source The class that owns the resource.
*/
protected JSONExtendableConfig(@NotNull final String configName,
final boolean removeUnused,
@NotNull final EcoPlugin plugin,
@NotNull final Class<?> source,
@NotNull final String subDirectoryPath,
@NotNull final String... updateBlacklist) {
this(configName, removeUnused, (PluginLike) plugin, source, subDirectoryPath, updateBlacklist);
}
}

View File

@@ -2,6 +2,7 @@ package com.willfp.eco.core.config.json;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.PluginLike;
import com.willfp.eco.core.config.json.wrapper.LoadableJSONConfigWrapper;
import org.jetbrains.annotations.NotNull;
@@ -18,7 +19,20 @@ public abstract class JSONStaticBaseConfig extends LoadableJSONConfigWrapper {
* @param plugin The plugin.
*/
protected JSONStaticBaseConfig(@NotNull final String configName,
@NotNull final EcoPlugin plugin) {
@NotNull final PluginLike plugin) {
super(Eco.getHandler().getConfigFactory().createLoadableJSONConfig(configName, plugin, "", plugin.getClass()));
}
/**
* Config implementation for configs present in the plugin's base directory (eg config.json, lang.json).
* <p>
* Does not automatically update.
*
* @param configName The name of the config
* @param plugin The plugin.
*/
protected JSONStaticBaseConfig(@NotNull final String configName,
@NotNull final EcoPlugin plugin) {
this(configName, (PluginLike) plugin);
}
}

View File

@@ -4,7 +4,7 @@ import com.willfp.eco.core.config.interfaces.LoadableConfig;
import org.jetbrains.annotations.NotNull;
/**
* Every {@link com.willfp.eco.core.EcoPlugin} has a config handler.
* Every {@link com.willfp.eco.core.PluginLike} has a config handler.
* <p>
* Handles updating and saving configs.
*/

View File

@@ -1,6 +1,6 @@
package com.willfp.eco.core.config.wrapper;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.PluginLike;
import com.willfp.eco.core.config.interfaces.Config;
import com.willfp.eco.core.config.interfaces.JSONConfig;
import org.bukkit.configuration.file.YamlConfiguration;
@@ -24,7 +24,7 @@ public interface ConfigFactory {
* @return The config implementation.
*/
Config createUpdatableYamlConfig(@NotNull String configName,
@NotNull EcoPlugin plugin,
@NotNull PluginLike plugin,
@NotNull String subDirectoryPath,
@NotNull Class<?> source,
boolean removeUnused,
@@ -42,7 +42,7 @@ public interface ConfigFactory {
* @return The config implementation.
*/
JSONConfig createUpdatableJSONConfig(@NotNull String configName,
@NotNull EcoPlugin plugin,
@NotNull PluginLike plugin,
@NotNull String subDirectoryPath,
@NotNull Class<?> source,
boolean removeUnused,
@@ -58,7 +58,7 @@ public interface ConfigFactory {
* @return The config implementation.
*/
JSONConfig createLoadableJSONConfig(@NotNull String configName,
@NotNull EcoPlugin plugin,
@NotNull PluginLike plugin,
@NotNull String subDirectoryPath,
@NotNull Class<?> source);
@@ -72,7 +72,7 @@ public interface ConfigFactory {
* @return The config implementation.
*/
Config createLoadableYamlConfig(@NotNull String configName,
@NotNull EcoPlugin plugin,
@NotNull PluginLike plugin,
@NotNull String subDirectoryPath,
@NotNull Class<?> source);

View File

@@ -2,6 +2,7 @@ package com.willfp.eco.core.config.yaml;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.PluginLike;
import com.willfp.eco.core.config.yaml.wrapper.LoadableYamlConfigWrapper;
import org.jetbrains.annotations.NotNull;
@@ -19,7 +20,7 @@ public abstract class YamlBaseConfig extends LoadableYamlConfigWrapper {
*/
protected YamlBaseConfig(@NotNull final String configName,
final boolean removeUnused,
@NotNull final EcoPlugin plugin,
@NotNull final PluginLike plugin,
@NotNull final String... updateBlacklist) {
super(
Eco.getHandler().getConfigFactory().createUpdatableYamlConfig(
@@ -39,7 +40,7 @@ public abstract class YamlBaseConfig extends LoadableYamlConfigWrapper {
*/
protected YamlBaseConfig(@NotNull final String configName,
final boolean removeUnused,
@NotNull final EcoPlugin plugin) {
@NotNull final PluginLike plugin) {
super(
Eco.getHandler().getConfigFactory().createUpdatableYamlConfig(
configName,
@@ -50,4 +51,28 @@ public abstract class YamlBaseConfig extends LoadableYamlConfigWrapper {
)
);
}
/**
* @param configName The name of the config
* @param removeUnused Whether keys not present in the default config should be removed on update.
* @param plugin The plugin.
* @param updateBlacklist Substring of keys to not add/remove keys for.
*/
protected YamlBaseConfig(@NotNull final String configName,
final boolean removeUnused,
@NotNull final EcoPlugin plugin,
@NotNull final String... updateBlacklist) {
this(configName, removeUnused, (PluginLike) plugin, updateBlacklist);
}
/**
* @param configName The name of the config
* @param removeUnused Whether keys not present in the default config should be removed on update.
* @param plugin The plugin.
*/
protected YamlBaseConfig(@NotNull final String configName,
final boolean removeUnused,
@NotNull final EcoPlugin plugin) {
this(configName, removeUnused, (PluginLike) plugin);
}
}

View File

@@ -2,6 +2,7 @@ package com.willfp.eco.core.config.yaml;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.PluginLike;
import com.willfp.eco.core.config.yaml.wrapper.LoadableYamlConfigWrapper;
import org.jetbrains.annotations.NotNull;
@@ -25,7 +26,7 @@ public abstract class YamlExtendableConfig extends LoadableYamlConfigWrapper {
*/
protected YamlExtendableConfig(@NotNull final String configName,
final boolean removeUnused,
@NotNull final EcoPlugin plugin,
@NotNull final PluginLike plugin,
@NotNull final Class<?> source,
@NotNull final String subDirectoryPath,
@NotNull final String... updateBlacklist) {
@@ -40,4 +41,20 @@ public abstract class YamlExtendableConfig extends LoadableYamlConfigWrapper {
)
);
}
/**
* @param configName The name of the config
* @param removeUnused Whether keys not present in the default config should be removed on update.
* @param plugin The plugin.
* @param updateBlacklist Substring of keys to not add/remove keys for.
* @param subDirectoryPath The subdirectory path.
* @param source The class that owns the resource.
*/
protected YamlExtendableConfig(@NotNull final String configName,
final boolean removeUnused,
@NotNull final EcoPlugin plugin,
@NotNull final Class<?> source,
@NotNull final String subDirectoryPath,
@NotNull final String... updateBlacklist) {
this(configName, removeUnused, (PluginLike) plugin, source, subDirectoryPath, updateBlacklist);
}
}

View File

@@ -2,6 +2,7 @@ package com.willfp.eco.core.config.yaml;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.PluginLike;
import com.willfp.eco.core.config.yaml.wrapper.LoadableYamlConfigWrapper;
import org.jetbrains.annotations.NotNull;
@@ -18,7 +19,20 @@ public abstract class YamlStaticBaseConfig extends LoadableYamlConfigWrapper {
* @param plugin The plugin.
*/
protected YamlStaticBaseConfig(@NotNull final String configName,
@NotNull final EcoPlugin plugin) {
@NotNull final PluginLike plugin) {
super(Eco.getHandler().getConfigFactory().createLoadableYamlConfig(configName, plugin, "", plugin.getClass()));
}
/**
* Config implementation for configs present in the plugin's base directory (eg config.yml, lang.yml).
* <p>
* Does not automatically update.
*
* @param configName The name of the config
* @param plugin The plugin.
*/
protected YamlStaticBaseConfig(@NotNull final String configName,
@NotNull final EcoPlugin plugin) {
this(configName, (PluginLike) plugin);
}
}

View File

@@ -0,0 +1,56 @@
package com.willfp.eco.core.data;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.data.keys.PersistentDataKey;
import org.bukkit.OfflinePlayer;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
/**
* Persistent data storage interface for players.
* <p>
* Profiles save automatically, so there is no need to save after changes.
*/
public interface PlayerProfile {
/**
* Write a key to a player's persistent data.
*
* @param key The key.
* @param value The value.
* @param <T> The type of the key.
*/
<T> void write(@NotNull PersistentDataKey<T> key,
@NotNull T value);
/**
* Read a key from a player's persistent data.
*
* @param key The key.
* @param <T> The type of the key.
* @return The value, or the default value if not found.
*/
<T> @NotNull T read(@NotNull PersistentDataKey<T> key);
/**
* Load a player profile.
*
* @param player The player.
* @return The profile.
*/
@NotNull
static PlayerProfile load(@NotNull final OfflinePlayer player) {
return load(player.getUniqueId());
}
/**
* Load a player profile.
*
* @param uuid The player's UUID.
* @return The profile.
*/
@NotNull
static PlayerProfile load(@NotNull final UUID uuid) {
return Eco.getHandler().getPlayerProfileHandler().load(uuid);
}
}

View File

@@ -0,0 +1,32 @@
package com.willfp.eco.core.data;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
/**
* API to handle player profiles.
*/
public interface PlayerProfileHandler {
/**
* Load a player profile.
*
* @param uuid The UUID.
* @return The profile.
*/
PlayerProfile load(@NotNull UUID uuid);
/**
* Save a player profile.
*
* @param uuid The uuid.
*/
void savePlayer(@NotNull UUID uuid);
/**
* Save all player data.
*
* @param async If the saving should be done asynchronously.
*/
void saveAll(boolean async);
}

View File

@@ -0,0 +1,24 @@
package com.willfp.eco.core.data.keys;
import org.jetbrains.annotations.NotNull;
import java.util.Set;
/**
* API to register persistent data keys.
*/
public interface KeyRegistry {
/**
* Register a persistent data key to be stored.
*
* @param key The key.
*/
void registerKey(@NotNull PersistentDataKey<?> key);
/**
* Get all registered keys.
*
* @return The keys.
*/
Set<PersistentDataKey<?>> getRegisteredKeys();
}

View File

@@ -0,0 +1,57 @@
package com.willfp.eco.core.data.keys;
import com.willfp.eco.core.Eco;
import lombok.Getter;
import org.bukkit.NamespacedKey;
import org.jetbrains.annotations.NotNull;
/**
* A persistent data key is a key with a type that can be stored about an offline player.
*
* @param <T> The type of the data.
*/
public class PersistentDataKey<T> {
/**
* The key of the persistent data value.
*/
@Getter
private final NamespacedKey key;
/**
* The default value for the key.
*/
@Getter
private final T defaultValue;
/**
* The persistent data key type.
*/
@Getter
private final PersistentDataKeyType type;
/**
* Create a new Persistent Data Key.
*
* @param key The key.
* @param type The data type.
* @param defaultValue The default value.
*/
public PersistentDataKey(@NotNull final NamespacedKey key,
@NotNull final PersistentDataKeyType type,
@NotNull final T defaultValue) {
this.key = key;
this.defaultValue = defaultValue;
this.type = type;
Eco.getHandler().getKeyRegistry().registerKey(this);
}
@Override
public String toString() {
return "PersistentDataKey{"
+ "key=" + key
+ ", defaultValue=" + defaultValue
+ ", type=" + type
+ '}';
}
}

View File

@@ -0,0 +1,26 @@
package com.willfp.eco.core.data.keys;
/**
* All storable data key types.
*/
public enum PersistentDataKeyType {
/**
* String.
*/
STRING,
/**
* Boolean.
*/
BOOLEAN,
/**
* Integer.
*/
INT,
/**
* Double.
*/
DOUBLE
}

View File

@@ -20,7 +20,7 @@ public class Display {
/**
* The display handler.
*/
public static DisplayHandler handler = null;
private static DisplayHandler handler = null;
/**
* Display on ItemStacks.
@@ -160,4 +160,20 @@ public class Display {
module.display(itemStack, player, args);
}
}
/**
* Set the display handler.
* <p>
* Internal API component, you will cause bugs if you create your own handler.
*
* @param handler The handler.
*/
@ApiStatus.Internal
public static void setHandler(@NotNull final DisplayHandler handler) {
if (Display.handler != null) {
throw new IllegalStateException("Display already initialized!");
}
Display.handler = handler;
}
}

View File

@@ -23,8 +23,8 @@ public interface DisplayHandler {
* @param player The player.
* @return The ItemStack.
*/
ItemStack display(@NotNull final ItemStack itemStack,
@Nullable final Player player);
ItemStack display(@NotNull ItemStack itemStack,
@Nullable Player player);
/**
* Revert on ItemStacks.
@@ -32,7 +32,7 @@ public interface DisplayHandler {
* @param itemStack The item.
* @return The ItemStack.
*/
ItemStack revert(@NotNull final ItemStack itemStack);
ItemStack revert(@NotNull ItemStack itemStack);
/**
* Finalize an ItemStacks.
@@ -40,7 +40,7 @@ public interface DisplayHandler {
* @param itemStack The item.
* @return The ItemStack.
*/
ItemStack finalize(@NotNull final ItemStack itemStack);
ItemStack finalize(@NotNull ItemStack itemStack);
/**
* Unfinalize an ItemStacks.
@@ -48,7 +48,7 @@ public interface DisplayHandler {
* @param itemStack The item.
* @return The ItemStack.
*/
ItemStack unfinalize(@NotNull final ItemStack itemStack);
ItemStack unfinalize(@NotNull ItemStack itemStack);
/**
* If an item is finalized.
@@ -56,5 +56,5 @@ public interface DisplayHandler {
* @param itemStack The item.
* @return If finalized.
*/
boolean isFinalized(@NotNull final ItemStack itemStack);
boolean isFinalized(@NotNull ItemStack itemStack);
}

View File

@@ -1,11 +1,15 @@
package com.willfp.eco.core.extensions;
import com.willfp.eco.core.EcoPlugin;
import com.willfp.eco.core.PluginLike;
import com.willfp.eco.core.config.updating.ConfigHandler;
import lombok.AccessLevel;
import lombok.Getter;
import org.apache.commons.lang.Validate;
import org.jetbrains.annotations.NotNull;
import java.io.File;
/**
* An extension is a separate jar file that hooks into the base plugin jar.
* <p>
@@ -15,9 +19,9 @@ import org.jetbrains.annotations.NotNull;
* Syntactically, extensions are very similar to plugins in their own right, except that
* they are loaded by another plugin.
*
* @see <a href="https://ecoenchants.polymart.org">EcoEnchants extension examples.</a>
* @see <a href="https://auxilor.polymart.org">Extension examples.</a>
*/
public abstract class Extension {
public abstract class Extension implements PluginLike {
/**
* The {@link EcoPlugin} that this extension is for.
*/
@@ -103,4 +107,14 @@ public abstract class Extension {
Validate.notNull(metadata, "Metadata cannot be null!");
return this.metadata.version();
}
@Override
public File getDataFolder() {
return this.plugin.getDataFolder();
}
@Override
public ConfigHandler getConfigHandler() {
return this.plugin.getConfigHandler();
}
}

View File

@@ -107,7 +107,7 @@ public interface FastItemStack {
* @param itemStack The ItemStack.
* @return The FastItemStack.
*/
static FastItemStack wrap(final ItemStack itemStack) {
static FastItemStack wrap(@Nullable final ItemStack itemStack) {
return Eco.getHandler().createFastItemStack(Objects.requireNonNullElseGet(itemStack, () -> new ItemStack(Material.AIR)));
}
}

View File

@@ -0,0 +1,44 @@
package com.willfp.eco.core.integrations.afk;
import lombok.experimental.UtilityClass;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
import java.util.Set;
/**
* Class to handle afk integrations.
*/
@UtilityClass
public class AFKManager {
/**
* A set of all registered integrations.
*/
private final Set<AFKWrapper> registered = new HashSet<>();
/**
* Register a new integration.
*
* @param integration The integration to register.
*/
public void register(@NotNull final AFKWrapper integration) {
registered.add(integration);
}
/**
* Get if a player is afk.
*
* @param player The player.
* @return If afk.
*/
public boolean isAfk(@NotNull final Player player) {
for (AFKWrapper afkWrapper : registered) {
if (afkWrapper.isAfk(player)) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,18 @@
package com.willfp.eco.core.integrations.afk;
import com.willfp.eco.core.integrations.Integration;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
/**
* Wrapper class for afk integrations.
*/
public interface AFKWrapper extends Integration {
/**
* Get if a player is afk.
*
* @param player The player.
* @return If afk.
*/
boolean isAfk(@NotNull Player player);
}

View File

@@ -0,0 +1,99 @@
package com.willfp.eco.core.integrations.economy;
import lombok.experimental.UtilityClass;
import org.bukkit.OfflinePlayer;
import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
import java.util.Set;
/**
* Class to handle economy.
*/
@UtilityClass
public class EconomyManager {
/**
* A set of all registered integrations.
*/
private final Set<EconomyWrapper> registered = new HashSet<>();
/**
* Register a new integration.
*
* @param integration The integration to register.
*/
public void register(@NotNull final EconomyWrapper integration) {
registered.add(integration);
}
/**
* Get if any economy registrations are registered.
*
* @return If any economy.
*/
public boolean hasRegistrations() {
return !registered.isEmpty();
}
/**
* Get if a player has a certain amount.
*
* @param player The player.
* @param amount The amount.
* @return If the player has the amount.
*/
public boolean hasAmount(@NotNull final OfflinePlayer player,
final double amount) {
for (EconomyWrapper wrapper : registered) {
return wrapper.hasAmount(player, amount);
}
return false;
}
/**
* Give money to a player.
*
* @param player The player.
* @param amount The amount to give.
* @return If the transaction was a success.
*/
public boolean giveMoney(@NotNull final OfflinePlayer player,
final double amount) {
for (EconomyWrapper wrapper : registered) {
return wrapper.giveMoney(player, amount);
}
return false;
}
/**
* Remove money from a player.
*
* @param player The player.
* @param amount The amount to remove.
* @return If the transaction was a success.
*/
public boolean removeMoney(@NotNull final OfflinePlayer player,
final double amount) {
for (EconomyWrapper wrapper : registered) {
return wrapper.removeMoney(player, amount);
}
return false;
}
/**
* Get the balance of a player.
*
* @param player The player.
* @return The balance.
*/
public double getBalance(@NotNull final OfflinePlayer player) {
for (EconomyWrapper wrapper : registered) {
return wrapper.getBalance(player);
}
return 0;
}
}

View File

@@ -0,0 +1,48 @@
package com.willfp.eco.core.integrations.economy;
import com.willfp.eco.core.integrations.Integration;
import org.bukkit.OfflinePlayer;
import org.jetbrains.annotations.NotNull;
/**
* Wrapper class for economy integrations.
*/
public interface EconomyWrapper extends Integration {
/**
* Get if a player has a certain amount.
*
* @param player The player.
* @param amount The amount.
* @return If the player has the amount.
*/
boolean hasAmount(@NotNull OfflinePlayer player,
double amount);
/**
* Give money to a player.
*
* @param player The player.
* @param amount The amount to give.
* @return If the transaction was a success.
*/
boolean giveMoney(@NotNull OfflinePlayer player,
double amount);
/**
* Remove money from a player.
*
* @param player The player.
* @param amount The amount to remove.
* @return If the transaction was a success.
*/
boolean removeMoney(@NotNull OfflinePlayer player,
double amount);
/**
* Get the balance of a player.
*
* @param player The player.
* @return The balance.
*/
double getBalance(@NotNull OfflinePlayer player);
}

View File

@@ -0,0 +1,20 @@
package com.willfp.eco.core.integrations.hologram;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* Dummy hologram, created if no integrations are present on the server.
*/
class DummyHologram implements Hologram {
@Override
public void remove() {
// Do nothing.
}
@Override
public void setContents(@NotNull final List<String> contents) {
// Do nothing.
}
}

View File

@@ -0,0 +1,22 @@
package com.willfp.eco.core.integrations.hologram;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* Wrapper class for plugin-specific holograms.
*/
public interface Hologram {
/**
* Remove the hologram.
*/
void remove();
/**
* Set the hologram contents.
*
* @param contents The contents.
*/
void setContents(@NotNull List<String> contents);
}

View File

@@ -0,0 +1,45 @@
package com.willfp.eco.core.integrations.hologram;
import lombok.experimental.UtilityClass;
import org.bukkit.Location;
import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Class to handle hologram integrations.
*/
@UtilityClass
public class HologramManager {
/**
* A set of all registered integrations.
*/
private final Set<HologramWrapper> registered = new HashSet<>();
/**
* Register a new integration.
*
* @param integration The integration to register.
*/
public void register(@NotNull final HologramWrapper integration) {
registered.add(integration);
}
/**
* Create hologram.
*
* @param location The location.
* @param contents The contents for the hologram.
* @return The hologram.
*/
public Hologram createHologram(@NotNull final Location location,
@NotNull final List<String> contents) {
for (HologramWrapper wrapper : registered) {
return wrapper.createHologram(location, contents);
}
return new DummyHologram();
}
}

View File

@@ -0,0 +1,22 @@
package com.willfp.eco.core.integrations.hologram;
import com.willfp.eco.core.integrations.Integration;
import org.bukkit.Location;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* Wrapper class for hologram integrations.
*/
public interface HologramWrapper extends Integration {
/**
* Create hologram.
*
* @param location The location.
* @param contents The contents for the hologram.
* @return The hologram.
*/
Hologram createHologram(@NotNull Location location,
@NotNull List<String> contents);
}

View File

@@ -1,5 +1,6 @@
package com.willfp.eco.core.integrations.mcmmo;
import com.willfp.eco.core.integrations.Integration;
import org.bukkit.block.Block;
import org.bukkit.event.Event;
import org.jetbrains.annotations.NotNull;
@@ -7,7 +8,7 @@ import org.jetbrains.annotations.NotNull;
/**
* Wrapper class for mcmmo integrations.
*/
public interface McmmoWrapper {
public interface McmmoWrapper extends Integration {
/**
* Get bonus drop count of block.
*

View File

@@ -8,9 +8,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
/**
* Class to handle placeholder integrations.

View File

@@ -2,7 +2,6 @@ package com.willfp.eco.core.items;
import com.willfp.eco.core.Eco;
import lombok.Getter;
import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit;
import org.bukkit.NamespacedKey;
import org.bukkit.inventory.ItemStack;
@@ -12,7 +11,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.function.Predicate;
/**
* A custom item has 3 components:
* A custom item has 3 components.
*
* <ul>
* <li>The key to identify it</li>

View File

@@ -49,7 +49,7 @@ public class TextureArgParser implements LookupArgParser {
assert testMeta != null;
if (testMeta instanceof SkullMeta skullMeta && finalSkullTexture != null) {
if (testMeta instanceof SkullMeta skullMeta) {
return finalSkullTexture.equalsIgnoreCase(SkullUtils.getSkullTexture(skullMeta));
}

View File

@@ -36,5 +36,5 @@ public abstract class ItemProvider {
* @return The TestableItem, or null if not found.
*/
@Nullable
public abstract TestableItem provideForKey(@NotNull final String key);
public abstract TestableItem provideForKey(@NotNull String key);
}

View File

@@ -2,7 +2,6 @@ package com.willfp.eco.core.recipe.parts;
import com.willfp.eco.core.items.TestableItem;
import lombok.Getter;
import org.apache.commons.lang.Validate;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

View File

@@ -4,6 +4,7 @@ import com.willfp.eco.core.Eco;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
import javax.net.ssl.HttpsURLConnection;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
@@ -11,7 +12,6 @@ import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.function.Consumer;
@@ -40,45 +40,42 @@ public class Paste {
* <p>
* Runs asynchronously to avoid hangups.
*
* @param responseHandler The consumer to accept the response token.
* @param callback The consumer to accept the response token.
*/
public void getHastebinToken(@NotNull final Consumer<String> responseHandler) {
public void getHastebinToken(@NotNull final Consumer<String> callback) {
Eco.getHandler().getEcoPlugin().getScheduler().runAsync(() -> {
try {
String url = "https://hastebin.com/documents";
URL obj = new URL(url);
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
byte[] postData = contents.getBytes(StandardCharsets.UTF_8);
int postDataLength = postData.length;
con.setRequestMethod("POST");
con.setRequestProperty("Content-Type", "application/json");
String requestURL = "https://hastebin.com/documents";
URL url = new URL(requestURL);
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setInstanceFollowRedirects(false);
conn.setRequestMethod("POST");
conn.setRequestProperty("User-Agent", "eco-Hastebin");
conn.setRequestProperty("Content-Length", Integer.toString(postDataLength));
conn.setUseCaches(false);
con.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(con.getOutputStream());
wr.writeBytes(URLEncoder.encode(contents, StandardCharsets.UTF_8));
wr.flush();
wr.close();
String response;
DataOutputStream wr;
BufferedReader iny = new BufferedReader(
new InputStreamReader(con.getInputStream()));
String output;
StringBuilder responseBuilder = new StringBuilder();
wr = new DataOutputStream(conn.getOutputStream());
wr.write(postData);
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
response = reader.readLine();
while ((output = iny.readLine()) != null) {
responseBuilder.append(output);
assert response != null;
if (response.contains("\"key\"")) {
response = response.substring(response.indexOf(":") + 2, response.length() - 2);
callback.accept(response);
}
iny.close();
String responseString = responseBuilder.toString();
responseString = responseString.replace("{\"key\":\"", "");
responseString = responseString.replace("\"}", "");
responseHandler.accept(responseString);
} catch (IOException e) {
responseHandler.accept(e.getMessage());
callback.accept(e.getMessage());
}
responseHandler.accept("");
});
}

View File

@@ -26,9 +26,9 @@ public class UpdateChecker extends PluginDependent<EcoPlugin> {
/**
* Get the latest version of the plugin.
*
* @param consumer The process to run after checking.
* @param callback The process to run after checking.
*/
public void getVersion(@NotNull final Consumer<? super String> consumer) {
public void getVersion(@NotNull final Consumer<? super String> callback) {
this.getPlugin().getScheduler().runAsync(() -> {
try {
InputStream inputStream = new URL(
@@ -37,7 +37,7 @@ public class UpdateChecker extends PluginDependent<EcoPlugin> {
Scanner scanner = new Scanner(inputStream);
if (scanner.hasNext()) {
consumer.accept(scanner.next());
callback.accept(scanner.next());
}
} catch (IOException e) {
this.getPlugin().getLogger().warning("Failed to check for updates: " + e.getMessage());

View File

@@ -2,12 +2,14 @@ package com.willfp.eco.util;
import lombok.experimental.UtilityClass;
import org.apache.commons.lang.Validate;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Player;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
@@ -92,6 +94,21 @@ public class BlockUtils {
blockBreakConsumer.accept(player, block);
}
/**
* Get if a block was placed by a player.
*
* @param block The block.
* @return If placed by a player.
*/
public boolean isPlayerPlaced(@NotNull final Block block) {
Chunk chunk = block.getChunk();
return chunk.getPersistentDataContainer().has(
NamespacedKeyUtils.createEcoKey(Integer.toString(block.getLocation().hashCode(), 16)),
PersistentDataType.INTEGER
);
}
/**
* Initialize the block break function.
*

View File

@@ -5,7 +5,9 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Utilities / API methods for lists.
@@ -21,7 +23,7 @@ public class ListUtils {
* @return The list, filled will null objects.
*/
@NotNull
public <@Nullable T> List<List<T>> create2DList(final int rows,
public static <@Nullable T> List<List<T>> create2DList(final int rows,
final int columns) {
List<List<T>> list = new ArrayList<>(rows);
while (list.size() < rows) {
@@ -34,4 +36,25 @@ public class ListUtils {
return list;
}
/**
* Convert a list potentially containing duplicates to a map where the value is the frequency of the key.
*
* @param list The list.
* @param <T> The type parameter of the list.
* @return The frequency map.
*/
@NotNull
public static <T> Map<T, Integer> listToFrequencyMap(@NotNull final List<T> list) {
Map<T, Integer> frequencyMap = new HashMap<>();
for (T object : list) {
if (frequencyMap.containsKey(object)) {
frequencyMap.put(object, frequencyMap.get(object) + 1);
} else {
frequencyMap.put(object, 1);
}
}
return frequencyMap;
}
}

View File

@@ -1,6 +1,5 @@
package com.willfp.eco.util;
import lombok.experimental.NonFinal;
import lombok.experimental.UtilityClass;
import org.jetbrains.annotations.NotNull;

View File

@@ -2,9 +2,13 @@ package com.willfp.eco.util;
import com.willfp.eco.core.Eco;
import com.willfp.eco.core.Prerequisite;
import com.willfp.eco.core.data.PlayerProfile;
import com.willfp.eco.core.data.keys.PersistentDataKey;
import com.willfp.eco.core.data.keys.PersistentDataKeyType;
import lombok.experimental.UtilityClass;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
@@ -14,6 +18,15 @@ import org.jetbrains.annotations.NotNull;
*/
@UtilityClass
public class PlayerUtils {
/**
* The data key for saved player names.
*/
private static final PersistentDataKey<String> PLAYER_NAME_KEY = new PersistentDataKey<>(
NamespacedKeyUtils.createEcoKey("player_name"),
PersistentDataKeyType.STRING,
"Unknown Player"
);
/**
* Get the audience from a player.
*
@@ -63,4 +76,30 @@ public class PlayerUtils {
}
}
}
/**
* Get saved display name for an offline player.
*
* @param player The player.
* @return The player name.
*/
public String getSavedDisplayName(@NotNull final OfflinePlayer player) {
PlayerProfile profile = PlayerProfile.load(player);
if (player instanceof Player onlinePlayer) {
profile.write(PLAYER_NAME_KEY, onlinePlayer.getDisplayName());
}
return profile.read(PLAYER_NAME_KEY);
}
/**
* Update the saved display name for a player.
*
* @param player The player.
*/
public void updateSavedDisplayName(@NotNull final Player player) {
PlayerProfile profile = PlayerProfile.load(player);
profile.write(PLAYER_NAME_KEY, player.getDisplayName());
}
}

View File

@@ -0,0 +1,47 @@
package com.willfp.eco.util;
import lombok.experimental.UtilityClass;
import org.bukkit.potion.PotionData;
import org.jetbrains.annotations.NotNull;
/**
* Utilities / API methods for potions.
*/
@UtilityClass
public class PotionUtils {
/**
* Get the duration (in ticks) for potion data.
*
* @param data The data.
* @return The duration.
*/
public int getDuration(@NotNull final PotionData data) {
if (data.isExtended()) {
return switch (data.getType()) {
case INSTANT_DAMAGE, INSTANT_HEAL: yield 0;
case POISON, REGEN: yield 1800;
case SLOW_FALLING, WEAKNESS, SLOWNESS: yield 4800;
case TURTLE_MASTER: yield 800;
default: yield 9600;
};
}
if (data.isUpgraded()) {
return switch (data.getType()) {
case INSTANT_DAMAGE, INSTANT_HEAL: yield 0;
case POISON, REGEN: yield 420;
case SLOW_FALLING, WEAKNESS, SLOWNESS: yield 440;
case TURTLE_MASTER: yield 400;
default: yield 1800;
};
}
return switch (data.getType()) {
case INSTANT_DAMAGE, INSTANT_HEAL: yield 0;
case POISON, REGEN: yield 900;
case SLOW_FALLING, WEAKNESS, SLOWNESS: yield 400;
case TURTLE_MASTER: yield 1800;
default: yield 3600;
};
}
}

View File

@@ -0,0 +1,48 @@
package com.willfp.eco.util;
import lombok.experimental.UtilityClass;
import org.apache.commons.lang.Validate;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import java.util.function.Supplier;
/**
* Utilities / API methods for the server.
*/
@UtilityClass
public class ServerUtils {
/**
* The TPS supplier.
*/
private Supplier<Double> tpsSupplier = null;
/**
* Get the current server TPS.
*
* @return The TPS.
*/
public double getTps() {
Validate.notNull(tpsSupplier, "Not initialized!");
double tps = tpsSupplier.get();
if (tps > 20) {
return 20;
} else {
return tps;
}
}
/**
* Initialize the tps supplier function.
*
* @param function The function.
*/
@ApiStatus.Internal
public void initialize(@NotNull final Supplier<Double> function) {
Validate.isTrue(tpsSupplier == null, "Already initialized!");
tpsSupplier = function;
}
}

View File

@@ -1,6 +1,7 @@
package com.willfp.eco.util;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.willfp.eco.core.Prerequisite;
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager;
import lombok.experimental.UtilityClass;
@@ -15,15 +16,21 @@ import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.awt.Color;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static net.md_5.bungee.api.ChatColor.BOLD;
import static net.md_5.bungee.api.ChatColor.COLOR_CHAR;
import static net.md_5.bungee.api.ChatColor.ITALIC;
import static net.md_5.bungee.api.ChatColor.MAGIC;
import static net.md_5.bungee.api.ChatColor.STRIKETHROUGH;
import static net.md_5.bungee.api.ChatColor.UNDERLINE;
/**
* Utilities / API methods for strings.
@@ -61,6 +68,22 @@ public class StringUtils {
.hexColors()
.build();
/**
* Color map.
*/
private static final Map<String, ChatColor> COLOR_MAP = new ImmutableMap.Builder<String, ChatColor>()
.put("&l", BOLD)
.put("&o", ITALIC)
.put("&n", UNDERLINE)
.put("&m", STRIKETHROUGH)
.put("&k", MAGIC)
.put("§l", BOLD)
.put("§o", ITALIC)
.put("§n", UNDERLINE)
.put("§m", STRIKETHROUGH)
.put("§k", MAGIC)
.build();
/**
* Format a list of strings.
* <p>
@@ -251,9 +274,9 @@ public class StringUtils {
if (option == FormatOption.WITH_PLACEHOLDERS) {
processedMessage = PlaceholderManager.translatePlaceholders(processedMessage, player);
}
processedMessage = ChatColor.translateAlternateColorCodes('&', processedMessage);
processedMessage = translateGradients(processedMessage);
processedMessage = translateHexColorCodes(processedMessage);
processedMessage = ChatColor.translateAlternateColorCodes('&', processedMessage);
if (Prerequisite.HAS_PAPER.isMet()) {
processedMessage = translateMiniMessage(processedMessage);
}
@@ -293,26 +316,12 @@ public class StringUtils {
@NotNull final Color end) {
String processedString = string;
List<ChatColor> modifiers = new ArrayList<>();
if (processedString.contains("&l")) {
modifiers.add(ChatColor.BOLD);
for (Map.Entry<String, ChatColor> entry : COLOR_MAP.entrySet()) {
if (processedString.contains(entry.getKey())) {
modifiers.add(entry.getValue());
}
if (processedString.contains("&o")) {
modifiers.add(ChatColor.ITALIC);
processedString = processedString.replace(entry.getKey(), "");
}
if (processedString.contains("&n")) {
modifiers.add(ChatColor.UNDERLINE);
}
if (processedString.contains("&m")) {
modifiers.add(ChatColor.STRIKETHROUGH);
}
if (processedString.contains("&k")) {
modifiers.add(ChatColor.MAGIC);
}
processedString = processedString.replace("&l", "");
processedString = processedString.replace("&o", "");
processedString = processedString.replace("&n", "");
processedString = processedString.replace("&k", "");
processedString = processedString.replace("&m", "");
StringBuilder stringBuilder = new StringBuilder();
ChatColor[] colors = getGradientColors(start, end, processedString.length());

View File

@@ -7,4 +7,5 @@ dependencies {
compileOnly 'org.reflections:reflections:0.9.12'
compileOnly 'net.kyori:adventure-text-minimessage:4.1.0-SNAPSHOT'
compileOnly 'net.kyori:adventure-platform-bukkit:4.0.0'
compileOnly 'com.google.guava:guava:31.0.1-jre'
}

View File

@@ -1,6 +1,6 @@
package com.willfp.eco.internal.config
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.PluginLike
import com.willfp.eco.core.config.interfaces.Config
import com.willfp.eco.core.config.interfaces.JSONConfig
import com.willfp.eco.core.config.wrapper.ConfigFactory
@@ -15,7 +15,7 @@ import org.bukkit.configuration.file.YamlConfiguration
class EcoConfigFactory : ConfigFactory {
override fun createUpdatableYamlConfig(
configName: String,
plugin: EcoPlugin,
plugin: PluginLike,
subDirectoryPath: String,
source: Class<*>,
removeUnused: Boolean,
@@ -33,7 +33,7 @@ class EcoConfigFactory : ConfigFactory {
override fun createUpdatableJSONConfig(
configName: String,
plugin: EcoPlugin,
plugin: PluginLike,
subDirectoryPath: String,
source: Class<*>,
removeUnused: Boolean,
@@ -51,7 +51,7 @@ class EcoConfigFactory : ConfigFactory {
override fun createLoadableJSONConfig(
configName: String,
plugin: EcoPlugin,
plugin: PluginLike,
subDirectoryPath: String,
source: Class<*>
): JSONConfig {
@@ -65,7 +65,7 @@ class EcoConfigFactory : ConfigFactory {
override fun createLoadableYamlConfig(
configName: String,
plugin: EcoPlugin,
plugin: PluginLike,
subDirectoryPath: String,
source: Class<*>
): Config {

View File

@@ -4,7 +4,6 @@ import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.willfp.eco.core.config.interfaces.JSONConfig
import com.willfp.eco.util.StringUtils
import org.apache.commons.lang.Validate
import java.util.*
import java.util.concurrent.ConcurrentHashMap
@@ -124,8 +123,7 @@ open class EcoJSONConfigWrapper : JSONConfig {
override fun getSubsection(path: String): JSONConfig {
val subsection = getSubsectionOrNull(path)
Validate.notNull(subsection)
return subsection!!
return subsection ?: EcoJSONConfigSection(emptyMap())
}
override fun getSubsectionOrNull(path: String): JSONConfig? {

View File

@@ -1,16 +1,20 @@
package com.willfp.eco.internal.config.json
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.PluginLike
import com.willfp.eco.core.config.interfaces.LoadableConfig
import org.jetbrains.annotations.NotNull
import java.io.*
import java.io.File
import java.io.FileNotFoundException
import java.io.FileOutputStream
import java.io.FileReader
import java.io.IOException
import java.io.OutputStream
import java.nio.file.Files
import java.nio.file.StandardOpenOption
@Suppress("UNCHECKED_CAST")
open class EcoLoadableJSONConfig(
configName: String,
private val plugin: EcoPlugin,
private val plugin: PluginLike,
private val subDirectoryPath: String,
val source: Class<*>
) : EcoJSONConfigWrapper(), LoadableConfig {
@@ -77,7 +81,7 @@ open class EcoLoadableJSONConfig(
}
init {
val directory: File = File(this.plugin.dataFolder, subDirectoryPath)
val directory = File(this.plugin.dataFolder, subDirectoryPath)
if (!directory.exists()) {
directory.mkdirs()
}

View File

@@ -1,6 +1,6 @@
package com.willfp.eco.internal.config.json
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.PluginLike
import org.bukkit.configuration.InvalidConfigurationException
import org.bukkit.configuration.file.YamlConfiguration
import java.io.BufferedReader
@@ -10,7 +10,7 @@ import java.nio.charset.StandardCharsets
open class EcoUpdatableJSONConfig(
configName: String,
plugin: EcoPlugin,
plugin: PluginLike,
subDirectoryPath: String,
source: Class<*>,
private val removeUnused: Boolean,

View File

@@ -1,6 +1,6 @@
package com.willfp.eco.internal.config.yaml
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.PluginLike
import com.willfp.eco.core.config.interfaces.LoadableConfig
import com.willfp.eco.core.config.interfaces.WrappedYamlConfiguration
import org.bukkit.configuration.InvalidConfigurationException
@@ -12,7 +12,7 @@ import java.io.OutputStream
open class EcoLoadableYamlConfig(
configName: String,
private val plugin: EcoPlugin,
private val plugin: PluginLike,
private val subDirectoryPath: String,
val source: Class<*>
) : EcoYamlConfigWrapper<YamlConfiguration>(), WrappedYamlConfiguration, LoadableConfig {

View File

@@ -1,6 +1,6 @@
package com.willfp.eco.internal.config.yaml
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.PluginLike
import org.bukkit.configuration.InvalidConfigurationException
import org.bukkit.configuration.file.YamlConfiguration
import java.io.BufferedReader
@@ -10,7 +10,7 @@ import java.nio.charset.StandardCharsets
class EcoUpdatableYamlConfig(
configName: String,
plugin: EcoPlugin,
plugin: PluginLike,
subDirectoryPath: String,
source: Class<*>,
private val removeUnused: Boolean,
@@ -27,9 +27,9 @@ class EcoUpdatableYamlConfig(
if (newConfig.getKeys(true) == this.handle.getKeys(true)) {
return
}
newConfig.getKeys(true).forEach { key: String ->
newConfig.getKeys(true).forEach { key ->
if (!this.handle.getKeys(true).contains(key)) {
if (updateBlacklist.stream().noneMatch { s: String -> key.contains(s) }) {
if (updateBlacklist.stream().noneMatch { key.contains(it) }) {
this.handle.set(key, newConfig[key])
}
}
@@ -67,7 +67,7 @@ class EcoUpdatableYamlConfig(
}
init {
this.updateBlacklist.removeIf { obj: String -> obj.isEmpty() }
this.updateBlacklist.removeIf { it.isEmpty() }
plugin.configHandler.addConfig(this)
update()
}

View File

@@ -2,7 +2,6 @@ package com.willfp.eco.internal.config.yaml
import com.willfp.eco.core.config.interfaces.Config
import com.willfp.eco.util.StringUtils
import org.apache.commons.lang.Validate
import org.bukkit.configuration.ConfigurationSection
import org.bukkit.configuration.file.YamlConfiguration
import java.io.StringReader
@@ -52,8 +51,7 @@ open class EcoYamlConfigWrapper<T : ConfigurationSection> : Config {
override fun getSubsection(path: String): Config {
val subsection = getSubsectionOrNull(path)
Validate.notNull(subsection)
return subsection!!
return subsection ?: EcoYamlConfigSection(YamlConfiguration())
}
override fun getSubsectionOrNull(path: String): Config? {

View File

@@ -0,0 +1,17 @@
package com.willfp.eco.internal.data
import com.willfp.eco.core.data.PlayerProfile
import com.willfp.eco.core.data.keys.PersistentDataKey
class EcoPlayerProfile(
val data: MutableMap<PersistentDataKey<*>, Any>
) : PlayerProfile {
override fun <T : Any> write(key: PersistentDataKey<T>, value: T) {
this.data[key] = value
}
override fun <T : Any> read(key: PersistentDataKey<T>): T {
@Suppress("UNCHECKED_CAST")
return this.data[key] as T? ?: key.defaultValue
}
}

View File

@@ -1,5 +1,6 @@
package com.willfp.eco.internal.display
import com.google.common.util.concurrent.ThreadFactoryBuilder
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.display.Display
import com.willfp.eco.core.display.DisplayHandler
@@ -10,16 +11,11 @@ import org.bukkit.NamespacedKey
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataType
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
class EcoDisplayHandler(plugin: EcoPlugin) : DisplayHandler {
/**
* All registered display modules.
*/
private val registeredModules = mutableMapOf<DisplayPriority, MutableList<DisplayModule>>()
/**
* NamespacedKey for finalizing.
*/
private val finalizeKey: NamespacedKey = plugin.namespacedKeyFactory.create("finalized")
init {

View File

@@ -12,10 +12,9 @@ import org.bukkit.event.player.PlayerExpChangeEvent
import org.bukkit.inventory.ItemStack
import org.bukkit.util.Vector
open class EcoDropQueue(player: Player) : InternalDropQueue {
open class EcoDropQueue(val player: Player) : InternalDropQueue {
val items = mutableListOf<ItemStack>()
var xp: Int = 0
val player: Player
var loc: Location
var hasTelekinesis = false
@@ -79,7 +78,6 @@ open class EcoDropQueue(player: Player) : InternalDropQueue {
}
init {
this.player = player
loc = player.location
}
}

View File

@@ -36,8 +36,9 @@ class Skull : SkullProxy {
profile = meta.javaClass.getDeclaredField("profile")
profile.isAccessible = true
}
val profile = profile[meta] as GameProfile?
val property = profile?.properties?.get("textures") as Property?
return property?.value
val profile = profile[meta] as GameProfile? ?: return null
val properties = profile.properties ?: return null
val prop = properties["textures"] ?: return null
return prop.toMutableList().firstOrNull()?.name
}
}

View File

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

View File

@@ -36,8 +36,9 @@ class Skull : SkullProxy {
profile = meta.javaClass.getDeclaredField("profile")
profile.isAccessible = true
}
val profile = profile[meta] as GameProfile?
val property = profile?.properties?.get("textures") as Property?
return property?.value
val profile = profile[meta] as GameProfile? ?: return null
val properties = profile.properties ?: return null
val prop = properties["textures"] ?: return null
return prop.toMutableList().firstOrNull()?.name
}
}

View File

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

View File

@@ -7,11 +7,11 @@ dependencies {
exclude group: 'net.kyori', module: 'adventure-api'
}
compileOnly 'net.kyori:adventure-platform-bukkit:4.0.0'
compileOnly 'net.kyori:adventure-api:4.9.1'
compileOnly 'net.kyori:adventure-api:4.9.2'
compileOnly 'net.kyori:adventure-text-serializer-gson:4.9.2'
compileOnly 'net.kyori:adventure-text-serializer-legacy:4.8.1'
compileOnly 'org.apache.maven:maven-artifact:3.0.3'
compileOnly 'com.google.code.gson:gson:2.8.7'
compileOnly 'net.kyori:adventure-text-serializer-legacy:4.9.2'
compileOnly 'org.apache.maven:maven-artifact:3.8.1'
compileOnly 'com.google.code.gson:gson:2.8.8'
compileOnly 'org.spigotmc:spigot-api:1.17.1-R0.1-SNAPSHOT'
compileOnly project(":eco-core:core-proxy")
compileOnly project(":eco-core:core-backend")
@@ -19,17 +19,27 @@ dependencies {
compileOnly 'com.sk89q.worldguard:worldguard-bukkit:7.0.7-SNAPSHOT'
compileOnly 'com.github.TechFortress:GriefPrevention:16.17.1'
compileOnly 'com.massivecraft:Factions:1.6.9.5-U0.5.10'
compileOnly 'com.github.cryptomorin:kingdoms:1.10.14'
compileOnly 'com.github.TownyAdvanced:Towny:0.97.2.5'
compileOnly 'com.github.angeschossen:LandsAPI:4.7.3'
compileOnly 'com.github.cryptomorin:kingdoms:1.11.9'
compileOnly 'com.github.TownyAdvanced:Towny:0.97.2.6'
compileOnly 'com.github.angeschossen:LandsAPI:5.15.2'
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'
compileOnly 'me.clip:placeholderapi:2.10.10'
compileOnly 'com.willfp:Oraxen:e1f4003d8d'
compileOnly 'com.github.oraxen:oraxen:bd81ace154'
compileOnly 'com.github.brcdev-minecraft:shopgui-api:2.2.0'
compileOnly 'com.github.LoneDev6:API-ItemsAdder:2.4.7'
compileOnly 'com.arcaniax:HeadDatabase-API:1.3.0'
compileOnly 'org.jetbrains.exposed:exposed-core:0.35.1'
compileOnly 'org.jetbrains.exposed:exposed-dao:0.35.1'
compileOnly 'org.jetbrains.exposed:exposed-jdbc:0.35.1'
compileOnly 'mysql:mysql-connector-java:8.0.25'
compileOnly 'com.gmail.filoghost.holographicdisplays:holographicdisplays-api:2.4.0'
compileOnly 'net.essentialsx:EssentialsX:2.19.0'
compileOnly 'com.bgsoftware:SuperiorSkyblockAPI:latest'
compileOnly 'com.github.MilkBowl:VaultAPI:1.7'
compileOnly 'world.bentobox:bentobox:1.17.3-SNAPSHOT'
compileOnly 'com.google.guava:guava:31.0.1-jre'
// CombatLogX V10 + NewbieHelper Expansion
compileOnly 'com.SirBlobman.combatlogx:CombatLogX-API:10.0.0.0-SNAPSHOT'

View File

@@ -4,6 +4,8 @@ import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.Handler
import com.willfp.eco.core.config.updating.ConfigHandler
import com.willfp.eco.core.config.wrapper.ConfigFactory
import com.willfp.eco.core.data.PlayerProfileHandler
import com.willfp.eco.core.data.keys.KeyRegistry
import com.willfp.eco.core.drops.DropQueueFactory
import com.willfp.eco.core.events.EventManager
import com.willfp.eco.core.extensions.ExtensionLoader
@@ -34,6 +36,8 @@ import com.willfp.eco.internal.proxy.EcoProxyFactory
import com.willfp.eco.internal.requirement.EcoRequirementFactory
import com.willfp.eco.internal.scheduling.EcoScheduler
import com.willfp.eco.proxy.FastItemStackFactoryProxy
import com.willfp.eco.spigot.data.EcoKeyRegistry
import com.willfp.eco.spigot.data.EcoPlayerProfileHandler
import com.willfp.eco.spigot.integrations.bstats.MetricHandler
import net.kyori.adventure.platform.bukkit.BukkitAudiences
import org.bukkit.inventory.ItemStack
@@ -44,6 +48,8 @@ class EcoHandler : EcoSpigotPlugin(), Handler {
private val cleaner = EcoCleaner()
private val requirementFactory = EcoRequirementFactory()
private var adventure: BukkitAudiences? = null
private val keyRegistry = EcoKeyRegistry(this)
private val playerProfileHandler = EcoPlayerProfileHandler(this)
override fun createScheduler(plugin: EcoPlugin): Scheduler {
return EcoScheduler(plugin)
@@ -133,6 +139,14 @@ class EcoHandler : EcoSpigotPlugin(), Handler {
return adventure
}
override fun getKeyRegistry(): KeyRegistry {
return keyRegistry
}
override fun getPlayerProfileHandler(): PlayerProfileHandler {
return playerProfileHandler
}
fun setAdventure(adventure: BukkitAudiences) {
this.adventure = adventure
}

View File

@@ -6,9 +6,12 @@ import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.Prerequisite
import com.willfp.eco.core.display.Display
import com.willfp.eco.core.integrations.IntegrationLoader
import com.willfp.eco.core.integrations.afk.AFKManager
import com.willfp.eco.core.integrations.anticheat.AnticheatManager
import com.willfp.eco.core.integrations.antigrief.AntigriefManager
import com.willfp.eco.core.integrations.customitems.CustomItemsManager
import com.willfp.eco.core.integrations.economy.EconomyManager
import com.willfp.eco.core.integrations.hologram.HologramManager
import com.willfp.eco.core.integrations.mcmmo.McmmoManager
import com.willfp.eco.core.integrations.shop.ShopManager
import com.willfp.eco.core.items.Items
@@ -19,8 +22,21 @@ import com.willfp.eco.internal.drops.DropManager
import com.willfp.eco.proxy.BlockBreakProxy
import com.willfp.eco.proxy.FastItemStackFactoryProxy
import com.willfp.eco.proxy.SkullProxy
import com.willfp.eco.proxy.TPSProxy
import com.willfp.eco.spigot.arrows.ArrowDataListener
import com.willfp.eco.spigot.display.*
import com.willfp.eco.spigot.data.DataListener
import com.willfp.eco.spigot.data.EcoPlayerProfileHandler
import com.willfp.eco.spigot.data.PlayerBlockListener
import com.willfp.eco.spigot.data.storage.DataHandler
import com.willfp.eco.spigot.data.storage.MySQLDataHandler
import com.willfp.eco.spigot.data.storage.YamlDataHandler
import com.willfp.eco.spigot.display.PacketAutoRecipe
import com.willfp.eco.spigot.display.PacketChat
import com.willfp.eco.spigot.display.PacketOpenWindowMerchant
import com.willfp.eco.spigot.display.PacketSetCreativeSlot
import com.willfp.eco.spigot.display.PacketSetSlot
import com.willfp.eco.spigot.display.PacketWindowItems
import com.willfp.eco.spigot.display.frame.clearFrames
import com.willfp.eco.spigot.drops.CollatedRunnable
import com.willfp.eco.spigot.eventlisteners.EntityDeathByEntityListeners
import com.willfp.eco.spigot.eventlisteners.NaturalExpGainListeners
@@ -28,24 +44,44 @@ import com.willfp.eco.spigot.eventlisteners.PlayerJumpListeners
import com.willfp.eco.spigot.eventlisteners.armor.ArmorChangeEventListeners
import com.willfp.eco.spigot.eventlisteners.armor.ArmorListener
import com.willfp.eco.spigot.gui.GUIListener
import com.willfp.eco.spigot.integrations.anticheat.*
import com.willfp.eco.spigot.integrations.antigrief.*
import com.willfp.eco.spigot.integrations.afk.AFKIntegrationCMI
import com.willfp.eco.spigot.integrations.afk.AFKIntegrationEssentials
import com.willfp.eco.spigot.integrations.anticheat.AnticheatAAC
import com.willfp.eco.spigot.integrations.anticheat.AnticheatAlice
import com.willfp.eco.spigot.integrations.anticheat.AnticheatMatrix
import com.willfp.eco.spigot.integrations.anticheat.AnticheatNCP
import com.willfp.eco.spigot.integrations.anticheat.AnticheatSpartan
import com.willfp.eco.spigot.integrations.anticheat.AnticheatVulcan
import com.willfp.eco.spigot.integrations.antigrief.AntigriefBentoBox
import com.willfp.eco.spigot.integrations.antigrief.AntigriefCombatLogXV10
import com.willfp.eco.spigot.integrations.antigrief.AntigriefCombatLogXV11
import com.willfp.eco.spigot.integrations.antigrief.AntigriefFactionsUUID
import com.willfp.eco.spigot.integrations.antigrief.AntigriefGriefPrevention
import com.willfp.eco.spigot.integrations.antigrief.AntigriefKingdoms
import com.willfp.eco.spigot.integrations.antigrief.AntigriefLands
import com.willfp.eco.spigot.integrations.antigrief.AntigriefSuperiorSkyblock2
import com.willfp.eco.spigot.integrations.antigrief.AntigriefTowny
import com.willfp.eco.spigot.integrations.antigrief.AntigriefWorldGuard
import com.willfp.eco.spigot.integrations.customitems.CustomItemsHeadDatabase
import com.willfp.eco.spigot.integrations.customitems.CustomItemsItemsAdder
import com.willfp.eco.spigot.integrations.customitems.CustomItemsOraxen
import com.willfp.eco.spigot.integrations.economy.EconomyVault
import com.willfp.eco.spigot.integrations.hologram.HologramCMI
import com.willfp.eco.spigot.integrations.hologram.HologramGHolo
import com.willfp.eco.spigot.integrations.hologram.HologramHolographicDisplays
import com.willfp.eco.spigot.integrations.mcmmo.McmmoIntegrationImpl
import com.willfp.eco.spigot.integrations.multiverseinventories.MultiverseInventoriesIntegration
import com.willfp.eco.spigot.integrations.shop.ShopShopGuiPlus
import com.willfp.eco.spigot.recipes.ShapedRecipeListener
import com.willfp.eco.util.BlockUtils
import com.willfp.eco.util.ServerUtils
import com.willfp.eco.util.SkullUtils
import net.kyori.adventure.platform.bukkit.BukkitAudiences
import net.milkbowl.vault.economy.Economy
import org.bukkit.Bukkit
import org.bukkit.Material
import org.bukkit.block.Block
import org.bukkit.entity.Player
import org.bukkit.event.Listener
import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.SkullMeta
abstract class EcoSpigotPlugin : EcoPlugin(
773,
@@ -53,24 +89,32 @@ abstract class EcoSpigotPlugin : EcoPlugin(
"com.willfp.eco.proxy",
"&a"
) {
lateinit var dataHandler: DataHandler
init {
Items.registerArgParser(EnchantmentArgParser())
Items.registerArgParser(TextureArgParser())
val skullProxy = getProxy(SkullProxy::class.java)
SkullUtils.initialize(
{ meta: SkullMeta, base64: String -> skullProxy.setSkullTexture(meta, base64) },
{ meta: SkullMeta -> skullProxy.getSkullTexture(meta) }
{ meta, base64 -> skullProxy.setSkullTexture(meta, base64) },
{ meta -> skullProxy.getSkullTexture(meta) }
)
val blockBreakProxy = getProxy(BlockBreakProxy::class.java)
BlockUtils.initialize { player: Player, block: Block -> blockBreakProxy.breakBlock(player, block) }
BlockUtils.initialize { player, block -> blockBreakProxy.breakBlock(player, block) }
val tpsProxy = getProxy(TPSProxy::class.java)
ServerUtils.initialize { tpsProxy.getTPS() }
postInit()
}
private fun postInit() {
Display.handler = EcoDisplayHandler(this)
Display.setHandler(EcoDisplayHandler(this))
this.dataHandler = if (this.configYml.getBool("mysql.enabled"))
MySQLDataHandler(this) else YamlDataHandler(this)
}
override fun handleEnable() {
@@ -106,6 +150,19 @@ abstract class EcoSpigotPlugin : EcoPlugin(
override fun handleReload() {
CollatedRunnable(this)
DropManager.update(this)
this.scheduler.runTimer(
{ clearFrames() },
this.configYml.getInt("display-frame-ttl").toLong(),
this.configYml.getInt("display-frame-ttl").toLong()
)
this.scheduler.runTimer(
{
(Eco.getHandler().playerProfileHandler as EcoPlayerProfileHandler)
.autosave(this.configYml.getBool("autosave.async"))
},
this.configYml.getInt("autosave.ticks").toLong(),
this.configYml.getInt("autosave.ticks").toLong()
)
}
override fun handleAfterLoad() {
@@ -116,6 +173,8 @@ abstract class EcoSpigotPlugin : EcoPlugin(
override fun loadIntegrationLoaders(): List<IntegrationLoader> {
return listOf(
// AntiGrief
IntegrationLoader("SuperiorSkyblock2") { AntigriefManager.register(AntigriefSuperiorSkyblock2()) },
IntegrationLoader("BentoBox") { AntigriefManager.register(AntigriefBentoBox()) },
IntegrationLoader("WorldGuard") { AntigriefManager.register(AntigriefWorldGuard()) },
IntegrationLoader("GriefPrevention") { AntigriefManager.register(AntigriefGriefPrevention()) },
IntegrationLoader("FactionsUUID") { AntigriefManager.register(AntigriefFactionsUUID()) },
@@ -140,6 +199,7 @@ abstract class EcoSpigotPlugin : EcoPlugin(
IntegrationLoader("NoCheatPlus") { AnticheatManager.register(this, AnticheatNCP()) },
IntegrationLoader("Spartan") { AnticheatManager.register(this, AnticheatSpartan()) },
IntegrationLoader("Vulcan") { AnticheatManager.register(this, AnticheatVulcan()) },
IntegrationLoader("Alice") { AnticheatManager.register(this, AnticheatAlice()) },
// Custom Items
IntegrationLoader("Oraxen") { CustomItemsManager.register(CustomItemsOraxen()) },
@@ -149,8 +209,30 @@ abstract class EcoSpigotPlugin : EcoPlugin(
// Shop
IntegrationLoader("ShopGUIPlus") { ShopManager.register(ShopShopGuiPlus()) },
// Hologram
IntegrationLoader("HolographicDisplays") { HologramManager.register(HologramHolographicDisplays(this)) },
IntegrationLoader("CMI") { HologramManager.register(HologramCMI()) },
IntegrationLoader("GHolo") { HologramManager.register(HologramGHolo()) },
// AFK
IntegrationLoader("Essentials") { AFKManager.register(AFKIntegrationEssentials()) },
IntegrationLoader("CMI") { AFKManager.register(AFKIntegrationCMI()) },
// Economy
IntegrationLoader("Vault") {
val rsp = Bukkit.getServer().servicesManager.getRegistration(Economy::class.java)
if (rsp != null) {
EconomyManager.register(EconomyVault(rsp.provider))
}
},
// Misc
IntegrationLoader("mcMMO") { McmmoManager.register(McmmoIntegrationImpl()) }
IntegrationLoader("mcMMO") { McmmoManager.register(McmmoIntegrationImpl()) },
IntegrationLoader("Multiverse-Inventories") {
this.eventManager.registerListener(
MultiverseInventoriesIntegration(this)
)
}
)
}
@@ -174,7 +256,9 @@ abstract class EcoSpigotPlugin : EcoPlugin(
PlayerJumpListeners(),
GUIListener(this),
ArrowDataListener(this),
ArmorChangeEventListeners(this)
ArmorChangeEventListeners(this),
DataListener(),
PlayerBlockListener(this)
)
}
}

View File

@@ -0,0 +1,21 @@
package com.willfp.eco.spigot.data
import com.willfp.eco.core.Eco
import com.willfp.eco.util.PlayerUtils
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.event.player.PlayerJoinEvent
import org.bukkit.event.player.PlayerQuitEvent
class DataListener : Listener {
@EventHandler
fun onLeave(event: PlayerQuitEvent) {
PlayerUtils.updateSavedDisplayName(event.player)
Eco.getHandler().playerProfileHandler.savePlayer(event.player.uniqueId)
}
@EventHandler
fun onJoin(event: PlayerJoinEvent) {
PlayerUtils.updateSavedDisplayName(event.player)
}
}

View File

@@ -0,0 +1,48 @@
package com.willfp.eco.spigot.data
import com.willfp.eco.core.data.keys.KeyRegistry
import com.willfp.eco.core.data.keys.PersistentDataKey
import com.willfp.eco.core.data.keys.PersistentDataKeyType
import com.willfp.eco.spigot.EcoSpigotPlugin
import org.bukkit.NamespacedKey
class EcoKeyRegistry(
private val plugin: EcoSpigotPlugin
) : KeyRegistry {
private val registry = mutableMapOf<NamespacedKey, PersistentDataKey<*>>()
override fun registerKey(key: PersistentDataKey<*>) {
if (this.registry.containsKey(key.key)) {
this.registry.remove(key.key)
}
validateKey(key)
this.registry[key.key] = key
plugin.dataHandler.updateKeys()
}
override fun getRegisteredKeys(): MutableSet<PersistentDataKey<*>> {
return registry.values.toMutableSet()
}
private fun <T> validateKey(key: PersistentDataKey<T>) {
when (key.type) {
PersistentDataKeyType.INT -> if (key.defaultValue !is Int) {
throw IllegalArgumentException("Invalid Data Type! Should be Int")
}
PersistentDataKeyType.DOUBLE -> if (key.defaultValue !is Double) {
throw IllegalArgumentException("Invalid Data Type! Should be Double")
}
PersistentDataKeyType.BOOLEAN -> if (key.defaultValue !is Boolean) {
throw IllegalArgumentException("Invalid Data Type! Should be Boolean")
}
PersistentDataKeyType.STRING -> if (key.defaultValue !is String) {
throw IllegalArgumentException("Invalid Data Type! Should be String")
}
else -> throw NullPointerException("Null value found!")
}
}
}

View File

@@ -0,0 +1,83 @@
package com.willfp.eco.spigot.data
import com.willfp.eco.core.Eco
import com.willfp.eco.core.data.PlayerProfile
import com.willfp.eco.core.data.PlayerProfileHandler
import com.willfp.eco.core.data.keys.PersistentDataKey
import com.willfp.eco.internal.data.EcoPlayerProfile
import com.willfp.eco.spigot.EcoSpigotPlugin
import org.bukkit.Bukkit
import java.util.*
class EcoPlayerProfileHandler(
private val plugin: EcoSpigotPlugin
) : PlayerProfileHandler {
private val loaded = mutableMapOf<UUID, PlayerProfile>()
private val handler = plugin.dataHandler
override fun load(uuid: UUID): PlayerProfile {
val found = loaded[uuid]
if (found != null) {
return found
}
val data = mutableMapOf<PersistentDataKey<*>, Any>()
for (key in Eco.getHandler().keyRegistry.registeredKeys) {
data[key] = handler.read(uuid, key.key) ?: key.defaultValue
}
val profile = EcoPlayerProfile(data)
loaded[uuid] = profile
return profile
}
override fun savePlayer(uuid: UUID) {
writeToHandler(uuid)
saveToHandler()
}
private fun writeToHandler(uuid: UUID) {
val profile = load(uuid)
for (key in Eco.getHandler().keyRegistry.registeredKeys) {
handler.write(uuid, key.key, profile.read(key) ?: key.defaultValue)
}
}
private fun saveToHandler() {
handler.save()
}
override fun saveAll(async: Boolean) {
val saver = {
for ((uuid, _) in loaded) {
writeToHandler(uuid)
}
saveToHandler()
}
if (async) {
plugin.scheduler.runAsync(saver)
} else {
saver.invoke()
}
}
fun autosave(async: Boolean) {
if (Bukkit.getOnlinePlayers().isEmpty()) {
return
}
if (plugin.configYml.getBool("autosave.log")) {
plugin.logger.info("Auto-Saving player data!")
}
saveAll(async)
if (plugin.configYml.getBool("autosave.log")) {
plugin.logger.info("Saved player data!")
}
}
}

View File

@@ -0,0 +1,53 @@
package com.willfp.eco.spigot.data
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.util.NamespacedKeyUtils
import org.bukkit.block.Block
import org.bukkit.event.EventHandler
import org.bukkit.event.EventPriority
import org.bukkit.event.Listener
import org.bukkit.event.block.BlockBreakEvent
import org.bukkit.event.block.BlockMultiPlaceEvent
import org.bukkit.event.block.BlockPlaceEvent
import org.bukkit.persistence.PersistentDataType
class PlayerBlockListener(
private val plugin: EcoPlugin
) : Listener {
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
fun onPlace(event: BlockPlaceEvent) {
val block = event.blockPlaced
writeKey(block)
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
fun onPlace(event: BlockMultiPlaceEvent) {
val block = event.blockPlaced
writeKey(block)
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
fun onBreak(event: BlockBreakEvent) {
val block = event.block
this.plugin.scheduler.run {
removeKey(block)
}
}
private fun writeKey(block: Block) {
val loc = block.location.hashCode().toString(16)
block.chunk.persistentDataContainer.set(
plugin.namespacedKeyFactory.create(loc.lowercase()),
PersistentDataType.INTEGER,
1
)
}
private fun removeKey(block: Block) {
val loc = block.location.hashCode().toString(16)
block.chunk.persistentDataContainer.remove(plugin.namespacedKeyFactory.create(loc.lowercase()))
}
}

View File

@@ -0,0 +1,13 @@
package com.willfp.eco.spigot.data.storage
import org.bukkit.NamespacedKey
import java.util.*
interface DataHandler {
fun save()
fun <T> write(uuid: UUID, key: NamespacedKey, value: T)
fun <T> read(uuid: UUID, key: NamespacedKey): T?
fun updateKeys()
}

View File

@@ -0,0 +1,123 @@
package com.willfp.eco.spigot.data.storage
import com.willfp.eco.core.Eco
import com.willfp.eco.core.data.keys.PersistentDataKey
import com.willfp.eco.core.data.keys.PersistentDataKeyType
import com.willfp.eco.spigot.EcoSpigotPlugin
import org.bukkit.NamespacedKey
import org.jetbrains.exposed.dao.id.UUIDTable
import org.jetbrains.exposed.sql.BooleanColumnType
import org.jetbrains.exposed.sql.Column
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.DoubleColumnType
import org.jetbrains.exposed.sql.IntegerColumnType
import org.jetbrains.exposed.sql.ResultRow
import org.jetbrains.exposed.sql.SchemaUtils
import org.jetbrains.exposed.sql.VarCharColumnType
import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update
import java.util.UUID
@Suppress("UNCHECKED_CAST")
class MySQLDataHandler(
plugin: EcoSpigotPlugin
) : DataHandler {
private val columns = mutableMapOf<String, Column<*>>()
init {
Database.connect(
"jdbc:mysql://" +
"${plugin.configYml.getString("mysql.host")}:" +
"${plugin.configYml.getString("mysql.port")}/" +
plugin.configYml.getString("mysql.database"),
driver = "com.mysql.cj.jdbc.Driver",
user = plugin.configYml.getString("mysql.user"),
password = plugin.configYml.getString("mysql.password")
)
transaction {
SchemaUtils.create(Players)
}
}
override fun updateKeys() {
transaction {
for (key in Eco.getHandler().keyRegistry.registeredKeys) {
registerColumn(key, Players)
}
SchemaUtils.createMissingTablesAndColumns(Players)
}
}
override fun save() {
// Do nothing
}
override fun <T> write(uuid: UUID, key: NamespacedKey, value: T) {
transaction {
getPlayer(uuid)
val column: Column<T> = getColumn(key.toString()) as Column<T>
Players.update({ Players.id eq uuid }) {
it[column] = value
}
}
}
override fun <T> read(uuid: UUID, key: NamespacedKey): T? {
var value: T? = null
transaction {
val player = getPlayer(uuid)
value = player[getColumn(key.toString())] as T?
}
return value
}
object Players : UUIDTable("eco_players") {
}
private fun <T> registerColumn(key: PersistentDataKey<T>, table: UUIDTable) {
table.apply {
if (this.columns.stream().anyMatch { it.name == key.key.toString() }) {
return@apply
}
when (key.type) {
PersistentDataKeyType.INT -> registerColumn<Int>(key.key.toString(), IntegerColumnType())
.default(key.defaultValue as Int)
PersistentDataKeyType.DOUBLE -> registerColumn<Double>(key.key.toString(), DoubleColumnType())
.default(key.defaultValue as Double)
PersistentDataKeyType.BOOLEAN -> registerColumn<Boolean>(key.key.toString(), BooleanColumnType())
.default(key.defaultValue as Boolean)
PersistentDataKeyType.STRING -> registerColumn<String>(key.key.toString(), VarCharColumnType(512))
.default(key.defaultValue as String)
else -> throw NullPointerException("Null value found!")
}
}
}
private fun getColumn(name: String): Column<*> {
val cached = columns[name]
if (cached != null) {
return cached
}
columns[name] = Players.columns.stream().filter { it.name == name }.findFirst().get()
return getColumn(name)
}
private fun getPlayer(uuid: UUID): ResultRow {
Players.select { Players.id eq uuid }.firstOrNull() ?: run {
Players.insert {
it[id] = uuid
}
}
return Players.select { Players.id eq uuid }.first()
}
}

View File

@@ -0,0 +1,37 @@
package com.willfp.eco.spigot.data.storage
import com.willfp.eco.core.config.yaml.YamlBaseConfig
import com.willfp.eco.spigot.EcoSpigotPlugin
import org.bukkit.NamespacedKey
import java.util.*
@Suppress("UNCHECKED_CAST")
class YamlDataHandler(
plugin: EcoSpigotPlugin
) : DataHandler {
private val dataYml = DataYml(plugin)
override fun save() {
dataYml.save()
}
override fun updateKeys() {
// Do nothing
}
override fun <T> write(uuid: UUID, key: NamespacedKey, value: T) {
dataYml.set("player.$uuid.$key", value)
}
override fun <T> read(uuid: UUID, key: NamespacedKey): T? {
return dataYml.get("player.$uuid.$key") as T?
}
class DataYml(
plugin: EcoSpigotPlugin
) : YamlBaseConfig(
"data",
false,
plugin
)
}

View File

@@ -6,6 +6,8 @@ import com.comphenix.protocol.events.PacketEvent
import com.willfp.eco.core.AbstractPacketAdapter
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.display.Display
import com.willfp.eco.spigot.display.frame.DisplayFrame
import com.willfp.eco.spigot.display.frame.lastDisplayFrame
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
@@ -21,5 +23,7 @@ class PacketSetCreativeSlot(plugin: EcoPlugin) :
itemStack!!
)
}
player.lastDisplayFrame = DisplayFrame.EMPTY
}
}

View File

@@ -6,6 +6,8 @@ import com.comphenix.protocol.events.PacketEvent
import com.willfp.eco.core.AbstractPacketAdapter
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.display.Display
import com.willfp.eco.spigot.display.frame.DisplayFrame
import com.willfp.eco.spigot.display.frame.lastDisplayFrame
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
@@ -20,5 +22,7 @@ class PacketSetSlot(plugin: EcoPlugin) : AbstractPacketAdapter(plugin, PacketTyp
item!!, player
)
}
player.lastDisplayFrame = DisplayFrame.EMPTY
}
}

View File

@@ -1,52 +1,148 @@
package com.willfp.eco.spigot.display
import com.comphenix.protocol.PacketType
import com.comphenix.protocol.ProtocolLibrary
import com.comphenix.protocol.events.PacketContainer
import com.comphenix.protocol.events.PacketEvent
import com.google.common.util.concurrent.ThreadFactoryBuilder
import com.willfp.eco.core.AbstractPacketAdapter
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.display.Display
import com.willfp.eco.core.fast.FastItemStack
import com.willfp.eco.spigot.display.frame.DisplayFrame
import com.willfp.eco.spigot.display.frame.HashedItem
import com.willfp.eco.spigot.display.frame.lastDisplayFrame
import com.willfp.eco.util.ServerUtils
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
class PacketWindowItems(plugin: EcoPlugin) : AbstractPacketAdapter(plugin, PacketType.Play.Server.WINDOW_ITEMS, false) {
private val ignorePacketList = ConcurrentHashMap.newKeySet<String>()
private val playerRates = ConcurrentHashMap<String, Int>()
private val threadFactory = ThreadFactoryBuilder().setNameFormat("eco-display-thread-%d").build()
private val executor = Executors.newCachedThreadPool(threadFactory)
private val scheduledExecutor = Executors.newSingleThreadScheduledExecutor(threadFactory)
override fun onSend(
packet: PacketContainer,
player: Player,
event: PacketEvent
) {
val windowId = packet.integers.read(0)
packet.itemListModifier.modify(0) { itemStacks: List<ItemStack>? ->
if (itemStacks == null) {
return@modify null
if (ignorePacketList.contains(player.name)) {
ignorePacketList.remove(player.name)
return
}
if (windowId == 0) {
val frameMap = mutableMapOf<Byte, Int>()
val windowId = packet.integers.read(0)
if (windowId != 0) {
player.lastDisplayFrame = DisplayFrame.EMPTY
}
val itemStacks = packet.itemListModifier.read(0) ?: return
handleRateLimit(player)
if (usingAsync(player)) {
executor.execute {
try {
modifyWindowItems(itemStacks, windowId, player)
} catch (e: Exception) {
if (this.getPlugin().configYml.getBool("async-display.log-errors")) {
this.getPlugin().logger.warning("Error happened in async processing! Disable async display (/plugins/eco/config.yml) if this is a frequent issue")
}
}
val newPacket = packet.deepClone()
newPacket.itemListModifier.write(0, itemStacks)
ignorePacketList.add(player.name)
ProtocolLibrary.getProtocolManager().sendServerPacket(player, newPacket)
}
} else {
packet.itemListModifier.write(0, modifyWindowItems(itemStacks, windowId, player))
}
}
private fun handleRateLimit(player: Player) {
fun modifyRateValueBy(player: Player, amount: Int) {
val name = player.name
val current = playerRates[name] ?: 0
val new = current + amount
if (new <= 0) {
playerRates.remove(name)
} else {
playerRates[name] = new
}
}
modifyRateValueBy(player, 1)
scheduledExecutor.schedule(
{ modifyRateValueBy(player, -1) },
this.getPlugin().configYml.getInt("async-display.ratelimit.timeframe").toLong(),
TimeUnit.SECONDS
)
}
private fun usingAsync(player: Player): Boolean {
if (this.getPlugin().configYml.getBool("async-display.enabled")) {
return true
}
if (
this.getPlugin().configYml.getBool("async-display.emergency.enabled")
&& ServerUtils.getTps() <= this.getPlugin().configYml.getDouble("async-display.emergency.cutoff")
) {
return true
}
if (
this.getPlugin().configYml.getBool("async-display.ratelimit.enabled")
&& (playerRates[player.name] ?: 0) >= this.getPlugin().configYml.getInt("async-display.ratelimit.cutoff")
) {
return true
}
return false
}
private fun modifyWindowItems(
itemStacks: MutableList<ItemStack>,
windowId: Int,
player: Player
): MutableList<ItemStack> {
if (this.getPlugin().configYml.getBool("use-display-frame") && windowId == 0) {
val frameMap = mutableMapOf<Byte, HashedItem>()
for (index in itemStacks.indices) {
frameMap[index.toByte()] = FastItemStack.wrap(itemStacks[index]).hashCode()
frameMap[index.toByte()] =
HashedItem(FastItemStack.wrap(itemStacks[index]).hashCode(), itemStacks[index])
}
val newFrame = DisplayFrame(frameMap)
val changes = player.lastDisplayFrame.getChangedSlots(newFrame)
val lastFrame = player.lastDisplayFrame
player.lastDisplayFrame = newFrame
val changes = lastFrame.getChangedSlots(newFrame)
for (index in changes) {
Display.display(itemStacks[index.toInt()], player)
}
for (index in (itemStacks.indices subtract changes.toSet())) {
itemStacks[index.toInt()] = lastFrame.getItem(index.toByte()) ?: itemStacks[index.toInt()]
}
} else {
itemStacks.forEach {
Display.display(it, player)
}
}
itemStacks
}
itemStacks.forEach { Display.display(it, player) }
}
return itemStacks
}
}

View File

@@ -1,28 +1,44 @@
package com.willfp.eco.spigot.display.frame
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
import java.util.*
import java.util.concurrent.ConcurrentHashMap
data class DisplayFrame(val items: Map<Byte, Int>) {
data class HashedItem(val hash: Int, val item: ItemStack)
data class DisplayFrame(val items: Map<Byte, HashedItem>) {
fun getChangedSlots(newFrame: DisplayFrame): List<Byte> {
val changes = mutableListOf<Byte>()
for ((slot, hash) in newFrame.items) {
if (items[slot] != hash) {
for ((slot, data) in newFrame.items) {
if (items[slot]?.hash != data.hash) {
changes.add(slot)
}
}
return changes
}
fun getItem(slot: Byte): ItemStack? {
return items[slot]?.item
}
private val frames = mutableMapOf<UUID, DisplayFrame>()
companion object {
val EMPTY = DisplayFrame(emptyMap())
}
}
private val frames = ConcurrentHashMap<UUID, DisplayFrame>()
var Player.lastDisplayFrame: DisplayFrame
get() {
return frames[this.uniqueId] ?: DisplayFrame(emptyMap())
return frames[this.uniqueId] ?: DisplayFrame.EMPTY
}
set(value) {
frames[this.uniqueId] = value
}
fun clearFrames() {
frames.clear()
}

View File

@@ -0,0 +1,15 @@
package com.willfp.eco.spigot.integrations.afk
import com.Zrips.CMI.CMI
import com.willfp.eco.core.integrations.afk.AFKWrapper
import org.bukkit.entity.Player
class AFKIntegrationCMI : AFKWrapper {
override fun isAfk(player: Player): Boolean {
return CMI.getInstance().playerManager.getUser(player)?.isAfk ?: false
}
override fun getPluginName(): String {
return "CMI"
}
}

View File

@@ -0,0 +1,18 @@
package com.willfp.eco.spigot.integrations.afk
import com.earth2me.essentials.Essentials
import com.willfp.eco.core.integrations.afk.AFKWrapper
import org.bukkit.entity.Player
import org.bukkit.plugin.java.JavaPlugin
class AFKIntegrationEssentials : AFKWrapper {
private val ess = JavaPlugin.getPlugin(Essentials::class.java)
override fun isAfk(player: Player): Boolean {
return ess.getUser(player) != null && ess.getUser(player).isAfk
}
override fun getPluginName(): String {
return "Essentials"
}
}

View File

@@ -0,0 +1,33 @@
package com.willfp.eco.spigot.integrations.anticheat
import com.willfp.eco.core.integrations.anticheat.AnticheatWrapper
import me.nik.alice.api.events.AliceViolationEvent
import org.bukkit.entity.Player
import org.bukkit.event.EventHandler
import org.bukkit.event.EventPriority
import org.bukkit.event.Listener
import java.util.*
class AnticheatAlice : AnticheatWrapper, Listener {
private val exempt: MutableSet<UUID> = HashSet()
override fun getPluginName(): String {
return "Alice"
}
override fun exempt(player: Player) {
exempt.add(player.uniqueId)
}
override fun unexempt(player: Player) {
exempt.remove(player.uniqueId)
}
@EventHandler(priority = EventPriority.LOWEST)
private fun onViolate(event: AliceViolationEvent) {
if (!exempt.contains(event.player.uniqueId)) {
return
}
event.isCancelled = true
}
}

View File

@@ -0,0 +1,75 @@
package com.willfp.eco.spigot.integrations.antigrief
import com.willfp.eco.core.integrations.antigrief.AntigriefWrapper
import org.bukkit.Location
import org.bukkit.World
import org.bukkit.block.Block
import org.bukkit.entity.LivingEntity
import org.bukkit.entity.Monster
import org.bukkit.entity.Player
import world.bentobox.bentobox.BentoBox
import world.bentobox.bentobox.api.user.User
import world.bentobox.bentobox.lists.Flags
class AntigriefBentoBox : AntigriefWrapper {
override fun canBreakBlock(
player: Player,
block: Block
): Boolean {
val island = BentoBox.getInstance().islandsManager.getIslandAt(block.location).orElse(null) ?: return true
return island.isAllowed(User.getInstance(player), Flags.BREAK_BLOCKS)
}
override fun canCreateExplosion(
player: Player,
location: Location
): Boolean {
val island = BentoBox.getInstance().islandsManager.getIslandAt(location).orElse(null) ?: return true
return island.isAllowed(User.getInstance(player), Flags.TNT_DAMAGE)
}
override fun canPlaceBlock(
player: Player,
block: Block
): Boolean {
val island = BentoBox.getInstance().islandsManager.getIslandAt(block.location).orElse(null) ?: return true
return island.isAllowed(User.getInstance(player), Flags.PLACE_BLOCKS)
}
override fun canInjure(
player: Player,
victim: LivingEntity
): Boolean {
val island = BentoBox.getInstance().islandsManager.getIslandAt(victim.location).orElse(null) ?: return true
return when (victim) {
is Player -> {
island.isAllowed(
User.getInstance(player), when (victim.world.environment) {
World.Environment.NORMAL -> Flags.PVP_OVERWORLD
World.Environment.NETHER -> Flags.PVP_NETHER
World.Environment.THE_END -> Flags.PVP_END
else -> Flags.PVP_OVERWORLD
}
)
}
is Monster -> island.isAllowed(User.getInstance(player), Flags.HURT_MONSTERS)
else -> island.isAllowed(User.getInstance(player), Flags.HURT_ANIMALS)
}
}
override fun getPluginName(): String {
return "BentoBox"
}
override fun equals(other: Any?): Boolean {
if (other !is AntigriefWrapper) {
return false
}
return other.pluginName == this.pluginName
}
override fun hashCode(): Int {
return this.pluginName.hashCode()
}
}

View File

@@ -3,7 +3,6 @@ package com.willfp.eco.spigot.integrations.antigrief
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.integrations.antigrief.AntigriefWrapper
import me.angeschossen.lands.api.integration.LandsIntegration
import me.angeschossen.lands.api.role.enums.RoleSetting
import org.bukkit.Location
import org.bukkit.block.Block
import org.bukkit.entity.LivingEntity
@@ -15,41 +14,32 @@ class AntigriefLands(private val plugin: EcoPlugin) : AntigriefWrapper {
player: Player,
block: Block
): Boolean {
val area = landsIntegration.getAreaByLoc(block.location)
return area?.canSetting(player, RoleSetting.BLOCK_BREAK, false) ?: true
val area = landsIntegration.getAreaByLoc(block.location) ?: return true
return area.isTrusted(player.uniqueId)
}
override fun canCreateExplosion(
player: Player,
location: Location
): Boolean {
val area = landsIntegration.getAreaByLoc(location)
return area?.canSetting(player, RoleSetting.BLOCK_IGNITE, false) ?: true
val area = landsIntegration.getAreaByLoc(location) ?: return true
return area.isTrusted(player.uniqueId)
}
override fun canPlaceBlock(
player: Player,
block: Block
): Boolean {
val area = landsIntegration.getAreaByLoc(block.location)
return area?.canSetting(player, RoleSetting.BLOCK_PLACE, false) ?: true
val area = landsIntegration.getAreaByLoc(block.location) ?: return true
return area.isTrusted(player.uniqueId)
}
override fun canInjure(
player: Player,
victim: LivingEntity
): Boolean {
val area = landsIntegration.getAreaByLoc(victim.location)
if (victim is Player) {
if (area != null) {
return area.canSetting(player, RoleSetting.ATTACK_PLAYER, false)
}
} else {
if (area != null) {
return area.canSetting(player, RoleSetting.ATTACK_ANIMAL, false)
}
}
return true
val area = landsIntegration.getAreaByLoc(victim.location) ?: return true
return area.isTrusted(player.uniqueId)
}
override fun getPluginName(): String {

View File

@@ -0,0 +1,49 @@
package com.willfp.eco.spigot.integrations.antigrief
import com.bgsoftware.superiorskyblock.api.SuperiorSkyblockAPI
import com.bgsoftware.superiorskyblock.api.enums.HitActionResult
import com.bgsoftware.superiorskyblock.api.island.IslandPrivilege
import com.willfp.eco.core.integrations.antigrief.AntigriefWrapper
import org.bukkit.Location
import org.bukkit.block.Block
import org.bukkit.entity.Animals
import org.bukkit.entity.LivingEntity
import org.bukkit.entity.Monster
import org.bukkit.entity.Player
class AntigriefSuperiorSkyblock2 : AntigriefWrapper {
override fun getPluginName(): String {
return "SuperiorSkyblock2"
}
override fun canBreakBlock(player: Player, block: Block): Boolean {
if (SuperiorSkyblockAPI.getPlayer(player).hasBypassModeEnabled()) return true
return SuperiorSkyblockAPI.getPlayer(player).hasPermission(IslandPrivilege.getByName("Break"))
|| SuperiorSkyblockAPI.getPlayer(player).hasPermission(IslandPrivilege.getByName("BREAK"))
}
override fun canCreateExplosion(player: Player, location: Location): Boolean {
if (SuperiorSkyblockAPI.getPlayer(player).hasBypassModeEnabled()) return true
return SuperiorSkyblockAPI.getIslandAt(location)?.isMember(SuperiorSkyblockAPI.getPlayer(player)) ?: true
}
override fun canPlaceBlock(player: Player, block: Block): Boolean {
if (SuperiorSkyblockAPI.getPlayer(player).hasBypassModeEnabled()) return true
return SuperiorSkyblockAPI.getPlayer(player).hasPermission(IslandPrivilege.getByName("Place"))
|| SuperiorSkyblockAPI.getPlayer(player).hasPermission(IslandPrivilege.getByName("PLACE"))
}
override fun canInjure(player: Player, victim: LivingEntity): Boolean {
if (SuperiorSkyblockAPI.getPlayer(player).hasBypassModeEnabled()) return true
return when (victim) {
is Player -> SuperiorSkyblockAPI.getPlayer(player).canHit(SuperiorSkyblockAPI.getPlayer(victim)).equals(HitActionResult.SUCCESS)
is Animals -> {
return SuperiorSkyblockAPI.getPlayer(player).hasPermission(IslandPrivilege.getByName("ANIMAL_DAMAGE"))
}
is Monster -> {
return SuperiorSkyblockAPI.getPlayer(player).hasPermission(IslandPrivilege.getByName("MONSTER_DAMAGE"))
}
else -> true
}
}
}

View File

@@ -0,0 +1,29 @@
package com.willfp.eco.spigot.integrations.economy
import com.willfp.eco.core.integrations.economy.EconomyWrapper
import net.milkbowl.vault.economy.Economy
import org.bukkit.OfflinePlayer
class EconomyVault(
private val vault: Economy
): EconomyWrapper {
override fun hasAmount(player: OfflinePlayer, amount: Double): Boolean {
return vault.has(player, amount)
}
override fun giveMoney(player: OfflinePlayer, amount: Double): Boolean {
return vault.depositPlayer(player, amount).transactionSuccess()
}
override fun removeMoney(player: OfflinePlayer, amount: Double): Boolean {
return vault.withdrawPlayer(player, amount).transactionSuccess()
}
override fun getBalance(player: OfflinePlayer): Double {
return vault.getBalance(player)
}
override fun getPluginName(): String {
return "Vault"
}
}

View File

@@ -0,0 +1,40 @@
package com.willfp.eco.spigot.integrations.hologram
import com.Zrips.CMI.CMI
import com.Zrips.CMI.Modules.Holograms.CMIHologram
import com.willfp.eco.core.integrations.hologram.Hologram
import com.willfp.eco.core.integrations.hologram.HologramWrapper
import net.Zrips.CMILib.Container.CMILocation
import org.bukkit.Location
import java.util.UUID
@Suppress("DEPRECATION")
class HologramCMI : HologramWrapper {
override fun createHologram(location: Location, contents: MutableList<String>): Hologram {
val cmiHolo = CMIHologram(UUID.randomUUID().toString(), CMILocation(location))
CMI.getInstance().hologramManager.addHologram(cmiHolo)
val holo = HologramImplCMI(cmiHolo)
holo.setContents(contents)
cmiHolo.enable()
return holo
}
override fun getPluginName(): String {
return "CMI"
}
class HologramImplCMI(
private val handle: CMIHologram
) : Hologram {
override fun remove() {
CMI.getInstance().hologramManager.removeHolo(handle)
}
override fun setContents(contents: MutableList<String>) {
handle.lines = contents
}
}
}

View File

@@ -0,0 +1,38 @@
package com.willfp.eco.spigot.integrations.hologram
import com.willfp.eco.core.integrations.hologram.Hologram
import com.willfp.eco.core.integrations.hologram.HologramWrapper
import me.gholo.api.GHoloAPI
import org.bukkit.Location
import java.util.*
@Suppress("DEPRECATION")
class HologramGHolo : HologramWrapper {
companion object {
private val api = GHoloAPI()
}
override fun createHologram(location: Location, contents: MutableList<String>): Hologram {
val id = UUID.randomUUID().toString()
api.insertHolo(id, location, contents)
return HologramImplGHolo(id)
}
override fun getPluginName(): String {
return "GHolo"
}
class HologramImplGHolo(
private val id: String
) : Hologram {
override fun remove() {
api.removeHolo(id)
}
override fun setContents(contents: MutableList<String>) {
api.getHolo(id)?.content = contents
}
}
}

View File

@@ -0,0 +1,41 @@
package com.willfp.eco.spigot.integrations.hologram
import com.gmail.filoghost.holographicdisplays.api.HologramsAPI
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.integrations.hologram.Hologram
import com.willfp.eco.core.integrations.hologram.HologramWrapper
import org.bukkit.Location
class HologramHolographicDisplays(
private val plugin: EcoPlugin
) : HologramWrapper {
override fun createHologram(location: Location, contents: MutableList<String>): Hologram {
val hologram = HologramImplHolographicDisplays(
HologramsAPI.createHologram(plugin, location)
)
hologram.setContents(contents)
return hologram
}
override fun getPluginName(): String {
return "HolographicDisplays"
}
class HologramImplHolographicDisplays(
private val handle: com.gmail.filoghost.holographicdisplays.api.Hologram
) : Hologram {
override fun remove() {
handle.delete()
}
override fun setContents(contents: MutableList<String>) {
handle.clearLines()
for (line in contents) {
handle.appendTextLine(line)
}
}
}
}

View File

@@ -27,6 +27,10 @@ class McmmoIntegrationImpl : McmmoWrapper {
} else event is FakeEvent
}
override fun getPluginName(): String {
return "mcMMO"
}
init {
if (!ClassUtils.exists("com.gmail.nossr50.events.fake.FakeEvent")) {
disabled = true

View File

@@ -0,0 +1,21 @@
package com.willfp.eco.spigot.integrations.multiverseinventories
import com.onarandombox.multiverseinventories.event.WorldChangeShareHandlingEvent
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.events.ArmorChangeEvent
import org.bukkit.Bukkit
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
class MultiverseInventoriesIntegration(
private val plugin: EcoPlugin
): Listener {
@EventHandler
fun onWorldChange(event: WorldChangeShareHandlingEvent) {
val before = event.player.inventory.armorContents.toMutableList()
this.plugin.scheduler.run {
val after = event.player.inventory.armorContents.toMutableList()
Bukkit.getPluginManager().callEvent(ArmorChangeEvent(event.player, before, after))
}
}
}

View File

@@ -3,6 +3,19 @@
# by Auxilor
#
mysql:
enabled: false # Set to false, data.yml will be used instead.
host: localhost
port: 3306
database: database
user: username
password: passy
autosave:
ticks: 20000 # The amount of ticks between autosaves
log: false # If auto-save messages should be sent to console
async: false # If saves should be performed asynchronously. May cause bugs without MySQL
# Options to fix villager bugs left behind from old (buggy) versions.
villager-display-fix: false
@@ -17,6 +30,49 @@ use-fast-collated-drops: true
enable-bstats: true
# Some plugins use their own item display systems (eg Triton)
# And must be ran after eco. Don't enable this unless you run a conflicting plugin
# And must be run after eco. Don't enable this unless you run a conflicting plugin
# and have been told to enable it.
use-lower-protocollib-priority: false
# Display frames massively optimize PacketWindowItems, however some users have
# reported display bugs by using it. If you have any problems with it, then you
# should disable this option.
use-display-frame: true
# Time to live for a display frame. In other words, this is how frequent (in ticks)
# that display frames will be cleared / deleted.
display-frame-ttl: 17
# Window items packets have the option to be run asynchronously. This may cause
# some bugs and is considered experimental, however it has been tested without
# any apparent issues. Enable this if performance is absolutely crucial or if you
# are experiencing severe display lag.
async-display:
# If async display should always be used.
enabled: false
# Log errors that occur in async processing.
log-errors: true
# If the server is running under heavy load (below a certain TPS value), enable
# async display automatically. This can prevent some server crashes under load.
emergency:
# If emergency async should be used.
enabled: true
# Below this TPS value, emergency async display will be used.
cutoff: 18
# If players with a large amount of display packets should have their processing
# done asynchronously. This will help if a player is trying to crash the server
# by overloading the display system.
ratelimit:
# If rate limit async display should be used.
enabled: true
# The amount of window items packets per timeframe needed to enable async display
# for a specified player.
cutoff: 4
# The length of the timeframe in seconds.
# Cutoff 5, Timeframe 1 means that if there are more than 5 window items packets
# being sent per second for a player, then that player should have their packets
# handled asynchronously.
timeframe: 1

View File

@@ -0,0 +1 @@
# For internal storage use only, do not modify.

View File

@@ -25,11 +25,25 @@ softdepend:
- ItemsAdder
- Oraxen
- HeadDatabase
- Multiverse-Inventories
- Alice
- HolographicDisplays
- GHolo
- CMI
- Essentials
- Vault
- BentoBox
libraries:
- 'org.reflections:reflections:0.9.12'
- 'org.apache.maven:maven-artifact:3.0.3'
- 'org.jetbrains.kotlin:kotlin-stdlib:1.5.31'
- 'net.kyori:adventure-platform-bukkit:4.0.0'
- 'net.kyori:adventure-api:4.9.1'
- 'net.kyori:adventure-api:4.9.2'
- 'net.kyori:adventure-text-serializer-gson:4.9.2'
- 'net.kyori:adventure-text-serializer-legacy:4.8.1'
- 'net.kyori:adventure-text-serializer-legacy:4.9.2'
- 'org.jetbrains.kotlin:kotlin-stdlib:1.5.21'
- 'org.jetbrains.exposed:exposed-core:0.35.1'
- 'org.jetbrains.exposed:exposed-dao:0.35.1'
- 'org.jetbrains.exposed:exposed-jdbc:0.35.1'
- 'mysql:mysql-connector-java:8.0.25'
- 'com.google.guava:guava:31.0.1-jre'

View File

@@ -0,0 +1,7 @@
package com.willfp.eco.proxy
import com.willfp.eco.core.proxy.AbstractProxy
interface TPSProxy : AbstractProxy {
fun getTPS(): Double
}

View File

@@ -1,2 +1,2 @@
version = 6.10.1
version = 6.13.0
plugin-name = eco

BIN
lib/CMIAPI8.7.8.2.jar Normal file

Binary file not shown.

BIN
lib/CMILib1.0.4.1.jar Normal file

Binary file not shown.

BIN
lib/GHolo.jar Normal file

Binary file not shown.

Binary file not shown.

BIN
lib/alice-api-1.2.3.jar Normal file

Binary file not shown.