Skip to content

Commit

Permalink
Clean up anti-fly mitigation and patch elytra fireworks
Browse files Browse the repository at this point in the history
  • Loading branch information
yogwoggf committed Apr 27, 2024
1 parent 93f5eab commit 763bac5
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 36 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
41 changes: 8 additions & 33 deletions src/main/kotlin/com/puffy/mitigations/AntiFlyMitigation.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,24 @@ 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

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) { _ ->
Expand All @@ -37,37 +34,26 @@ 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
}
}

private fun updateTicksAscending() {
if (player.y > lastY && !isFlightAllowedAtCurrentTick()) {
if (player.y > lastY && !player.isAllowedFlight()) {
ticksSpentAscending++
} else {
ticksSpentAscending = 0
Expand All @@ -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
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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) }
}
)

Expand All @@ -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)
Expand Down
23 changes: 23 additions & 0 deletions src/main/kotlin/com/puffy/util/IsAllowedFlight.kt
Original file line number Diff line number Diff line change
@@ -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
}
16 changes: 16 additions & 0 deletions src/main/kotlin/com/puffy/util/IsInAir.kt
Original file line number Diff line number Diff line change
@@ -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()
}
2 changes: 1 addition & 1 deletion src/main/resources/fabric.mod.json
Original file line number Diff line number Diff line change
@@ -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": [
Expand Down

0 comments on commit 763bac5

Please sign in to comment.