Compare commits

..

49 Commits

Author SHA1 Message Date
Auxilor
18e7548c23 Updated to 6.13.4 2021-11-08 14:05:32 +00:00
Auxilor
40020b0503 Added Extension#onAfterLoad and Extension#onReload 2021-11-08 14:04:24 +00:00
Auxilor
830f92bbdb Dropped Velocity and worked on bungee 2021-11-08 13:15:42 +00:00
Auxilor
c6e39e89fd Updated to 6.13.3 2021-11-08 10:58:26 +00:00
Auxilor
7d38d7db15 Saved player name changes 2021-11-08 10:58:18 +00:00
Auxilor
a6f139c47e Fixed plugin.yml 2021-11-05 14:40:56 +00:00
Auxilor
1bae159f94 Added velocity data listener 2021-11-05 14:30:43 +00:00
Auxilor
43d2835087 Prerequisite changes 2021-11-05 14:18:35 +00:00
Auxilor
cb2d4d6a34 Added ServerDisconnectEvent listener 2021-11-05 14:16:41 +00:00
Auxilor
9bb73d8e90 Merge remote-tracking branch 'origin/develop' into develop
# Conflicts:
#	eco-core/core-plugin/src/main/kotlin/com/willfp/eco/spigot/data/storage/MySQLDataHandler.kt
2021-11-05 14:06:22 +00:00
Auxilor
86c4867206 Added BungeeDataListener and IridiumSkyblock support 2021-11-04 20:28:47 +00:00
Auxilor
43e13248d6 SQL changes (Please be done) and Antigrief changes 2021-11-04 20:15:26 +00:00
Auxilor
f6ff45228a Merge branch 'master' into develop 2021-11-04 20:02:29 +00:00
Will FP
af1c20f5c5 Merge pull request #49
Added DeluxeCombat integration
2021-11-04 20:01:59 +00:00
Auxilor
9ac564c345 Finally got exposed to shut up 2021-11-04 20:00:13 +00:00
Auxilor
857d51a7a1 Added PersistentDataKey#values 2021-11-04 19:16:38 +00:00
Auxilor
ab5f8cee5a Cleaned up DataHandler 2021-11-04 19:04:59 +00:00
Auxilor
bdd518b280 More Persistent Data changes 2021-11-04 19:00:47 +00:00
_OfTeN_
494c1a87b2 Added DeluxeCombat integration 2021-11-04 19:30:16 +03:00
Auxilor
f78f92b5f6 Fixed writing 2021-11-04 15:26:31 +00:00
Auxilor
048b56c58c Disabled Exposed logging 2021-11-04 14:06:09 +00:00
Auxilor
75dec2bf49 Re-added writeSafely to optionally specify predefined player ResultRow 2021-11-04 13:36:35 +00:00
Auxilor
7119da13b7 Bug Fixes 2021-11-04 13:32:23 +00:00
Auxilor
6a8637922b More SQL changes 2021-11-04 13:15:21 +00:00
Auxilor
abce7b898c Changed shutdown order 2021-11-04 13:02:23 +00:00
Auxilor
562d7f382c Updated to 6.13.2 2021-11-04 12:59:52 +00:00
Auxilor
c72b15c3a6 Several performance and stability improvements 2021-11-04 12:57:39 +00:00
Auxilor
54b7e99c82 Updated to 6.13.1 2021-11-03 11:32:41 +00:00
Auxilor
4b6a0ee05d Added option to blacklist inventory types from async display, fixing anvils 2021-11-03 11:32:31 +00:00
Auxilor
89e5d8898b Merge branch 'master' into develop
# Conflicts:
#	eco-core/core-plugin/src/main/resources/config.yml
2021-11-03 11:00:25 +00:00
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
86f230c836 Added in config.yml warning 2021-11-02 17:46:47 +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
33 changed files with 770 additions and 139 deletions

View File

@@ -31,10 +31,10 @@ allprojects {
// 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
// Spigot API, Bungee API
maven { url 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' }
// PlaceholderAPI
@@ -54,6 +54,9 @@ allprojects {
// CombatLogX
maven { url 'https://nexus.sirblobman.xyz/repository/public/' }
// IridiumSkyblock
maven { url 'https://nexus.iridiumdevelopment.net/repository/maven-releases/' }
}
dependencies {

View File

@@ -7,6 +7,7 @@ import com.willfp.eco.core.config.updating.ConfigHandler;
import com.willfp.eco.core.display.Display;
import com.willfp.eco.core.display.DisplayModule;
import com.willfp.eco.core.events.EventManager;
import com.willfp.eco.core.extensions.Extension;
import com.willfp.eco.core.extensions.ExtensionLoader;
import com.willfp.eco.core.factory.MetadataValueFactory;
import com.willfp.eco.core.factory.NamespacedKeyFactory;
@@ -469,6 +470,10 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
this.reload();
for (Extension extension : this.getExtensionLoader().getLoadedExtensions()) {
extension.handleAfterLoad();
}
this.getLogger().info("Loaded " + this.color + this.getName());
}
@@ -483,6 +488,10 @@ public abstract class EcoPlugin extends JavaPlugin implements PluginLike {
this.getScheduler().cancelAll();
this.handleReload();
for (Extension extension : this.extensionLoader.getLoadedExtensions()) {
extension.handleReload();
}
}
/**

View File

@@ -1,5 +1,6 @@
package com.willfp.eco.core;
import com.willfp.eco.core.integrations.economy.EconomyManager;
import com.willfp.eco.core.proxy.ProxyConstants;
import com.willfp.eco.util.ClassUtils;
import lombok.Getter;
@@ -31,8 +32,11 @@ public class Prerequisite {
);
/**
* Requires the server to be running an implementation of paper.
* Requires the server to have vault installed.
*
* @deprecated Use {@link EconomyManager#hasRegistrations()} instead.
*/
@Deprecated
public static final Prerequisite HAS_VAULT = new Prerequisite(
() -> ClassUtils.exists("net.milkbowl.vault.economy.Economy"),
"Requires server to have vault"
@@ -46,6 +50,22 @@ public class Prerequisite {
"Requires server to be running 1.17+"
);
/**
* Requires the server to be running an implementation of BungeeCord.
*/
public static final Prerequisite HAS_BUNGEECORD = new Prerequisite(
() -> ClassUtils.exists("net.md_5.bungee.api.event.ServerConnectedEvent"),
"Requires server to be running BungeeCord (or a fork)"
);
/**
* Requires the server to be running an implementation of Velocity.
*/
public static final Prerequisite HAS_VELOCITY = new Prerequisite(
() -> ClassUtils.exists("com.velocitypowered.api.event.player.ServerConnectedEvent"),
"Requires server to be running Velocity (or a fork)"
);
/**
* If the necessary prerequisite condition has been met.
*/

View File

@@ -27,6 +27,15 @@ public interface PlayerProfileHandler {
* Save all player data.
*
* @param async If the saving should be done asynchronously.
* @deprecated async is now handled automatically depending on implementation.
*/
void saveAll(boolean async);
@Deprecated
default void saveAll(boolean async) {
saveAll();
}
/**
* Save all player data.
*/
void saveAll();
}

View File

@@ -5,6 +5,8 @@ import lombok.Getter;
import org.bukkit.NamespacedKey;
import org.jetbrains.annotations.NotNull;
import java.util.Set;
/**
* A persistent data key is a key with a type that can be stored about an offline player.
*
@@ -54,4 +56,13 @@ public class PersistentDataKey<T> {
+ ", type=" + type
+ '}';
}
/**
* Get all persistent data keys.
*
* @return The keys.
*/
public static Set<PersistentDataKey<?>> values() {
return Eco.getHandler().getKeyRegistry().getRegisteredKeys();
}
}

View File

@@ -1,6 +1,5 @@
package com.willfp.eco.core.display;
import lombok.Setter;
import lombok.experimental.UtilityClass;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
@@ -21,7 +20,6 @@ public class Display {
/**
* The display handler.
*/
@Setter
private static DisplayHandler handler = null;
/**
@@ -162,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

@@ -57,6 +57,20 @@ public abstract class Extension implements PluginLike {
this.onDisable();
}
/**
* Method to handle after load.
*/
public final void handleAfterLoad() {
this.onAfterLoad();
}
/**
* Method to handle plugin reloads.
*/
public final void handleReload() {
this.onReload();
}
/**
* Called on enabling Extension.
*/
@@ -67,6 +81,16 @@ public abstract class Extension implements PluginLike {
*/
protected abstract void onDisable();
/**
* Called the once the base plugin is done loading.
*/
protected abstract void onAfterLoad();
/**
* Called on plugin reload.
*/
protected abstract void onReload();
/**
* Set the metadata of the extension.
* <p>

View File

@@ -15,7 +15,7 @@ public interface AntigriefWrapper extends Integration {
*
* @param player The player.
* @param block The block.
* @return If player cna break block.
* @return If player can break block.
*/
boolean canBreakBlock(Player player, Block block);

View File

@@ -73,6 +73,11 @@ public abstract class AbstractItemStackBuilder<T extends ItemMeta, U extends Abs
@Override
public U addEnchantment(@NotNull final Enchantment enchantment,
final int level) {
//noinspection ConstantConditions
if (enchantment == null) {
return (U) this;
}
meta.addEnchant(enchantment, level, true);
return (U) this;
}

View File

@@ -90,7 +90,17 @@ public class PlayerUtils {
profile.write(PLAYER_NAME_KEY, onlinePlayer.getDisplayName());
}
return profile.read(PLAYER_NAME_KEY);
String saved = profile.read(PLAYER_NAME_KEY);
if (saved.equals(PLAYER_NAME_KEY.getDefaultValue())) {
String name = player.getName();
if (name != null) {
profile.write(PLAYER_NAME_KEY, player.getName());
return player.getName();
}
}
return saved;
}
/**

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

@@ -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,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

@@ -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

@@ -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

@@ -38,6 +38,10 @@ dependencies {
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'
compileOnly 'com.iridium:IridiumSkyblock:3.1.2'
compileOnly 'net.md-5:bungeecord-api:1.16-R0.5-SNAPSHOT'
// CombatLogX V10 + NewbieHelper Expansion
compileOnly 'com.SirBlobman.combatlogx:CombatLogX-API:10.0.0.0-SNAPSHOT'

View File

@@ -22,6 +22,7 @@ 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.data.DataListener
import com.willfp.eco.spigot.data.EcoPlayerProfileHandler
@@ -29,12 +30,7 @@ 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.*
import com.willfp.eco.spigot.display.frame.clearFrames
import com.willfp.eco.spigot.drops.CollatedRunnable
import com.willfp.eco.spigot.eventlisteners.EntityDeathByEntityListeners
@@ -45,21 +41,8 @@ import com.willfp.eco.spigot.eventlisteners.armor.ArmorListener
import com.willfp.eco.spigot.gui.GUIListener
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.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.anticheat.*
import com.willfp.eco.spigot.integrations.antigrief.*
import com.willfp.eco.spigot.integrations.customitems.CustomItemsHeadDatabase
import com.willfp.eco.spigot.integrations.customitems.CustomItemsItemsAdder
import com.willfp.eco.spigot.integrations.customitems.CustomItemsOraxen
@@ -72,16 +55,14 @@ import com.willfp.eco.spigot.integrations.multiverseinventories.MultiverseInvent
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,
@@ -97,12 +78,15 @@ abstract class EcoSpigotPlugin : EcoPlugin(
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()
}
@@ -137,10 +121,23 @@ abstract class EcoSpigotPlugin : EcoPlugin(
logger.severe("")
}
// Init FIS
this.getProxy(FastItemStackFactoryProxy::class.java).create(ItemStack(Material.AIR)).unwrap()
/*
I'll figure this one out eventually...
if (Prerequisite.HAS_BUNGEECORD.isMet) {
BungeeDataListener.register()
}
*/
}
override fun handleDisable() {
this.logger.info("Saving player data...")
val start = System.currentTimeMillis()
(Eco.getHandler().playerProfileHandler as EcoPlayerProfileHandler).saveAllBlocking()
this.logger.info("Saved player data! Took ${System.currentTimeMillis() - start}ms")
Eco.getHandler().adventure?.close()
}
@@ -155,7 +152,7 @@ abstract class EcoSpigotPlugin : EcoPlugin(
this.scheduler.runTimer(
{
(Eco.getHandler().playerProfileHandler as EcoPlayerProfileHandler)
.autosave(this.configYml.getBool("autosave.async"))
.autosave()
},
this.configYml.getInt("autosave.ticks").toLong(),
this.configYml.getInt("autosave.ticks").toLong()
@@ -170,7 +167,10 @@ abstract class EcoSpigotPlugin : EcoPlugin(
override fun loadIntegrationLoaders(): List<IntegrationLoader> {
return listOf(
// AntiGrief
IntegrationLoader("IridiumSkyblock") { AntigriefManager.register(AntigriefIridiumSkyblock()) },
IntegrationLoader("DeluxeCombat") { AntigriefManager.register(AntigriefDeluxeCombat()) },
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()) },

View File

@@ -0,0 +1,34 @@
package com.willfp.eco.spigot.data
import com.willfp.eco.core.Eco
import net.md_5.bungee.api.ProxyServer
import net.md_5.bungee.api.event.ServerConnectedEvent
import net.md_5.bungee.api.event.ServerDisconnectEvent
import net.md_5.bungee.api.event.ServerSwitchEvent
import net.md_5.bungee.api.plugin.Listener
import net.md_5.bungee.event.EventHandler
class BungeeDataListener : Listener {
@EventHandler
fun onConnected(event: ServerConnectedEvent) {
(Eco.getHandler().playerProfileHandler as EcoPlayerProfileHandler).unloadPlayer(event.player.uniqueId)
}
@EventHandler
fun onDisconnect(event: ServerDisconnectEvent) {
(Eco.getHandler().playerProfileHandler as EcoPlayerProfileHandler).unloadPlayer(event.player.uniqueId)
}
@EventHandler
fun onSwitch(event: ServerSwitchEvent) {
(Eco.getHandler().playerProfileHandler as EcoPlayerProfileHandler).unloadPlayer(event.player.uniqueId)
}
companion object {
fun register() {
ProxyServer.getInstance().pluginManager.registerListener(
null, BungeeDataListener()
)
}
}
}

View File

@@ -11,11 +11,12 @@ class DataListener : Listener {
@EventHandler
fun onLeave(event: PlayerQuitEvent) {
PlayerUtils.updateSavedDisplayName(event.player)
Eco.getHandler().playerProfileHandler.savePlayer(event.player.uniqueId)
(Eco.getHandler().playerProfileHandler as EcoPlayerProfileHandler).unloadPlayer(event.player.uniqueId)
}
@EventHandler
fun onJoin(event: PlayerJoinEvent) {
(Eco.getHandler().playerProfileHandler as EcoPlayerProfileHandler).unloadPlayerBlocking(event.player.uniqueId)
PlayerUtils.updateSavedDisplayName(event.player)
}
}
}

View File

@@ -32,40 +32,29 @@ class EcoPlayerProfileHandler(
return profile
}
fun unloadPlayer(uuid: UUID) {
handler.savePlayer(uuid)
loaded.remove(uuid)
}
fun unloadPlayerBlocking(uuid: UUID) {
handler.saveAllBlocking(listOf(uuid))
loaded.remove(uuid)
}
override fun savePlayer(uuid: UUID) {
writeToHandler(uuid)
saveToHandler()
handler.savePlayer(uuid)
}
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)
}
override fun saveAll() {
handler.saveAll(loaded.keys.toList())
}
private fun saveToHandler() {
handler.save()
fun saveAllBlocking() {
handler.saveAllBlocking(loaded.keys.toList())
}
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) {
fun autosave() {
if (Bukkit.getOnlinePlayers().isEmpty()) {
return
}
@@ -74,7 +63,7 @@ class EcoPlayerProfileHandler(
plugin.logger.info("Auto-Saving player data!")
}
saveAll(async)
saveAll()
if (plugin.configYml.getBool("autosave.log")) {
plugin.logger.info("Saved player data!")

View File

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

View File

@@ -1,9 +1,12 @@
package com.willfp.eco.spigot.data.storage
import com.google.common.util.concurrent.ThreadFactoryBuilder
import com.willfp.eco.core.Eco
import com.willfp.eco.core.data.PlayerProfile
import com.willfp.eco.core.data.keys.PersistentDataKey
import com.willfp.eco.core.data.keys.PersistentDataKeyType
import com.willfp.eco.spigot.EcoSpigotPlugin
import org.apache.logging.log4j.Level
import org.bukkit.NamespacedKey
import org.jetbrains.exposed.dao.id.UUIDTable
import org.jetbrains.exposed.sql.BooleanColumnType
@@ -13,18 +16,25 @@ 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.Table
import org.jetbrains.exposed.sql.VarCharColumnType
import org.jetbrains.exposed.sql.checkMappingConsistence
import org.jetbrains.exposed.sql.exposedLogger
import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.TransactionManager
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update
import java.util.UUID
import java.util.concurrent.Executors
@Suppress("UNCHECKED_CAST")
class MySQLDataHandler(
plugin: EcoSpigotPlugin
) : DataHandler {
private val columns = mutableMapOf<String, Column<*>>()
private val threadFactory = ThreadFactoryBuilder().setNameFormat("eco-mysql-thread-%d").build()
private val executor = Executors.newCachedThreadPool(threadFactory)
init {
Database.connect(
@@ -40,6 +50,15 @@ class MySQLDataHandler(
transaction {
SchemaUtils.create(Players)
}
// Get Exposed to shut the hell up
exposedLogger::class.java.getDeclaredField("logger").apply { isAccessible = true }
.apply {
get(exposedLogger).apply {
this.javaClass.getDeclaredMethod("setLevel", Level::class.java)
.invoke(this, Level.OFF)
}
}
}
override fun updateKeys() {
@@ -52,17 +71,53 @@ class MySQLDataHandler(
}
}
override fun save() {
// Do nothing
override fun <T> write(uuid: UUID, key: NamespacedKey, value: T) {
getPlayer(uuid)
writeAsserted(uuid, key, value)
}
override fun <T> write(uuid: UUID, key: NamespacedKey, value: T) {
private fun <T> writeAsserted(uuid: UUID, key: NamespacedKey, value: T, async: Boolean = true) {
val column: Column<T> = getColumn(key.toString()) as Column<T>
fun executeTransaction() {
transaction {
Players.update({ Players.id eq uuid }) {
it[column] = value
}
}
}
if (async) {
executor.execute { executeTransaction() }
} else {
executeTransaction()
}
}
override fun savePlayer(uuid: UUID) {
savePlayer(uuid, async = false)
}
override fun saveAll(uuids: Iterable<UUID>) {
for (uuid in uuids) {
savePlayer(uuid)
}
}
override fun saveAllBlocking(uuids: Iterable<UUID>) {
for (uuid in uuids) {
savePlayer(uuid, async = false)
}
}
private fun savePlayer(uuid: UUID, async: Boolean = true) {
val profile = PlayerProfile.load(uuid)
transaction {
getPlayer(uuid)
val column: Column<T> = getColumn(key.toString()) as Column<T>
Players.update({ Players.id eq uuid }) {
it[column] = value
for (key in Eco.getHandler().keyRegistry.registeredKeys) {
writeAsserted(uuid, key.key, profile.read(key), async = async)
}
}
}
@@ -76,12 +131,14 @@ class MySQLDataHandler(
return value
}
object Players : UUIDTable("eco_players") {
}
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)
@@ -89,7 +146,7 @@ class MySQLDataHandler(
.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(128))
PersistentDataKeyType.STRING -> registerColumn<String>(key.key.toString(), VarCharColumnType(512))
.default(key.defaultValue as String)
else -> throw NullPointerException("Null value found!")
@@ -108,12 +165,40 @@ class MySQLDataHandler(
}
private fun getPlayer(uuid: UUID): ResultRow {
Players.select { Players.id eq uuid }.firstOrNull() ?: run {
Players.insert {
it[id] = uuid
}
val player = transaction {
Players.select { Players.id eq uuid }.limit(1).singleOrNull()
}
return Players.select { Players.id eq uuid }.first()
return if (player != null) {
player
} else {
transaction {
Players.insert { it[id] = uuid }
}
getPlayer(uuid)
}
}
private fun createMissingTablesAndColumnsSilently(table: Table) {
with(TransactionManager.current()) {
fun execStatements(statements: List<String>) {
for (statement in statements) {
exec(statement)
}
}
db.dialect.resetCaches()
val createStatements = SchemaUtils.createStatements(table)
execStatements(createStatements)
commit()
val alterStatements = SchemaUtils.addMissingColumnsStatements(table)
execStatements(alterStatements)
commit()
val executedStatements = createStatements + alterStatements
val modifyTablesStatements = checkMappingConsistence(table).filter { it !in executedStatements }
execStatements(modifyTablesStatements)
commit()
db.dialect.resetCaches()
}
}
}

View File

@@ -1,9 +1,11 @@
package com.willfp.eco.spigot.data.storage
import com.willfp.eco.core.Eco
import com.willfp.eco.core.config.yaml.YamlBaseConfig
import com.willfp.eco.core.data.PlayerProfile
import com.willfp.eco.spigot.EcoSpigotPlugin
import org.bukkit.NamespacedKey
import java.util.*
import java.util.UUID
@Suppress("UNCHECKED_CAST")
class YamlDataHandler(
@@ -15,8 +17,20 @@ class YamlDataHandler(
dataYml.save()
}
override fun updateKeys() {
// Do nothing
override fun saveAll(uuids: Iterable<UUID>) {
for (uuid in uuids) {
savePlayer(uuid)
}
save()
}
override fun savePlayer(uuid: UUID) {
val profile = PlayerProfile.load(uuid)
for (key in Eco.getHandler().keyRegistry.registeredKeys) {
write(uuid, key.key, profile.read(key))
}
}
override fun <T> write(uuid: UUID, key: NamespacedKey, value: T) {

View File

@@ -1,8 +1,10 @@
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
@@ -10,53 +12,151 @@ 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
) {
if (ignorePacketList.contains(player.name)) {
ignorePacketList.remove(player.name)
return
}
val windowId = packet.integers.read(0)
if (windowId != 0) {
player.lastDisplayFrame = DisplayFrame.EMPTY
}
packet.itemListModifier.modify(0) { itemStacks: MutableList<ItemStack>? ->
if (itemStacks == null) {
return@modify null
val itemStacks = packet.itemListModifier.read(0) ?: return
handleRateLimit(player)
if (usingAsync(player)) {
fun modifyAndSend(itemStacks: MutableList<ItemStack>, windowId: Int, player: Player) {
modifyWindowItems(itemStacks, windowId, player)
val newPacket = packet.deepClone()
newPacket.itemListModifier.write(0, itemStacks)
ignorePacketList.add(player.name)
ProtocolLibrary.getProtocolManager().sendServerPacket(player, newPacket)
}
if (this.getPlugin().configYml.getBool("use-display-frame") && windowId == 0) {
val frameMap = mutableMapOf<Byte, HashedItem>()
executor.execute {
try {
modifyAndSend(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")
}
for (index in itemStacks.indices) {
frameMap[index.toByte()] =
HashedItem(FastItemStack.wrap(itemStacks[index]).hashCode(), itemStacks[index])
this.getPlugin().scheduler.run {
modifyAndSend(itemStacks, windowId, player)
}
}
val newFrame = DisplayFrame(frameMap)
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)) {
itemStacks[index.toInt()] = lastFrame.getItem(index.toByte()) ?: itemStacks[index.toInt()]
}
} else {
itemStacks.forEach { Display.display(it, player) }
}
itemStacks
} 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.getStrings("async-display.disable-on-types", false)
.map { it.lowercase() }.contains(player.openInventory.type.name.lowercase())
) {
return false
}
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()] =
HashedItem(FastItemStack.wrap(itemStacks[index]).hashCode(), itemStacks[index])
}
val newFrame = DisplayFrame(frameMap)
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) }
}
return itemStacks
}
}

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

@@ -0,0 +1,34 @@
package com.willfp.eco.spigot.integrations.antigrief
import com.willfp.eco.core.integrations.antigrief.AntigriefWrapper
import nl.marido.deluxecombat.api.DeluxeCombatAPI
import org.bukkit.Location
import org.bukkit.block.Block
import org.bukkit.entity.LivingEntity
import org.bukkit.entity.Player
class AntigriefDeluxeCombat: AntigriefWrapper {
override fun getPluginName(): String {
return "DeluxeCombat";
}
override fun canBreakBlock(player: Player, block: Block): Boolean {
return true;
}
override fun canCreateExplosion(player: Player, location: Location): Boolean {
return true;
}
override fun canPlaceBlock(player: Player, block: Block): Boolean {
return true;
}
override fun canInjure(player: Player, victim: LivingEntity): Boolean {
val api = DeluxeCombatAPI()
return when(victim) {
is Player -> (api.hasProtection(victim) || !api.hasPvPEnabled(victim)) && !api.isInCombat(victim)
else -> true
}
}
}

View File

@@ -0,0 +1,60 @@
package com.willfp.eco.spigot.integrations.antigrief
import com.iridium.iridiumskyblock.PermissionType
import com.iridium.iridiumskyblock.api.IridiumSkyblockAPI
import com.willfp.eco.core.integrations.antigrief.AntigriefWrapper
import org.bukkit.Location
import org.bukkit.block.Block
import org.bukkit.entity.LivingEntity
import org.bukkit.entity.Player
class AntigriefIridiumSkyblock : AntigriefWrapper {
private val api = IridiumSkyblockAPI.getInstance()
override fun canBreakBlock(
player: Player,
block: Block
): Boolean {
return api.getIslandPermission(api.getIslandViaLocation(block.location).orElse(null) ?: return true, api.getUser(player), PermissionType.BLOCK_BREAK)
}
override fun canCreateExplosion(
player: Player,
location: Location
): Boolean {
return api.getIslandPermission(api.getIslandViaLocation(location).orElse(null) ?: return true, api.getUser(player), PermissionType.BLOCK_BREAK)
}
override fun canPlaceBlock(
player: Player,
block: Block
): Boolean {
return api.getIslandPermission(api.getIslandViaLocation(block.location).orElse(null) ?: return true, api.getUser(player), PermissionType.BLOCK_PLACE)
}
override fun canInjure(
player: Player,
victim: LivingEntity
): Boolean {
return when (victim) {
is Player -> api.getIslandViaLocation(victim.location).orElse(null) != null
else -> api.getIslandPermission(api.getIslandViaLocation(victim.location).orElse(null) ?: return true, api.getUser(player), PermissionType.KILL_MOBS)
}
}
override fun getPluginName(): String {
return "IridiumSkyblock"
}
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

@@ -6,18 +6,19 @@ 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.*
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))
cmiHolo.enable()
CMI.getInstance().hologramManager.addHologram(cmiHolo)
val holo = HologramImplCMI(cmiHolo)
holo.setContents(contents)
cmiHolo.enable()
return holo
}

View File

@@ -3,6 +3,18 @@
# 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
# Options to fix villager bugs left behind from old (buggy) versions.
villager-display-fix: false
@@ -17,7 +29,7 @@ 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
@@ -30,15 +42,42 @@ use-display-frame: true
# that display frames will be cleared / deleted.
display-frame-ttl: 17
mysql:
enabled: false # Set to false, data.yml will be used instead.
host: localhost
port: 3306
database: database
user: username
password: passy
# 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
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
# Log errors that occur in async processing.
log-errors: true
# The inventory types that should never be processed asynchronously.
# A list of IDs can be found here:
# https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/event/inventory/InventoryType.html
disable-on-types:
- 'anvil'
# 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

@@ -32,6 +32,10 @@ softdepend:
- CMI
- Essentials
- Vault
- BentoBox
- DeluxeCombat
- IridiumSkyblock
- SuperiorSkyblock2
libraries:
- 'org.reflections:reflections:0.9.12'
- 'org.apache.maven:maven-artifact:3.0.3'
@@ -44,4 +48,5 @@ libraries:
- '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'
- '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.12.2
version = 6.13.4
plugin-name = eco

BIN
lib/DeluxeCombat API.jar Normal file

Binary file not shown.