mirror of
https://github.com/CCBlueX/LiquidBounce.git
synced 2025-09-04 16:27:38 +00:00
Added cache to player simulation (#3759)
This commit is contained in:
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
16
gradlew
vendored
16
gradlew
vendored
@@ -55,7 +55,7 @@
|
|||||||
# Darwin, MinGW, and NonStop.
|
# Darwin, MinGW, and NonStop.
|
||||||
#
|
#
|
||||||
# (3) This script is generated from the Groovy template
|
# (3) This script is generated from the Groovy template
|
||||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
# within the Gradle project.
|
# within the Gradle project.
|
||||||
#
|
#
|
||||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
@@ -145,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
|||||||
case $MAX_FD in #(
|
case $MAX_FD in #(
|
||||||
max*)
|
max*)
|
||||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||||
# shellcheck disable=SC3045
|
# shellcheck disable=SC2039,SC3045
|
||||||
MAX_FD=$( ulimit -H -n ) ||
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
warn "Could not query maximum file descriptor limit"
|
warn "Could not query maximum file descriptor limit"
|
||||||
esac
|
esac
|
||||||
@@ -153,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
|||||||
'' | soft) :;; #(
|
'' | soft) :;; #(
|
||||||
*)
|
*)
|
||||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||||
# shellcheck disable=SC3045
|
# shellcheck disable=SC2039,SC3045
|
||||||
ulimit -n "$MAX_FD" ||
|
ulimit -n "$MAX_FD" ||
|
||||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
esac
|
esac
|
||||||
@@ -202,11 +202,11 @@ fi
|
|||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
# Collect all arguments for the java command;
|
# Collect all arguments for the java command:
|
||||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||||
# shell script including quotes and variable substitutions, so put them in
|
# and any embedded shellness will be escaped.
|
||||||
# double quotes to make sure that they get re-expanded; and
|
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||||
# * put everything else in single quotes, so that it's not re-expanded.
|
# treated as '${Hostname}' itself on the command line.
|
||||||
|
|
||||||
set -- \
|
set -- \
|
||||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
20
gradlew.bat
vendored
20
gradlew.bat
vendored
@@ -43,11 +43,11 @@ set JAVA_EXE=java.exe
|
|||||||
%JAVA_EXE% -version >NUL 2>&1
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
if %ERRORLEVEL% equ 0 goto execute
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation.
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
@@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
|||||||
|
|
||||||
if exist "%JAVA_EXE%" goto execute
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation.
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
|
@@ -379,7 +379,7 @@ object FakeLag : Listenable {
|
|||||||
val arrows = ModuleAutoDodge.findFlyingArrows(world)
|
val arrows = ModuleAutoDodge.findFlyingArrows(world)
|
||||||
val playerSimulation = RigidPlayerSimulation(pos)
|
val playerSimulation = RigidPlayerSimulation(pos)
|
||||||
|
|
||||||
return ModuleAutoDodge.getInflictedHits(playerSimulation, arrows, maxTicks = 40) { }
|
return ModuleAutoDodge.getInflictedHits(playerSimulation, arrows, maxTicks = 40)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class LagResult {
|
enum class LagResult {
|
||||||
|
@@ -25,7 +25,6 @@ import net.ccbluex.liquidbounce.event.handler
|
|||||||
import net.ccbluex.liquidbounce.event.repeatable
|
import net.ccbluex.liquidbounce.event.repeatable
|
||||||
import net.ccbluex.liquidbounce.features.module.Category
|
import net.ccbluex.liquidbounce.features.module.Category
|
||||||
import net.ccbluex.liquidbounce.features.module.Module
|
import net.ccbluex.liquidbounce.features.module.Module
|
||||||
import net.ccbluex.liquidbounce.features.module.modules.render.murdermystery.ModuleMurderMystery
|
|
||||||
import net.ccbluex.liquidbounce.render.renderEnvironmentForGUI
|
import net.ccbluex.liquidbounce.render.renderEnvironmentForGUI
|
||||||
import net.ccbluex.liquidbounce.utils.aiming.Rotation
|
import net.ccbluex.liquidbounce.utils.aiming.Rotation
|
||||||
import net.ccbluex.liquidbounce.utils.aiming.RotationManager
|
import net.ccbluex.liquidbounce.utils.aiming.RotationManager
|
||||||
@@ -180,12 +179,12 @@ object ModuleAutoBow : Module("AutoBow", Category.COMBAT, aliases = arrayOf("Bow
|
|||||||
arrow.tick()
|
arrow.tick()
|
||||||
|
|
||||||
players.forEach { (entity, player) ->
|
players.forEach { (entity, player) ->
|
||||||
player.tick()
|
val playerSnapshot = player.getSnapshotAt(i)
|
||||||
|
|
||||||
val playerHitBox =
|
val playerHitBox =
|
||||||
Box(-0.3, 0.0, -0.3, 0.3, 1.8, 0.3)
|
Box(-0.3, 0.0, -0.3, 0.3, 1.8, 0.3)
|
||||||
.expand(0.3)
|
.expand(0.3)
|
||||||
.offset(player.pos)
|
.offset(playerSnapshot.pos)
|
||||||
|
|
||||||
val raycastResult = playerHitBox.raycast(lastPos, arrow.pos)
|
val raycastResult = playerHitBox.raycast(lastPos, arrow.pos)
|
||||||
|
|
||||||
@@ -198,12 +197,12 @@ object ModuleAutoBow : Module("AutoBow", Category.COMBAT, aliases = arrayOf("Bow
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun findAndBuildSimulatedPlayers(): List<Pair<AbstractClientPlayerEntity, SimulatedPlayer>> {
|
fun findAndBuildSimulatedPlayers(): List<Pair<AbstractClientPlayerEntity, SimulatedPlayerCache>> {
|
||||||
return world.players.filter {
|
return world.players.filter {
|
||||||
it != player &&
|
it != player &&
|
||||||
Line(player.pos, player.rotationVector).squaredDistanceTo(it.pos) < 10.0 * 10.0
|
Line(player.pos, player.rotationVector).squaredDistanceTo(it.pos) < 10.0 * 10.0
|
||||||
}.map {
|
}.map {
|
||||||
Pair(it, SimulatedPlayer.fromOtherPlayer(it, SimulatedPlayer.SimulatedPlayerInput.guessInput(it)))
|
Pair(it, PlayerSimulationCache.getSimulationForOtherPlayers(it))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -30,7 +30,7 @@ import net.ccbluex.liquidbounce.render.engine.Color4b
|
|||||||
import net.ccbluex.liquidbounce.render.renderEnvironmentForWorld
|
import net.ccbluex.liquidbounce.render.renderEnvironmentForWorld
|
||||||
import net.ccbluex.liquidbounce.render.withColor
|
import net.ccbluex.liquidbounce.render.withColor
|
||||||
import net.ccbluex.liquidbounce.utils.combat.findEnemy
|
import net.ccbluex.liquidbounce.utils.combat.findEnemy
|
||||||
import net.ccbluex.liquidbounce.utils.entity.SimulatedPlayer
|
import net.ccbluex.liquidbounce.utils.entity.PlayerSimulationCache
|
||||||
import net.ccbluex.liquidbounce.utils.math.toVec3
|
import net.ccbluex.liquidbounce.utils.math.toVec3
|
||||||
import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket
|
import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket
|
||||||
import net.minecraft.util.math.Vec3d
|
import net.minecraft.util.math.Vec3d
|
||||||
@@ -149,8 +149,7 @@ internal object ModuleTickBase : Module("TickBase", Category.COMBAT) {
|
|||||||
|
|
||||||
tickBuffer.clear()
|
tickBuffer.clear()
|
||||||
|
|
||||||
val input = SimulatedPlayer.SimulatedPlayerInput.fromClientPlayer(event.directionalInput)
|
val simulatedPlayer = PlayerSimulationCache.getSimulationForLocalPlayer()
|
||||||
val simulatedPlayer = SimulatedPlayer.fromClientPlayer(input)
|
|
||||||
|
|
||||||
if (tickBalance <= 0) {
|
if (tickBalance <= 0) {
|
||||||
reachedTheLimit = true
|
reachedTheLimit = true
|
||||||
@@ -166,13 +165,15 @@ internal object ModuleTickBase : Module("TickBase", Category.COMBAT) {
|
|||||||
return@handler
|
return@handler
|
||||||
}
|
}
|
||||||
|
|
||||||
repeat(min(tickBalance.toInt(), maxTicksAtATime)) {
|
val tickRange = 0 until min(tickBalance.toInt(), maxTicksAtATime)
|
||||||
simulatedPlayer.tick()
|
val snapshots = simulatedPlayer.getSnapshotsBetween(tickRange)
|
||||||
|
|
||||||
|
snapshots.forEach {
|
||||||
tickBuffer += TickData(
|
tickBuffer += TickData(
|
||||||
simulatedPlayer.pos,
|
it.pos,
|
||||||
simulatedPlayer.fallDistance,
|
it.fallDistance,
|
||||||
simulatedPlayer.velocity,
|
it.velocity,
|
||||||
simulatedPlayer.onGround
|
it.onGround
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -28,9 +28,10 @@ import net.ccbluex.liquidbounce.features.module.modules.render.murdermystery.Mod
|
|||||||
import net.ccbluex.liquidbounce.features.module.modules.world.scaffold.ModuleScaffold
|
import net.ccbluex.liquidbounce.features.module.modules.world.scaffold.ModuleScaffold
|
||||||
import net.ccbluex.liquidbounce.utils.client.EventScheduler
|
import net.ccbluex.liquidbounce.utils.client.EventScheduler
|
||||||
import net.ccbluex.liquidbounce.utils.client.Timer
|
import net.ccbluex.liquidbounce.utils.client.Timer
|
||||||
|
import net.ccbluex.liquidbounce.utils.entity.CachedPlayerSimulation
|
||||||
import net.ccbluex.liquidbounce.utils.entity.PlayerSimulation
|
import net.ccbluex.liquidbounce.utils.entity.PlayerSimulation
|
||||||
|
import net.ccbluex.liquidbounce.utils.entity.PlayerSimulationCache
|
||||||
import net.ccbluex.liquidbounce.utils.entity.SimulatedArrow
|
import net.ccbluex.liquidbounce.utils.entity.SimulatedArrow
|
||||||
import net.ccbluex.liquidbounce.utils.entity.SimulatedPlayer
|
|
||||||
import net.ccbluex.liquidbounce.utils.kotlin.Priority
|
import net.ccbluex.liquidbounce.utils.kotlin.Priority
|
||||||
import net.minecraft.client.world.ClientWorld
|
import net.minecraft.client.world.ClientWorld
|
||||||
import net.minecraft.entity.projectile.ArrowEntity
|
import net.minecraft.entity.projectile.ArrowEntity
|
||||||
@@ -65,12 +66,10 @@ object ModuleAutoDodge : Module("AutoDodge", Category.COMBAT) {
|
|||||||
|
|
||||||
val arrows = findFlyingArrows(world)
|
val arrows = findFlyingArrows(world)
|
||||||
|
|
||||||
val input = SimulatedPlayer.SimulatedPlayerInput.fromClientPlayer(event.directionalInput)
|
val simulatedPlayer = CachedPlayerSimulation(PlayerSimulationCache.getSimulationForLocalPlayer())
|
||||||
|
|
||||||
val simulatedPlayer = SimulatedPlayer.fromClientPlayer(input)
|
|
||||||
|
|
||||||
val inflictedHit =
|
val inflictedHit =
|
||||||
getInflictedHits(simulatedPlayer, arrows, hitboxExpansion = DodgePlanner.SAFE_DISTANCE_WITH_PADDING) {}
|
getInflictedHits(simulatedPlayer, arrows, hitboxExpansion = DodgePlanner.SAFE_DISTANCE_WITH_PADDING)
|
||||||
?: return@handler
|
?: return@handler
|
||||||
|
|
||||||
val dodgePlan =
|
val dodgePlan =
|
||||||
@@ -112,13 +111,10 @@ object ModuleAutoDodge : Module("AutoDodge", Category.COMBAT) {
|
|||||||
arrows: List<ArrowEntity>,
|
arrows: List<ArrowEntity>,
|
||||||
maxTicks: Int = 80,
|
maxTicks: Int = 80,
|
||||||
hitboxExpansion: Double = 0.7,
|
hitboxExpansion: Double = 0.7,
|
||||||
behaviour: (T) -> Unit,
|
|
||||||
): HitInfo? {
|
): HitInfo? {
|
||||||
val simulatedArrows = arrows.map { SimulatedArrow(world, it.pos, it.velocity, false) }
|
val simulatedArrows = arrows.map { SimulatedArrow(world, it.pos, it.velocity, false) }
|
||||||
|
|
||||||
for (i in 0 until maxTicks) {
|
for (i in 0 until maxTicks) {
|
||||||
behaviour(simulatedPlayer)
|
|
||||||
|
|
||||||
simulatedPlayer.tick()
|
simulatedPlayer.tick()
|
||||||
|
|
||||||
simulatedArrows.forEachIndexed { arrowIndex, arrow ->
|
simulatedArrows.forEachIndexed { arrowIndex, arrow ->
|
||||||
|
@@ -32,7 +32,7 @@ import net.ccbluex.liquidbounce.features.module.modules.player.ModuleBlink
|
|||||||
import net.ccbluex.liquidbounce.features.module.modules.world.scaffold.ModuleScaffold
|
import net.ccbluex.liquidbounce.features.module.modules.world.scaffold.ModuleScaffold
|
||||||
import net.ccbluex.liquidbounce.render.*
|
import net.ccbluex.liquidbounce.render.*
|
||||||
import net.ccbluex.liquidbounce.render.engine.Color4b
|
import net.ccbluex.liquidbounce.render.engine.Color4b
|
||||||
import net.ccbluex.liquidbounce.utils.entity.SimulatedPlayer
|
import net.ccbluex.liquidbounce.utils.entity.PlayerSimulationCache
|
||||||
import net.ccbluex.liquidbounce.utils.entity.eyes
|
import net.ccbluex.liquidbounce.utils.entity.eyes
|
||||||
import net.ccbluex.liquidbounce.utils.math.geometry.Face
|
import net.ccbluex.liquidbounce.utils.math.geometry.Face
|
||||||
import net.ccbluex.liquidbounce.utils.math.geometry.Line
|
import net.ccbluex.liquidbounce.utils.math.geometry.Line
|
||||||
@@ -59,33 +59,26 @@ object ModuleDebug : Module("Debug", Category.RENDER) {
|
|||||||
object RenderSimulatedPlayer : ToggleableConfigurable(this, "SimulatedPlayer", false) {
|
object RenderSimulatedPlayer : ToggleableConfigurable(this, "SimulatedPlayer", false) {
|
||||||
|
|
||||||
private val ticksToPredict by int("TicksToPredict", 20, 5..100)
|
private val ticksToPredict by int("TicksToPredict", 20, 5..100)
|
||||||
private val simLines = mutableListOf<Vec3d>()
|
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
val tickRep = handler<MovementInputEvent> { event ->
|
val tickRep = handler<MovementInputEvent> { _ ->
|
||||||
// We aren't actually where we are because of blink.
|
// We aren't actually where we are because of blink.
|
||||||
// So this module shall not cause any disturbance in that case.
|
// So this module shall not cause any disturbance in that case.
|
||||||
if (ModuleBlink.enabled) {
|
if (ModuleBlink.enabled) {
|
||||||
return@handler
|
return@handler
|
||||||
}
|
}
|
||||||
|
|
||||||
simLines.clear()
|
PlayerSimulationCache.getSimulationForLocalPlayer().simulateUntil(this.ticksToPredict)
|
||||||
|
|
||||||
val input =
|
|
||||||
SimulatedPlayer.SimulatedPlayerInput.fromClientPlayer(event.directionalInput)
|
|
||||||
|
|
||||||
val simulatedPlayer = SimulatedPlayer.fromClientPlayer(input)
|
|
||||||
|
|
||||||
repeat(ticksToPredict) {
|
|
||||||
simulatedPlayer.tick()
|
|
||||||
simLines.add(simulatedPlayer.pos)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val renderHandler = handler<WorldRenderEvent> { event ->
|
val renderHandler = handler<WorldRenderEvent> { event ->
|
||||||
|
val cachedPositions = PlayerSimulationCache
|
||||||
|
.getSimulationForLocalPlayer()
|
||||||
|
.getSnapshotsBetween(0 until this.ticksToPredict)
|
||||||
|
|
||||||
renderEnvironmentForWorld(event.matrixStack) {
|
renderEnvironmentForWorld(event.matrixStack) {
|
||||||
withColor(Color4b.BLUE) {
|
withColor(Color4b.BLUE) {
|
||||||
drawLineStrip(positions = simLines.map { relativeToCamera(it).toVec3() }.toTypedArray())
|
drawLineStrip(positions = cachedPositions.map { relativeToCamera(it.pos).toVec3() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,10 +129,6 @@ object ModuleDebug : Module("Debug", Category.RENDER) {
|
|||||||
|
|
||||||
val pointTo = face.nearestPointTo(line)
|
val pointTo = face.nearestPointTo(line)
|
||||||
|
|
||||||
if (pointTo == null) {
|
|
||||||
return@repeatable
|
|
||||||
}
|
|
||||||
|
|
||||||
ModuleDebug.debugGeometry(
|
ModuleDebug.debugGeometry(
|
||||||
ModuleScaffold,
|
ModuleScaffold,
|
||||||
"targetPoint",
|
"targetPoint",
|
||||||
@@ -212,7 +201,7 @@ object ModuleDebug : Module("Debug", Category.RENDER) {
|
|||||||
textList.forEachIndexed { index, text ->
|
textList.forEachIndexed { index, text ->
|
||||||
context.drawCenteredTextWithShadow(
|
context.drawCenteredTextWithShadow(
|
||||||
mc.textRenderer, text, width / 2, 40 +
|
mc.textRenderer, text, width / 2, 40 +
|
||||||
(mc.textRenderer.fontHeight * index), Color4b.WHITE.toRGBA()
|
(mc.textRenderer.fontHeight * index), Color4b.WHITE.toRGBA()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,151 @@
|
|||||||
|
package net.ccbluex.liquidbounce.utils.entity
|
||||||
|
|
||||||
|
import net.ccbluex.liquidbounce.event.Listenable
|
||||||
|
import net.ccbluex.liquidbounce.event.events.GameTickEvent
|
||||||
|
import net.ccbluex.liquidbounce.event.events.MovementInputEvent
|
||||||
|
import net.ccbluex.liquidbounce.event.handler
|
||||||
|
import net.ccbluex.liquidbounce.utils.client.player
|
||||||
|
import net.ccbluex.liquidbounce.utils.movement.DirectionalInput
|
||||||
|
import net.minecraft.entity.player.PlayerEntity
|
||||||
|
import net.minecraft.util.math.Vec3d
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||||
|
import kotlin.concurrent.read
|
||||||
|
import kotlin.concurrent.write
|
||||||
|
|
||||||
|
object PlayerSimulationCache: Listenable {
|
||||||
|
private val otherPlayerCache = ConcurrentHashMap<PlayerEntity, SimulatedPlayerCache>()
|
||||||
|
private var localPlayerCache: SimulatedPlayerCache? = null
|
||||||
|
|
||||||
|
private val gameTickHandler = handler<GameTickEvent> {
|
||||||
|
this.otherPlayerCache.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val movementHandler = handler<MovementInputEvent> {
|
||||||
|
val simulatedPlayer = SimulatedPlayer.fromClientPlayer(
|
||||||
|
SimulatedPlayer.SimulatedPlayerInput.fromClientPlayer(it.directionalInput)
|
||||||
|
)
|
||||||
|
|
||||||
|
localPlayerCache = SimulatedPlayerCache(simulatedPlayer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSimulationForOtherPlayers(player: PlayerEntity): SimulatedPlayerCache {
|
||||||
|
return otherPlayerCache.computeIfAbsent(player) {
|
||||||
|
val simulatedPlayer = SimulatedPlayer.fromOtherPlayer(
|
||||||
|
player,
|
||||||
|
SimulatedPlayer.SimulatedPlayerInput.guessInput(player)
|
||||||
|
)
|
||||||
|
|
||||||
|
SimulatedPlayerCache(simulatedPlayer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSimulationForLocalPlayer(): SimulatedPlayerCache {
|
||||||
|
val cached = localPlayerCache
|
||||||
|
|
||||||
|
if (cached != null) {
|
||||||
|
return cached
|
||||||
|
}
|
||||||
|
|
||||||
|
val simulatedPlayer = SimulatedPlayer.fromClientPlayer(
|
||||||
|
SimulatedPlayer.SimulatedPlayerInput.fromClientPlayer(DirectionalInput(player.input))
|
||||||
|
)
|
||||||
|
|
||||||
|
val simulatedPlayerCache = SimulatedPlayerCache(simulatedPlayer)
|
||||||
|
|
||||||
|
localPlayerCache = simulatedPlayerCache
|
||||||
|
|
||||||
|
return simulatedPlayerCache
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SimulatedPlayerCache(private val simulatedPlayer: SimulatedPlayer) {
|
||||||
|
private var currentSimulationStep = 0
|
||||||
|
private val simulationSteps = ArrayList<SimulatedPlayerSnapshot>().apply {
|
||||||
|
add(SimulatedPlayerSnapshot(simulatedPlayer))
|
||||||
|
}
|
||||||
|
private val lock = ReentrantReadWriteLock()
|
||||||
|
|
||||||
|
fun simulateUntil(ticks: Int) {
|
||||||
|
check(ticks > 0) { "ticks must be greater than 0" }
|
||||||
|
|
||||||
|
if (currentSimulationStep >= ticks) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
lock.write {
|
||||||
|
while (currentSimulationStep < ticks) {
|
||||||
|
simulatedPlayer.tick()
|
||||||
|
simulationSteps.add(SimulatedPlayerSnapshot(simulatedPlayer))
|
||||||
|
|
||||||
|
this.currentSimulationStep++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSnapshotAt(ticks: Int): SimulatedPlayerSnapshot {
|
||||||
|
simulateUntil(ticks)
|
||||||
|
|
||||||
|
lock.read {
|
||||||
|
return simulationSteps[ticks]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun simulate() = sequence<SimulatedPlayerSnapshot> {
|
||||||
|
var idx = 0
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
yield(getSnapshotAt(idx))
|
||||||
|
|
||||||
|
idx++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSnapshotsBetween(tickRange: IntRange): List<SimulatedPlayerSnapshot> {
|
||||||
|
check(tickRange.endInclusive < 60 * 20) { "tried to simulate a player for more than a minute!" }
|
||||||
|
|
||||||
|
simulateUntil(tickRange.endInclusive + 1)
|
||||||
|
|
||||||
|
return lock.read {
|
||||||
|
ArrayList(simulationSteps.subList(tickRange.start, tickRange.endInclusive + 1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun simulateBetween(tickRange: IntRange): Sequence<SimulatedPlayerSnapshot> {
|
||||||
|
check(tickRange.endInclusive < 60 * 20) { "tried to simulate a player for more than a minute!" }
|
||||||
|
|
||||||
|
simulateUntil(tickRange.endInclusive + 1)
|
||||||
|
|
||||||
|
return sequence<SimulatedPlayerSnapshot> {
|
||||||
|
var idx = 0
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
yield(getSnapshotAt(idx))
|
||||||
|
|
||||||
|
idx++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class SimulatedPlayerSnapshot(s: SimulatedPlayer) {
|
||||||
|
val pos = s.pos
|
||||||
|
val fallDistance = s.fallDistance
|
||||||
|
val velocity = s.velocity
|
||||||
|
val onGround = s.onGround
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Yes, this name sucks as [SimulatedPlayerCache] already exists, but I don't know a better name :/
|
||||||
|
*/
|
||||||
|
class CachedPlayerSimulation(val simulatedPlayer: SimulatedPlayerCache): PlayerSimulation {
|
||||||
|
override val pos: Vec3d
|
||||||
|
get() = this.simulatedPlayer.getSnapshotAt(this.ticks).pos
|
||||||
|
|
||||||
|
private var ticks = 0
|
||||||
|
|
||||||
|
override fun tick() {
|
||||||
|
this.ticks++
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user