Added testing world and WorldInfoMessage

This commit is contained in:
Andrew Lalis 2022-07-09 20:09:57 +02:00
parent dd97f43c9a
commit 45834af62d
13 changed files with 272 additions and 210 deletions

View File

@ -4,8 +4,10 @@ import nl.andrewl.aos2_client.control.PlayerInputKeyCallback;
import nl.andrewl.aos2_client.control.PlayerViewCursorCallback;
import nl.andrewl.aos2_client.render.GameRenderer;
import nl.andrewl.aos_core.model.Chunk;
import nl.andrewl.aos_core.model.ColorPalette;
import nl.andrewl.aos_core.model.World;
import nl.andrewl.aos_core.net.ChunkDataMessage;
import nl.andrewl.aos_core.net.WorldInfoMessage;
import nl.andrewl.aos_core.net.udp.PlayerUpdateMessage;
import nl.andrewl.record_net.Message;
import org.slf4j.Logger;
@ -74,6 +76,9 @@ public class Client implements Runnable {
}
public void onMessageReceived(Message msg) {
if (msg instanceof WorldInfoMessage worldInfo) {
world.setPalette(ColorPalette.fromArray(worldInfo.palette()));
}
if (msg instanceof ChunkDataMessage chunkDataMessage) {
Chunk chunk = chunkDataMessage.toChunk();
world.addChunk(chunk);
@ -81,7 +86,8 @@ public class Client implements Runnable {
}
if (msg instanceof PlayerUpdateMessage playerUpdate) {
if (playerUpdate.clientId() == clientId) {
gameRenderer.getCamera().setPosition(playerUpdate.px(), playerUpdate.py() + 1.8f, playerUpdate.pz());
float eyeHeight = playerUpdate.crouching() ? 1.1f : 1.7f;
gameRenderer.getCamera().setPosition(playerUpdate.px(), playerUpdate.py() + eyeHeight, playerUpdate.pz());
gameRenderer.getCamera().setVelocity(playerUpdate.vx(), playerUpdate.vy(), playerUpdate.vz());
// TODO: Unload far away chunks and request close chunks we don't have.
}

View File

@ -32,6 +32,7 @@ public final class Net {
serializer.registerType(10, PlayerUpdateMessage.class);
serializer.registerType(11, PlayerJoinMessage.class);
serializer.registerType(12, PlayerLeaveMessage.class);
serializer.registerType(13, WorldInfoMessage.class);
}
public static ExtendedDataInputStream getInputStream(InputStream in) {

View File

@ -28,6 +28,24 @@ public class ColorPalette {
colors[value - 1].set(r, g, b);
}
public float[] toArray() {
float[] array = new float[3 * MAX_COLORS];
for (int i = 0; i < MAX_COLORS; i++) {
array[i * 3] = colors[i].x;
array[i * 3 + 1] = colors[i].y;
array[i * 3 + 2] = colors[i].z;
}
return array;
}
public static ColorPalette fromArray(float[] array) {
ColorPalette palette = new ColorPalette();
for (int i = 0; i < array.length / 3; i++) {
palette.colors[i].set(array[i * 3], array[i * 3 + 1], array[i * 3 + 2]);
}
return palette;
}
public static ColorPalette grayscale() {
ColorPalette palette = new ColorPalette();
for (int i = 0; i < MAX_COLORS; i++) {

View File

@ -0,0 +1,5 @@
package nl.andrewl.aos_core.model;
public interface Iterator3D {
void doAction(int x, int y, int z);
}

View File

@ -5,8 +5,7 @@ import org.joml.Vector3f;
import org.joml.Vector3i;
import org.joml.Vector3ic;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
/**
* A world is just a collection of chunks that together form the environment
@ -14,7 +13,20 @@ import java.util.Map;
*/
public class World {
protected final Map<Vector3ic, Chunk> chunkMap = new HashMap<>();
protected final ColorPalette palette = ColorPalette.rainbow();
protected ColorPalette palette;
public World(ColorPalette palette, Collection<Chunk> chunks) {
this.palette = palette;
for (var chunk : chunks) addChunk(chunk);
}
public World(ColorPalette palette) {
this(palette, Collections.emptyList());
}
public World() {
this(ColorPalette.rainbow());
}
public void addChunk(Chunk chunk) {
chunkMap.put(chunk.getPosition(), chunk);
@ -32,6 +44,10 @@ public class World {
return palette;
}
public void setPalette(ColorPalette palette) {
this.palette = palette;
}
public byte getBlockAt(Vector3f pos) {
return getBlockAt(pos, new Vector3i());
}
@ -62,6 +78,10 @@ public class World {
chunk.setBlockAt(blockPos.x, blockPos.y, blockPos.z, block);
}
public void setBlockAt(int x, int y, int z, byte block) {
setBlockAt(new Vector3f(x, y, z), block);
}
// public byte getBlockAt(int x, int y, int z) {
//// int chunkX = x / Chunk.SIZE;
//// int localX = x % Chunk.SIZE;

View File

@ -0,0 +1,127 @@
package nl.andrewl.aos_core.model;
import org.joml.Vector3i;
import java.util.Collections;
/**
* Simple container for a bunch of static methods for creating pre-made worlds
* for when you don't want to spend time on that yourself.
*/
public final class Worlds {
private Worlds() {}
/**
* A world consisting of a single chunk, with blocks from y = 0 to y = 8.
* @return The world.
*/
public static World smallCube() {
Chunk chunk = new Chunk(0, 0, 0);
for (int x = 0; x < Chunk.SIZE; x++) {
for (int z = 0; z < Chunk.SIZE; z++) {
for (int y = 0; y < Chunk.SIZE / 2; y++) {
chunk.setBlockAt(x, y, z, (byte) 40);
}
}
}
return new World(ColorPalette.rainbow(), Collections.singleton(chunk));
}
/**
* A 3x3 chunk world consisting of various structures and areas that are
* ideal for testing the game.
* @return The world.
*/
public static World testingWorld() {
ColorPalette palette = new ColorPalette();
palette.setColor((byte) 1, 0.610f, 0.604f, 0.494f);// light brown
palette.setColor((byte) 9, 0.610f, 0.604f, 0.2f);// light brown
palette.setColor((byte) 2, 1, 1, 1);// white
palette.setColor((byte) 3, 0, 0, 0);// black
palette.setColor((byte) 4, 1, 0, 0);// red
palette.setColor((byte) 5, 0, 1, 0);// green
palette.setColor((byte) 6, 0, 0, 1);// blue
palette.setColor((byte) 7, 1, 1, 0);// yellow
palette.setColor((byte) 8, 1, 0, 0);// magenta
World world = new World(palette);
for (int x = -1; x < 2; x++) {
for (int z = -1; z < 2; z++) {
for (int y = -1; y < 2; y++) {
world.addChunk(new Chunk(x, y, z));
}
}
}
Vector3i min = new Vector3i(-1 * Chunk.SIZE);
Vector3i max = new Vector3i(2 * Chunk.SIZE - 1);
int groundLevel = 0;
for (int x = min.x; x <= max.x; x++) {
for (int z = min.z; z <= max.z; z++) {
for (int y = min.y; y < groundLevel; y++) {
byte color;
if (x % 2 == 0 && z % 2 == 0) {
color = 1;
} else {
color = 9;
}
world.setBlockAt(x, y, z, color);
}
}
}
// -Z axis
for (int z = min.z; z < 0; z++) {
world.setBlockAt(0, -1, z, (byte) 4);
}
// +Z axis
for (int z = 0; z <= max.z; z++) {
world.setBlockAt(0, -1, z, (byte) 6);
}
// -X axis
for (int x = min.x; x < 0; x++) {
world.setBlockAt(x, -1, 0, (byte) 5);
}
// +X axis
for (int x = 0; x <= max.x; x++) {
world.setBlockAt(x, -1, 0, (byte) 7);
}
// Draw a '+' in the + side of the world.
world.setBlockAt(10, -1, 10, (byte) 3);
world.setBlockAt(11, -1, 10, (byte) 3);
world.setBlockAt(12, -1, 10, (byte) 3);
world.setBlockAt(9, -1, 10, (byte) 3);
world.setBlockAt(8, -1, 10, (byte) 3);
world.setBlockAt(10, -1, 9, (byte) 3);
world.setBlockAt(10, -1, 8, (byte) 3);
world.setBlockAt(10, -1, 11, (byte) 3);
world.setBlockAt(10, -1, 12, (byte) 3);
// Draw a '-' in the - side of the world.
world.setBlockAt(-7, -1, -8, (byte) 3);
world.setBlockAt(-8, -1, -8, (byte) 3);
world.setBlockAt(-9, -1, -8, (byte) 3);
world.setBlockAt(-10, -1, -8, (byte) 3);
world.setBlockAt(-11, -1, -8, (byte) 3);
// Draw a '+' shaped wall.
for (int x = 16; x < 26; x++) {
world.setBlockAt(x, 0, 16, (byte) 1);
world.setBlockAt(x, 1, 16, (byte) 1);
world.setBlockAt(x, 2, 16, (byte) 1);
}
for (int z = 16; z < 26; z++) {
world.setBlockAt(16, 0, z, (byte) 1);
world.setBlockAt(16, 1, z, (byte) 1);
world.setBlockAt(16, 2, z, (byte) 1);
}
// Add a small staircase.
world.setBlockAt(14, 0, 20, (byte) 1);
world.setBlockAt(14, 0, 21, (byte) 1);
world.setBlockAt(14, 0, 22, (byte) 1);
world.setBlockAt(15, 1, 20, (byte) 1);
world.setBlockAt(15, 1, 21, (byte) 1);
world.setBlockAt(15, 1, 22, (byte) 1);
return world;
}
}

View File

@ -0,0 +1,16 @@
package nl.andrewl.aos_core.net;
import nl.andrewl.aos_core.model.World;
import nl.andrewl.record_net.Message;
/**
* Message that the server sends to connecting clients with some metadata about
* the world.
*/
public record WorldInfoMessage(
float[] palette
) implements Message {
public WorldInfoMessage(World world) {
this(world.getPalette().toArray());
}
}

View File

@ -1,6 +1,5 @@
package nl.andrewl.aos_core.net.udp;
import nl.andrewl.aos_core.model.Player;
import nl.andrewl.record_net.Message;
/**
@ -11,14 +10,6 @@ public record PlayerUpdateMessage(
int clientId,
float px, float py, float pz,
float vx, float vy, float vz,
float ox, float oy
) implements Message {
public PlayerUpdateMessage(Player player) {
this(
player.getId(),
player.getPosition().x, player.getPosition().y, player.getPosition().z,
player.getVelocity().x, player.getVelocity().y, player.getVelocity().z,
player.getOrientation().x, player.getOrientation().y
);
}
}
float ox, float oy,
boolean crouching
) implements Message {}

View File

@ -90,6 +90,7 @@ public class ClientCommunicationHandler {
Net.write(new ConnectAcceptMessage(player.getId()), out);
log.debug("Sent connect accept message.");
sendTcpMessage(new WorldInfoMessage(server.getWorld()));
for (var chunk : server.getWorld().getChunkMap().values()) {
sendTcpMessage(new ChunkDataMessage(chunk));
}

View File

@ -31,8 +31,14 @@ public class PlayerManager {
clientHandlers.put(player.getId(), handler);
log.info("Registered player \"{}\" with id {}", player.getUsername(), player.getId());
player.setPosition(new Vector3f(0, 64, 0));
broadcastTcpMessage(new PlayerJoinMessage(player));
broadcastUdpMessage(new PlayerUpdateMessage(player));
broadcastTcpMessageToAllBut(new PlayerJoinMessage(player), player);
broadcastUdpMessage(new PlayerUpdateMessage(
player.getId(),
player.getPosition().x, player.getPosition().y, player.getPosition().z,
player.getVelocity().x, player.getVelocity().y, player.getVelocity().z,
player.getOrientation().x, player.getOrientation().y,
player.getLastInputState().crouching()
));
return player;
}
@ -83,6 +89,14 @@ public class PlayerManager {
}
}
public void broadcastTcpMessageToAllBut(Message msg, ServerPlayer player) {
for (var entry : clientHandlers.entrySet()) {
if (entry.getKey() != player.getId()) {
entry.getValue().sendTcpMessage(msg);
}
}
}
public void broadcastUdpMessage(Message msg) {
try {
byte[] data = Net.write(msg);

View File

@ -1,22 +1,18 @@
package nl.andrewl.aos2_server;
import nl.andrewl.aos_core.model.Chunk;
import nl.andrewl.aos_core.model.World;
import nl.andrewl.aos_core.model.WorldIO;
import nl.andrewl.aos_core.model.Worlds;
import nl.andrewl.aos_core.net.UdpReceiver;
import nl.andrewl.aos_core.net.udp.ClientInputState;
import nl.andrewl.aos_core.net.udp.ClientOrientationState;
import nl.andrewl.aos_core.net.udp.DatagramInit;
import nl.andrewl.aos_core.net.udp.PlayerUpdateMessage;
import nl.andrewl.record_net.Message;
import org.joml.Vector3f;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.*;
import java.nio.file.Path;
import java.util.Random;
import java.util.concurrent.ForkJoinPool;
public class Server implements Runnable {
@ -31,65 +27,13 @@ public class Server implements Runnable {
private final WorldUpdater worldUpdater;
public Server() throws IOException {
this.serverSocket = new ServerSocket(24464, 5);
this.serverSocket = new ServerSocket(25565, 5);
this.serverSocket.setReuseAddress(true);
this.datagramSocket = new DatagramSocket(24464);
this.datagramSocket = new DatagramSocket(25565);
this.datagramSocket.setReuseAddress(true);
this.playerManager = new PlayerManager();
this.worldUpdater = new WorldUpdater(this, 20);
// Generate world. TODO: do this elsewhere.
Random rand = new Random(1);
this.world = new World();
for (int x = -5; x <= 5; x++) {
for (int y = 0; y <= 5; y++) {
for (int z = -3; z <= 3; z++) {
Chunk chunk = new Chunk(x, y, z);
if (y <= 3) {
for (int i = 0; i < Chunk.TOTAL_SIZE; i++) {
chunk.getBlocks()[i] = (byte) rand.nextInt(20, 40);
}
}
world.addChunk(chunk);
}
}
}
world.setBlockAt(new Vector3f(5, 64, 5), (byte) 50);
world.setBlockAt(new Vector3f(5, 64, 6), (byte) 50);
world.setBlockAt(new Vector3f(5, 64, 7), (byte) 50);
world.setBlockAt(new Vector3f(5, 65, 6), (byte) 50);
world.setBlockAt(new Vector3f(5, 66, 7), (byte) 50);
world.setBlockAt(new Vector3f(5, 65, 7), (byte) 50);
world.setBlockAt(new Vector3f(5, 67, 8), (byte) 50);
world.setBlockAt(new Vector3f(6, 67, 8), (byte) 50);
world.setBlockAt(new Vector3f(7, 67, 8), (byte) 50);
world.setBlockAt(new Vector3f(5, 67, 9), (byte) 50);
world.setBlockAt(new Vector3f(6, 67, 9), (byte) 50);
world.setBlockAt(new Vector3f(7, 67, 9), (byte) 50);
for (int z = 0; z > -20; z--) {
world.setBlockAt(new Vector3f(0, 63, z), (byte) 120);
}
for (int x = 0; x < 10; x++) {
world.setBlockAt(new Vector3f(x - 5, 64, 3), (byte) 80);
world.setBlockAt(new Vector3f(x - 5, 65, 3), (byte) 80);
world.setBlockAt(new Vector3f(x - 5, 66, 3), (byte) 80);
}
for (int z = 0; z < 10; z++) {
world.setBlockAt(new Vector3f(20, 64, z), (byte) 80);
world.setBlockAt(new Vector3f(20, 65, z), (byte) 80);
world.setBlockAt(new Vector3f(20, 66, z), (byte) 80);
}
world.setBlockAt(new Vector3f(21, 64, 6), (byte) 1);
for (int x = 0; x < 127; x++) {
world.setBlockAt(new Vector3f(x - 50, 63, -15), (byte) x);
}
WorldIO.write(world, Path.of("testworld"));
// this.world = WorldIO.read(Path.of("testworld"));
this.world = Worlds.testingWorld();
}
@Override
@ -118,12 +62,25 @@ public class Server implements Runnable {
ServerPlayer player = playerManager.getPlayer(inputState.clientId());
if (player != null) {
player.setLastInputState(inputState);
playerManager.broadcastUdpMessage(new PlayerUpdateMessage(
player.getId(),
player.getPosition().x, player.getPosition().y, player.getPosition().z,
player.getVelocity().x, player.getVelocity().y, player.getVelocity().z,
player.getOrientation().x, player.getOrientation().y,
player.getLastInputState().crouching()
));
}
} else if (msg instanceof ClientOrientationState orientationState) {
ServerPlayer player = playerManager.getPlayer(orientationState.clientId());
if (player != null) {
player.setOrientation(orientationState.x(), orientationState.y());
playerManager.broadcastUdpMessageToAllBut(new PlayerUpdateMessage(player), player);
playerManager.broadcastUdpMessageToAllBut(new PlayerUpdateMessage(
player.getId(),
player.getPosition().x, player.getPosition().y, player.getPosition().z,
player.getVelocity().x, player.getVelocity().y, player.getVelocity().z,
player.getOrientation().x, player.getOrientation().y,
player.getLastInputState().crouching()
), player);
}
}
}

View File

@ -51,13 +51,11 @@ public class ServerPlayer extends Player {
}
public void tick(float dt, World world) {
// log.info("Ticking player " + id);
updated = false; // Reset the updated flag. This will be set to true if the player was updated in this tick.
checkBlockCollisions(dt, world);
if (isGrounded(world)) {
// System.out.println("g");
tickHorizontalVelocity();
if (lastInputState.jumping()) velocity.y = JUMP_SPEED;
} else {
@ -71,8 +69,6 @@ public class ServerPlayer extends Player {
position.add(scaledVelocity);
updated = true;
}
// System.out.printf("pos: [%.3f, %.3f, %.3f]%n", position.x, position.y, position.z);
}
private void tickHorizontalVelocity() {
@ -152,45 +148,42 @@ public class ServerPlayer extends Player {
// Check if the player is about to hit a wall.
// -Z
if (
world.getBlockAt(nextTickPosition.x(), nextTickPosition.y(), minZNextTick) != 0 &&
world.getBlockAt(nextTickPosition.x(), nextTickPosition.y() + 1, minZNextTick) != 0
) {
if (world.getBlockAt(nextTickPosition.x(), nextTickPosition.y() + 1, minZNextTick) != 0) {
System.out.println("wall -z");
position.z = ((float) minZNextTick) + RADIUS + 0.001f;
velocity.z = 0;
updated = true;
}
// +Z
if (
world.getBlockAt(nextTickPosition.x(), nextTickPosition.y(), maxZNextTick) != 0 &&
world.getBlockAt(nextTickPosition.x(), nextTickPosition.y() + 1, maxZNextTick) != 0
) {
System.out.println("wall +z");
position.z = ((float) maxZNextTick) - RADIUS - 0.001f;
velocity.z = 0;
updated = true;
}
// -X
if (
world.getBlockAt(minXNextTick, nextTickPosition.y(), nextTickPosition.z()) != 0 &&
world.getBlockAt(minXNextTick, nextTickPosition.y() + 1, nextTickPosition.z()) != 0
) {
System.out.println("wall -x");
position.x = ((float) minXNextTick) + RADIUS + 0.001f;
velocity.x = 0;
updated = true;
}
// +X
if (
world.getBlockAt(maxXNextTick, nextTickPosition.y(), nextTickPosition.z()) != 0 &&
world.getBlockAt(maxXNextTick, nextTickPosition.y() + 1, nextTickPosition.z()) != 0
) {
System.out.println("wall +x");
position.x = ((float) maxXNextTick) - RADIUS - 0.001f;
velocity.x = 0;
updated = true;
}
// if (
// world.getBlockAt(nextTickPosition.x(), nextTickPosition.y(), maxZNextTick) != 0 &&
// world.getBlockAt(nextTickPosition.x(), nextTickPosition.y() + 1, maxZNextTick) != 0
// ) {
// System.out.println("wall +z");
// position.z = ((float) maxZNextTick) - RADIUS - 0.001f;
// velocity.z = 0;
// updated = true;
// }
// // -X
// if (
// world.getBlockAt(minXNextTick, nextTickPosition.y(), nextTickPosition.z()) != 0 &&
// world.getBlockAt(minXNextTick, nextTickPosition.y() + 1, nextTickPosition.z()) != 0
// ) {
// System.out.println("wall -x");
// position.x = ((float) minXNextTick) + RADIUS + 0.001f;
// velocity.x = 0;
// updated = true;
// }
// // +X
// if (
// world.getBlockAt(maxXNextTick, nextTickPosition.y(), nextTickPosition.z()) != 0 &&
// world.getBlockAt(maxXNextTick, nextTickPosition.y() + 1, nextTickPosition.z()) != 0
// ) {
// System.out.println("wall +x");
// position.x = ((float) maxXNextTick) - RADIUS - 0.001f;
// velocity.x = 0;
// updated = true;
// }
// Check if the player is going to hit a ceiling on the next tick, and cancel velocity and set position.
final float nextTickHeadY = nextTickPosition.y() + (lastInputState.crouching() ? HEIGHT_CROUCH : HEIGHT);

View File

@ -57,100 +57,13 @@ public class WorldUpdater implements Runnable {
private void tick() {
for (var player : server.getPlayerManager().getPlayers()) {
player.tick(secondsPerTick, server.getWorld());
if (player.isUpdated()) server.getPlayerManager().broadcastUdpMessage(new PlayerUpdateMessage(player));
}
}
private void updatePlayerMovement(ServerPlayer player) {
boolean updated = false;
var v = player.getVelocity();
var hv = new Vector3f(v.x, 0, v.z);
var p = player.getPosition();
// Check if we have a negative velocity that will cause us to fall through a block next tick.
float nextTickY = p.y + v.y * secondsPerTick;
if (server.getWorld().getBlockAt(new Vector3f(p.x, nextTickY, p.z)) != 0) {
// Find the first block we'll hit and set the player down on that.
int floorY = (int) Math.floor(p.y) - 1;
while (true) {
if (server.getWorld().getBlockAt(new Vector3f(p.x, floorY, p.z)) != 0) {
p.y = floorY + 1f;
v.y = 0;
break;
} else {
floorY--;
}
}
}
// Check if the player is on the ground.
boolean grounded = (Math.floor(p.y) == p.y && server.getWorld().getBlockAt(new Vector3f(p.x, p.y - 0.0001f, p.z)) != 0);
if (!grounded) {
v.y -= 9.81f * secondsPerTick;
}
// Apply horizontal deceleration to the player before computing any input-derived acceleration.
if (grounded && hv.length() > 0) {
Vector3f deceleration = new Vector3f(hv).negate().normalize().mul(Math.min(hv.length(), 2f));
hv.add(deceleration);
if (hv.length() < 0.1f) {
hv.set(0);
}
v.x = hv.x;
v.z = hv.z;
updated = true;
}
Vector3f a = new Vector3f();
var inputState = player.getLastInputState();
if (inputState.jumping() && grounded) {
v.y = 15f;
}
final float horizontalAcceleration = 5;
// Compute horizontal motion separately.
if (grounded) {
if (inputState.forward()) a.z -= 1;
if (inputState.backward()) a.z += 1;
if (inputState.left()) a.x -= 1;
if (inputState.right()) a.x += 1;
// if (inputState.crouching()) a.y -= 1; // TODO: do crouching instead of down.
if (a.lengthSquared() > 0) {
a.normalize();
Matrix4f moveTransform = new Matrix4f();
moveTransform.rotate(player.getOrientation().x, new Vector3f(0, 1, 0));
moveTransform.transformDirection(a);
a.mul(horizontalAcceleration);
hv.add(a);
final float maxSpeed;
if (inputState.crouching()) {
maxSpeed = 2.5f;
} else if (inputState.sprinting()) {
maxSpeed = 10f;
} else {
maxSpeed = 6f;
}
if (hv.length() > maxSpeed) {
hv.normalize(maxSpeed);
}
v.x = hv.x;
v.z = hv.z;
updated = true;
}
}
// Apply velocity to the player's position.
if (v.lengthSquared() > 0) {
Vector3f scaledVelocity = new Vector3f(v);
scaledVelocity.mul(secondsPerTick);
p.add(scaledVelocity);
updated = true;
}
if (updated) {
server.getPlayerManager().broadcastUdpMessage(new PlayerUpdateMessage(player));
if (player.isUpdated()) server.getPlayerManager().broadcastUdpMessage(new PlayerUpdateMessage(
player.getId(),
player.getPosition().x, player.getPosition().y, player.getPosition().z,
player.getVelocity().x, player.getVelocity().y, player.getVelocity().z,
player.getOrientation().x, player.getOrientation().y,
player.getLastInputState().crouching()
));
}
}
}