diff --git a/gradle.properties b/gradle.properties index 44d4335..d063f4d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,7 +9,7 @@ loader_version=0.15.9 fabric_kotlin_version=1.10.19+kotlin.1.9.23 access_widener=puffy-anti-exploit.accesswidener # Mod Properties -mod_version=0.2.6 +mod_version=0.2.7 maven_group=com.puffy archives_base_name=puffy-anti-exploit # Dependencies diff --git a/src/main/kotlin/com/puffy/mitigations/AntiFlyMitigation.kt b/src/main/kotlin/com/puffy/mitigations/AntiFlyMitigation.kt index 30165b2..9e2cd03 100644 --- a/src/main/kotlin/com/puffy/mitigations/AntiFlyMitigation.kt +++ b/src/main/kotlin/com/puffy/mitigations/AntiFlyMitigation.kt @@ -3,9 +3,10 @@ package com.puffy.mitigations import com.puffy.events.DetectionEventEmitter import com.puffy.events.DetectionInfo import com.puffy.events.PacketEventEmitter +import com.puffy.util.isAllowedFlight +import com.puffy.util.isInAir import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents.EndTick -import net.minecraft.entity.effect.StatusEffect import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket import net.minecraft.server.network.ServerPlayerEntity @@ -13,17 +14,13 @@ const val MAXIMUM_TICKS_SPENT_FLOATING = 85 const val MAXIMUM_TICKS_SPENT_ASCENDING = 20 private const val FLOATING_Y_TOLERANCE = 1 private const val MITIGATION_NAME = "Flight" -// https://minecraft.fandom.com/wiki/Levitation -private const val LEVITATION_STATUS_ID = 25 -// Likely, once the player's velocity reaches this threshold it's probably from a tnt jump or mod -private const val FLIGHT_SPEED_THRESHOLD = 5 class AntiFlyMitigation(val player: ServerPlayerEntity) { private var ticksSpentFloating = 0 private var ticksSpentAscending = 0 private var lastY = 0.0 - private var isAllowedToFly = false + private var lastTickFlightPermission = false init { PacketEventEmitter.addListener(player, PlayerMoveC2SPacket::class) { _ -> @@ -37,29 +34,18 @@ class AntiFlyMitigation(val player: ServerPlayerEntity) { updateTicksFloating() updateTicksAscending() resetCountersWhenFlightRemoved() - isAllowedToFly = isFlightAllowedAtCurrentTick() + lastTickFlightPermission = player.isAllowedFlight() } ) } private fun isFlyHacking(): Boolean { return (ticksSpentFloating >= MAXIMUM_TICKS_SPENT_FLOATING || - ticksSpentAscending >= MAXIMUM_TICKS_SPENT_ASCENDING) && !isAllowedToFly + ticksSpentAscending >= MAXIMUM_TICKS_SPENT_ASCENDING) && !player.isAllowedFlight() } private fun updateTicksFloating() { - val world = player.entityWorld - val boundingBox = player.boundingBox.expand(0.0, 0.2, 0.0) - - val collidingBlocks = world.getBlockCollisions(player, boundingBox) - val collidingEntities = - world.getOtherEntities(player, boundingBox) { entity -> !entity.equals(player) } - - if ( - !collidingBlocks.any() && - !collidingEntities.any() && - player.y - lastY <= FLOATING_Y_TOLERANCE - ) { + if (player.isInAir() && player.y - lastY <= FLOATING_Y_TOLERANCE) { ticksSpentFloating++ } else { ticksSpentFloating = 0 @@ -67,7 +53,7 @@ class AntiFlyMitigation(val player: ServerPlayerEntity) { } private fun updateTicksAscending() { - if (player.y > lastY && !isFlightAllowedAtCurrentTick()) { + if (player.y > lastY && !player.isAllowedFlight()) { ticksSpentAscending++ } else { ticksSpentAscending = 0 @@ -76,19 +62,8 @@ class AntiFlyMitigation(val player: ServerPlayerEntity) { lastY = player.y } - private fun isFlightAllowedAtCurrentTick(): Boolean { - return (player.abilities.flying || player.abilities.allowFlying) || - player.isTouchingWater || - player.isSubmergedInWater || - player.isSwimming || - player.isFallFlying || - player.hasStatusEffect(StatusEffect.byRawId(LEVITATION_STATUS_ID)) || - player.velocity.length() >= FLIGHT_SPEED_THRESHOLD || - player.fallDistance > 0 - } - private fun resetCountersWhenFlightRemoved() { - if (isAllowedToFly != isFlightAllowedAtCurrentTick()) { + if (lastTickFlightPermission != player.isAllowedFlight()) { ticksSpentFloating = 0 ticksSpentAscending = 0 } diff --git a/src/main/kotlin/com/puffy/mitigations/antispeed/MagnitudeWeighter.kt b/src/main/kotlin/com/puffy/mitigations/antispeed/MagnitudeWeighter.kt index 3f565fc..8208cef 100644 --- a/src/main/kotlin/com/puffy/mitigations/antispeed/MagnitudeWeighter.kt +++ b/src/main/kotlin/com/puffy/mitigations/antispeed/MagnitudeWeighter.kt @@ -1,6 +1,7 @@ package com.puffy.mitigations.antispeed import java.util.function.BiPredicate +import net.minecraft.item.Items import net.minecraft.server.network.ServerPlayerEntity import org.slf4j.LoggerFactory @@ -32,6 +33,10 @@ object MagnitudeWeighter { }, Weight("Server-permitted Velocity Bias", 0.25) { player, _ -> player.velocity.length() > SERVER_VELOCITY_THRESHOLD + }, + Weight("Server-permitted Firework Bias", 0.10) { player, _ -> + player.isFallFlying && + player.inventory.main.any { stack -> stack.isOf(Items.FIREWORK_ROCKET) } } ) @@ -49,7 +54,7 @@ object MagnitudeWeighter { val activeWeights = getActiveWeightsForPlayer(player, lastY) activeWeights.forEach { weight -> - logger.debug("Weight '${weight.title}' is active for player ${player.name.string}") + logger.info("Weight '${weight.title}' is active for player ${player.name.string}") } return initialMagnitude * getTotalWeightedMultiplier(activeWeights) diff --git a/src/main/kotlin/com/puffy/util/IsAllowedFlight.kt b/src/main/kotlin/com/puffy/util/IsAllowedFlight.kt new file mode 100644 index 0000000..38c6cfc --- /dev/null +++ b/src/main/kotlin/com/puffy/util/IsAllowedFlight.kt @@ -0,0 +1,23 @@ +package com.puffy.util + +import net.minecraft.entity.effect.StatusEffect +import net.minecraft.server.network.ServerPlayerEntity + +// https://minecraft.fandom.com/wiki/Levitation +private const val LEVITATION_STATUS_ID = 25 +// Likely, once the player's velocity reaches this threshold it's probably from a tnt jump or mod +private const val FLIGHT_SPEED_THRESHOLD = 5 + +fun ServerPlayerEntity.isAllowedFlight(): Boolean { + // Water internally is implemented as an alternative movement style which isn't walking or + // flight, but a little in between. So this returns true for water to be safe + + return (this.abilities.flying || this.abilities.allowFlying) || + this.isTouchingWater || + this.isSubmergedInWater || + this.isSwimming || + this.isFallFlying || + this.hasStatusEffect(StatusEffect.byRawId(LEVITATION_STATUS_ID)) || + this.velocity.length() >= FLIGHT_SPEED_THRESHOLD || + this.fallDistance > 0 +} diff --git a/src/main/kotlin/com/puffy/util/IsInAir.kt b/src/main/kotlin/com/puffy/util/IsInAir.kt new file mode 100644 index 0000000..93da377 --- /dev/null +++ b/src/main/kotlin/com/puffy/util/IsInAir.kt @@ -0,0 +1,16 @@ +package com.puffy.util + +import net.minecraft.server.network.ServerPlayerEntity + +private const val Y_BIAS = 0.2 + +fun ServerPlayerEntity.isInAir(): Boolean { + val world = this.entityWorld + val boundingBox = this.boundingBox.expand(0.0, Y_BIAS, 0.0) + + val collidingBlocks = world.getBlockCollisions(this, boundingBox) + val collidingEntities = + world.getOtherEntities(this, boundingBox) { entity -> !entity.equals(this) } + + return !collidingBlocks.any() && !collidingEntities.any() +} diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 525bb16..dbc144e 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -1,7 +1,7 @@ { "schemaVersion": 1, "id": "puffy-anti-exploit", - "version": "0.2.6", + "version": "0.2.7", "name": "puffy-anti-exploit", "description": "A server-side anti exploit which aims to reduce the possible amount of advantages a player can obtain with a hacked client. This mod does not attempt to ban such players, it only aims to fix certain parts of the server and gently counter these players.", "authors": [