Compare commits

..

18 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
17 changed files with 361 additions and 65 deletions

View File

@@ -31,8 +31,8 @@ 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
maven { url 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' }

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

@@ -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,8 @@ 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'
// 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
@@ -51,6 +52,7 @@ 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
@@ -72,16 +74,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 +97,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()
}
@@ -171,6 +174,7 @@ abstract class EcoSpigotPlugin : EcoPlugin(
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()) },

View File

@@ -82,6 +82,10 @@ class MySQLDataHandler(
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 +93,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!")

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,137 @@ 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)) {
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)
}
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)) {
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.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

@@ -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,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,7 +30,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 +43,36 @@ 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
# 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,7 @@ softdepend:
- CMI
- Essentials
- Vault
- BentoBox
libraries:
- 'org.reflections:reflections:0.9.12'
- 'org.apache.maven:maven-artifact:3.0.3'
@@ -44,4 +45,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.0
plugin-name = eco