mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-15 02:15:34 -04:00
physics: handle physics and add gravity
This commit is contained in:
parent
caebbdef67
commit
a8a565cf44
@ -32,6 +32,7 @@ import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
|
|||||||
import glm_.vec3.Vec3
|
import glm_.vec3.Vec3
|
||||||
import java.lang.reflect.InvocationTargetException
|
import java.lang.reflect.InvocationTargetException
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import kotlin.math.pow
|
||||||
|
|
||||||
abstract class Entity(
|
abstract class Entity(
|
||||||
protected val connection: PlayConnection,
|
protected val connection: PlayConnection,
|
||||||
@ -52,6 +53,8 @@ abstract class Entity(
|
|||||||
|
|
||||||
protected open val hasCollisions = true
|
protected open val hasCollisions = true
|
||||||
|
|
||||||
|
var onGround = false
|
||||||
|
|
||||||
private val defaultAABB = AABB(
|
private val defaultAABB = AABB(
|
||||||
Vec3(-(entityType.width / 2 + HITBOX_MARGIN), 0, -(entityType.width / 2 + HITBOX_MARGIN)),
|
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))
|
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 {
|
private fun collide(deltaPosition: Vec3, collisionsToCheck: VoxelShape, aabb: AABB): Vec3 {
|
||||||
|
onGround = false
|
||||||
val delta = Vec3(deltaPosition)
|
val delta = Vec3(deltaPosition)
|
||||||
if (delta.y != 0.0f) {
|
if (delta.y != 0.0f) {
|
||||||
delta.y = collisionsToCheck.computeOffset(aabb, deltaPosition.y, Axes.Y)
|
delta.y = collisionsToCheck.computeOffset(aabb, deltaPosition.y, Axes.Y)
|
||||||
aabb.offsetAssign(0f, delta.y, 0f)
|
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
|
val xPriority = delta.x > delta.z
|
||||||
if (delta.x != 0.0f && xPriority) {
|
if (delta.x != 0.0f && xPriority) {
|
||||||
delta.x = collisionsToCheck.computeOffset(aabb, deltaPosition.x, Axes.X)
|
delta.x = collisionsToCheck.computeOffset(aabb, deltaPosition.x, Axes.X)
|
||||||
aabb.offsetAssign(delta.x, 0f, 0f)
|
aabb.offsetAssign(delta.x, 0f, 0f)
|
||||||
|
if (delta.x != deltaPosition.x) {
|
||||||
|
velocity?.x = 0.0f
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (delta.z != 0.0f) {
|
if (delta.z != 0.0f) {
|
||||||
delta.z = collisionsToCheck.computeOffset(aabb, deltaPosition.z, Axes.Z)
|
delta.z = collisionsToCheck.computeOffset(aabb, deltaPosition.z, Axes.Z)
|
||||||
aabb.offsetAssign(0f, 0f, delta.z)
|
aabb.offsetAssign(0f, 0f, delta.z)
|
||||||
|
if (delta.z != deltaPosition.z) {
|
||||||
|
velocity?.z = 0.0f
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (delta.x != 0.0f && !xPriority) {
|
if (delta.x != 0.0f && !xPriority) {
|
||||||
delta.x = collisionsToCheck.computeOffset(aabb, deltaPosition.x, Axes.X)
|
delta.x = collisionsToCheck.computeOffset(aabb, deltaPosition.x, Axes.X)
|
||||||
|
if (delta.x != deltaPosition.x) {
|
||||||
|
velocity?.x = 0.0f
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return delta
|
return delta
|
||||||
}
|
}
|
||||||
|
|
||||||
fun computeTimeStep(deltaTime: Float) {
|
fun computeTimeStep(deltaMillis: Long) {
|
||||||
|
val deltaTime = deltaMillis.toFloat() / 1000.0f
|
||||||
if (! hasNoGravity) {
|
if (! hasNoGravity) {
|
||||||
if (velocity == null) {
|
if (velocity == null) {
|
||||||
velocity = Vec3(0, deltaTime * ProtocolDefinition.GRAVITY)
|
velocity = Vec3(0, deltaTime * ProtocolDefinition.GRAVITY, 0)
|
||||||
} else {
|
} else {
|
||||||
velocity!!.y += deltaTime * ProtocolDefinition.GRAVITY;
|
velocity!!.y += deltaTime * ProtocolDefinition.GRAVITY;
|
||||||
}
|
}
|
||||||
@ -245,6 +265,14 @@ abstract class Entity(
|
|||||||
if (velocity == VecUtil.EMPTY_VEC3) {
|
if (velocity == VecUtil.EMPTY_VEC3) {
|
||||||
velocity = null
|
velocity = null
|
||||||
}
|
}
|
||||||
|
if (velocity == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
velocity?.timesAssign(0.25f.pow(deltaTime))
|
||||||
|
if (velocity?.length()!! < 0.05f) {
|
||||||
|
velocity = null
|
||||||
|
return
|
||||||
|
}
|
||||||
velocity?.let {
|
velocity?.let {
|
||||||
move(velocity!! * deltaTime)
|
move(velocity!! * deltaTime)
|
||||||
}
|
}
|
||||||
|
@ -206,7 +206,6 @@ class Camera(
|
|||||||
for (shader in shaders) {
|
for (shader in shaders) {
|
||||||
shader.use().setMat4("viewProjectionMatrix", matrix)
|
shader.use().setMat4("viewProjectionMatrix", matrix)
|
||||||
}
|
}
|
||||||
|
|
||||||
positionChangeCallback()
|
positionChangeCallback()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,10 +235,20 @@ class Camera(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun calculateViewMatrix(): Mat4 {
|
private fun calculateViewMatrix(): Mat4 {
|
||||||
cameraPosition = playerEntity.position + Vec3(0, PLAYER_HEIGHT, 0)
|
cameraPosition = getAbsoluteCameraPosition()
|
||||||
return glm.lookAt(cameraPosition, cameraPosition + cameraFront, CAMERA_UP_VEC3)
|
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) {
|
fun setRotation(yaw: Float, pitch: Float) {
|
||||||
playerEntity.rotation = EntityRotation(yaw.toDouble(), pitch.toDouble())
|
playerEntity.rotation = EntityRotation(yaw.toDouble(), pitch.toDouble())
|
||||||
|
|
||||||
@ -258,34 +267,21 @@ class Camera(
|
|||||||
|
|
||||||
fun draw() {
|
fun draw() {
|
||||||
if (!currentPositionSent || !currentRotationSent) {
|
if (!currentPositionSent || !currentRotationSent) {
|
||||||
|
recalculateViewProjectionMatrix()
|
||||||
sendPositionToServer()
|
sendPositionToServer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun 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) {
|
if (System.currentTimeMillis() - lastMovementPacketSent < ProtocolDefinition.TICK_TIME) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!currentPositionSent && !currentPositionSent) {
|
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) {
|
} else if (!currentPositionSent) {
|
||||||
connection.sendPacket(PlayerPositionC2SPacket(cameraPosition - Vec3(0, PLAYER_HEIGHT, 0), false))
|
connection.sendPacket(PlayerPositionC2SPacket(playerEntity.position, playerEntity.onGround))
|
||||||
} else {
|
} else {
|
||||||
connection.sendPacket(PlayerRotationC2SPacket(EntityRotation(yaw, pitch), false))
|
connection.sendPacket(PlayerRotationC2SPacket(playerEntity.rotation, playerEntity.onGround))
|
||||||
}
|
}
|
||||||
lastMovementPacketSent = System.currentTimeMillis()
|
lastMovementPacketSent = System.currentTimeMillis()
|
||||||
currentPositionSent = true
|
currentPositionSent = true
|
||||||
|
@ -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.c2s.login.LoginStartC2SPacket
|
||||||
import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket
|
import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket
|
||||||
import de.bixilon.minosoft.protocol.packets.s2c.S2CPacket
|
import de.bixilon.minosoft.protocol.packets.s2c.S2CPacket
|
||||||
import de.bixilon.minosoft.protocol.protocol.ConnectionStates
|
import de.bixilon.minosoft.protocol.protocol.*
|
||||||
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.terminal.CLI
|
import de.bixilon.minosoft.terminal.CLI
|
||||||
import de.bixilon.minosoft.terminal.commands.commands.Command
|
import de.bixilon.minosoft.terminal.commands.commands.Command
|
||||||
import de.bixilon.minosoft.util.CountUpAndDownLatch
|
import de.bixilon.minosoft.util.CountUpAndDownLatch
|
||||||
import de.bixilon.minosoft.util.ServerAddress
|
import de.bixilon.minosoft.util.ServerAddress
|
||||||
import de.bixilon.minosoft.util.logging.Log
|
import de.bixilon.minosoft.util.logging.Log
|
||||||
import de.bixilon.minosoft.util.logging.LogLevels
|
import de.bixilon.minosoft.util.logging.LogLevels
|
||||||
|
import de.bixilon.minosoft.util.time.TimeWorker
|
||||||
|
import de.bixilon.minosoft.util.time.TimeWorkerTask
|
||||||
|
|
||||||
|
|
||||||
class PlayConnection(
|
class PlayConnection(
|
||||||
@ -70,6 +69,9 @@ class PlayConnection(
|
|||||||
private set
|
private set
|
||||||
private var _connectionState: ConnectionStates = ConnectionStates.DISCONNECTED
|
private var _connectionState: ConnectionStates = ConnectionStates.DISCONNECTED
|
||||||
|
|
||||||
|
lateinit var velocityHandlerTask: TimeWorkerTask
|
||||||
|
private var velocityHandlerLastExecutionTime: Long = 0L
|
||||||
|
|
||||||
override var connectionState: ConnectionStates
|
override var connectionState: ConnectionStates
|
||||||
get() = _connectionState
|
get() = _connectionState
|
||||||
set(value) {
|
set(value) {
|
||||||
@ -112,6 +114,17 @@ class PlayConnection(
|
|||||||
if (CLI.getCurrentConnection() == null) {
|
if (CLI.getCurrentConnection() == null) {
|
||||||
CLI.setCurrentConnection(this)
|
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 -> {
|
ConnectionStates.DISCONNECTED -> {
|
||||||
// unregister all custom recipes
|
// unregister all custom recipes
|
||||||
@ -121,6 +134,9 @@ class PlayConnection(
|
|||||||
CLI.setCurrentConnection(null)
|
CLI.setCurrentConnection(null)
|
||||||
Command.print("Disconnected from current connection!")
|
Command.print("Disconnected from current connection!")
|
||||||
}
|
}
|
||||||
|
if (this::velocityHandlerTask.isInitialized) {
|
||||||
|
TimeWorker.removeTask(velocityHandlerTask)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
}
|
}
|
||||||
|
@ -93,7 +93,7 @@ public final class ProtocolDefinition {
|
|||||||
public static final int TICKS_PER_SECOND = 20;
|
public static final int TICKS_PER_SECOND = 20;
|
||||||
public static final int TICK_TIME = 1000 / TICKS_PER_SECOND;
|
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
|
public static final float GRAVITY = -10; // TODO: find the correct value
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer;
|
|||||||
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition;
|
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition;
|
||||||
import de.bixilon.minosoft.util.logging.Log;
|
import de.bixilon.minosoft.util.logging.Log;
|
||||||
import de.bixilon.minosoft.util.microsoft.MicrosoftOAuthUtils;
|
import de.bixilon.minosoft.util.microsoft.MicrosoftOAuthUtils;
|
||||||
|
import de.bixilon.minosoft.util.time.TimeWorker;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
@ -286,6 +287,7 @@ public final class Util {
|
|||||||
public static void initUtilClasses() {
|
public static void initUtilClasses() {
|
||||||
forceClassInit(Log.class);
|
forceClassInit(Log.class);
|
||||||
forceClassInit(MicrosoftOAuthUtils.class);
|
forceClassInit(MicrosoftOAuthUtils.class);
|
||||||
|
forceClassInit(TimeWorker.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Map<String, String> urlQueryToMap(String query) {
|
public static Map<String, String> urlQueryToMap(String query) {
|
||||||
|
30
src/main/java/de/bixilon/minosoft/util/time/TimeWorker.kt
Normal file
30
src/main/java/de/bixilon/minosoft/util/time/TimeWorker.kt
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
package de.bixilon.minosoft.util.time
|
||||||
|
|
||||||
|
data class TimeWorkerTask(
|
||||||
|
val interval: Int,
|
||||||
|
val runnable: Runnable,
|
||||||
|
) {
|
||||||
|
var lastExecution: Long = 0L
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user