physics: handle physics and add gravity

This commit is contained in:
Lukas 2021-04-17 14:49:22 +02:00
parent caebbdef67
commit a8a565cf44
7 changed files with 106 additions and 26 deletions

View File

@ -32,6 +32,7 @@ import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import glm_.vec3.Vec3
import java.lang.reflect.InvocationTargetException
import java.util.*
import kotlin.math.pow
abstract class Entity(
protected val connection: PlayConnection,
@ -52,6 +53,8 @@ abstract class Entity(
protected open val hasCollisions = true
var onGround = false
private val defaultAABB = AABB(
Vec3(-(entityType.width / 2 + HITBOX_MARGIN), 0, -(entityType.width / 2 + HITBOX_MARGIN)),
Vec3(+(entityType.width / 2 + HITBOX_MARGIN), entityType.height, +(entityType.width / 2 + HITBOX_MARGIN))
@ -214,30 +217,47 @@ abstract class Entity(
}
private fun collide(deltaPosition: Vec3, collisionsToCheck: VoxelShape, aabb: AABB): Vec3 {
onGround = false
val delta = Vec3(deltaPosition)
if (delta.y != 0.0f) {
delta.y = collisionsToCheck.computeOffset(aabb, deltaPosition.y, Axes.Y)
aabb.offsetAssign(0f, delta.y, 0f)
if (delta.y != deltaPosition.y) {
velocity?.y = 0.0f
if (deltaPosition.y < 0) {
onGround = true;
}
}
}
val xPriority = delta.x > delta.z
if (delta.x != 0.0f && xPriority) {
delta.x = collisionsToCheck.computeOffset(aabb, deltaPosition.x, Axes.X)
aabb.offsetAssign(delta.x, 0f, 0f)
if (delta.x != deltaPosition.x) {
velocity?.x = 0.0f
}
}
if (delta.z != 0.0f) {
delta.z = collisionsToCheck.computeOffset(aabb, deltaPosition.z, Axes.Z)
aabb.offsetAssign(0f, 0f, delta.z)
if (delta.z != deltaPosition.z) {
velocity?.z = 0.0f
}
}
if (delta.x != 0.0f && !xPriority) {
delta.x = collisionsToCheck.computeOffset(aabb, deltaPosition.x, Axes.X)
if (delta.x != deltaPosition.x) {
velocity?.x = 0.0f
}
}
return delta
}
fun computeTimeStep(deltaTime: Float) {
fun computeTimeStep(deltaMillis: Long) {
val deltaTime = deltaMillis.toFloat() / 1000.0f
if (! hasNoGravity) {
if (velocity == null) {
velocity = Vec3(0, deltaTime * ProtocolDefinition.GRAVITY)
velocity = Vec3(0, deltaTime * ProtocolDefinition.GRAVITY, 0)
} else {
velocity!!.y += deltaTime * ProtocolDefinition.GRAVITY;
}
@ -245,6 +265,14 @@ abstract class Entity(
if (velocity == VecUtil.EMPTY_VEC3) {
velocity = null
}
if (velocity == null) {
return
}
velocity?.timesAssign(0.25f.pow(deltaTime))
if (velocity?.length()!! < 0.05f) {
velocity = null
return
}
velocity?.let {
move(velocity!! * deltaTime)
}

View File

@ -206,7 +206,6 @@ class Camera(
for (shader in shaders) {
shader.use().setMat4("viewProjectionMatrix", matrix)
}
positionChangeCallback()
}
@ -236,10 +235,20 @@ class Camera(
}
private fun calculateViewMatrix(): Mat4 {
cameraPosition = playerEntity.position + Vec3(0, PLAYER_HEIGHT, 0)
cameraPosition = getAbsoluteCameraPosition()
return glm.lookAt(cameraPosition, cameraPosition + cameraFront, CAMERA_UP_VEC3)
}
private fun getAbsoluteCameraPosition(): Vec3 {
return playerEntity.position + Vec3(0, PLAYER_HEIGHT, 0)
}
fun checkPosition() {
if (cameraPosition != getAbsoluteCameraPosition()) {
currentPositionSent = false
}
}
fun setRotation(yaw: Float, pitch: Float) {
playerEntity.rotation = EntityRotation(yaw.toDouble(), pitch.toDouble())
@ -258,34 +267,21 @@ class Camera(
fun draw() {
if (!currentPositionSent || !currentRotationSent) {
recalculateViewProjectionMatrix()
sendPositionToServer()
}
}
private fun sendPositionToServer() {
if (System.currentTimeMillis() - lastMovementPacketSent > ProtocolDefinition.TICK_TIME) {
if (!currentPositionSent && !currentPositionSent) {
connection.sendPacket(PlayerPositionAndRotationC2SPacket(playerEntity.position, playerEntity.rotation, false))
} else if (!currentPositionSent) {
connection.sendPacket(PlayerPositionC2SPacket(playerEntity.position, false))
} else {
connection.sendPacket(PlayerRotationC2SPacket(playerEntity.rotation, false))
}
lastMovementPacketSent = System.currentTimeMillis()
currentPositionSent = true
currentRotationSent = true
return
}
if (System.currentTimeMillis() - lastMovementPacketSent < ProtocolDefinition.TICK_TIME) {
return
}
if (!currentPositionSent && !currentPositionSent) {
connection.sendPacket(PlayerPositionAndRotationC2SPacket(cameraPosition - Vec3(0, PLAYER_HEIGHT, 0), EntityRotation(yaw, pitch), false))
connection.sendPacket(PlayerPositionAndRotationC2SPacket(playerEntity.position, playerEntity.rotation, playerEntity.onGround))
} else if (!currentPositionSent) {
connection.sendPacket(PlayerPositionC2SPacket(cameraPosition - Vec3(0, PLAYER_HEIGHT, 0), false))
connection.sendPacket(PlayerPositionC2SPacket(playerEntity.position, playerEntity.onGround))
} else {
connection.sendPacket(PlayerRotationC2SPacket(EntityRotation(yaw, pitch), false))
connection.sendPacket(PlayerRotationC2SPacket(playerEntity.rotation, playerEntity.onGround))
}
lastMovementPacketSent = System.currentTimeMillis()
currentPositionSent = true

View File

@ -35,16 +35,15 @@ import de.bixilon.minosoft.protocol.packets.c2s.handshaking.HandshakeC2SPacket
import de.bixilon.minosoft.protocol.packets.c2s.login.LoginStartC2SPacket
import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket
import de.bixilon.minosoft.protocol.packets.s2c.S2CPacket
import de.bixilon.minosoft.protocol.protocol.ConnectionStates
import de.bixilon.minosoft.protocol.protocol.PacketSender
import de.bixilon.minosoft.protocol.protocol.PacketTypes
import de.bixilon.minosoft.protocol.protocol.Protocol
import de.bixilon.minosoft.protocol.protocol.*
import de.bixilon.minosoft.terminal.CLI
import de.bixilon.minosoft.terminal.commands.commands.Command
import de.bixilon.minosoft.util.CountUpAndDownLatch
import de.bixilon.minosoft.util.ServerAddress
import de.bixilon.minosoft.util.logging.Log
import de.bixilon.minosoft.util.logging.LogLevels
import de.bixilon.minosoft.util.time.TimeWorker
import de.bixilon.minosoft.util.time.TimeWorkerTask
class PlayConnection(
@ -70,6 +69,9 @@ class PlayConnection(
private set
private var _connectionState: ConnectionStates = ConnectionStates.DISCONNECTED
lateinit var velocityHandlerTask: TimeWorkerTask
private var velocityHandlerLastExecutionTime: Long = 0L
override var connectionState: ConnectionStates
get() = _connectionState
set(value) {
@ -112,6 +114,17 @@ class PlayConnection(
if (CLI.getCurrentConnection() == null) {
CLI.setCurrentConnection(this)
}
velocityHandlerLastExecutionTime = System.currentTimeMillis()
velocityHandlerTask = TimeWorkerTask(ProtocolDefinition.TICK_TIME / 4) {
val currentTime = System.currentTimeMillis();
val deltaTime = currentTime - velocityHandlerLastExecutionTime
for (entity in world.entityIdMap.values) {
entity.computeTimeStep(deltaTime)
}
renderer?.renderWindow?.camera?.checkPosition()
velocityHandlerLastExecutionTime = currentTime
}
TimeWorker.addTask(velocityHandlerTask)
}
ConnectionStates.DISCONNECTED -> {
// unregister all custom recipes
@ -121,6 +134,9 @@ class PlayConnection(
CLI.setCurrentConnection(null)
Command.print("Disconnected from current connection!")
}
if (this::velocityHandlerTask.isInitialized) {
TimeWorker.removeTask(velocityHandlerTask)
}
}
else -> {
}

View File

@ -93,7 +93,7 @@ public final class ProtocolDefinition {
public static final int TICKS_PER_SECOND = 20;
public static final int TICK_TIME = 1000 / TICKS_PER_SECOND;
public static final float VELOCITY_CONSTANT = 1 / 8000f * TICKS_PER_SECOND;
public static final float VELOCITY_CONSTANT = (float) TICKS_PER_SECOND / 80f;
public static final float GRAVITY = -10; // TODO: find the correct value

View File

@ -23,6 +23,7 @@ import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer;
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition;
import de.bixilon.minosoft.util.logging.Log;
import de.bixilon.minosoft.util.microsoft.MicrosoftOAuthUtils;
import de.bixilon.minosoft.util.time.TimeWorker;
import org.jetbrains.annotations.NotNull;
import java.io.*;
@ -286,6 +287,7 @@ public final class Util {
public static void initUtilClasses() {
forceClassInit(Log.class);
forceClassInit(MicrosoftOAuthUtils.class);
forceClassInit(TimeWorker.class);
}
public static Map<String, String> urlQueryToMap(String query) {

View File

@ -0,0 +1,30 @@
package de.bixilon.minosoft.util.time
import de.bixilon.minosoft.Minosoft
object TimeWorker {
private val TASKS: MutableList<TimeWorkerTask> = mutableListOf()
init {
Thread({
while (true) {
val currentTime = System.currentTimeMillis();
for (task in TASKS) {
if (currentTime - task.lastExecution >= task.interval) {
Minosoft.THREAD_POOL.execute(task.runnable)
task.lastExecution = currentTime
}
}
Thread.sleep(1)
}
}, "TimeWorkerThread").start()
}
fun addTask(task: TimeWorkerTask) {
TASKS.add(task)
}
fun removeTask(task: TimeWorkerTask) {
TASKS.remove(task)
}
}

View File

@ -0,0 +1,8 @@
package de.bixilon.minosoft.util.time
data class TimeWorkerTask(
val interval: Int,
val runnable: Runnable,
) {
var lastExecution: Long = 0L
}