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.control.PlayerViewCursorCallback;
import nl.andrewl.aos2_client.render.GameRenderer; import nl.andrewl.aos2_client.render.GameRenderer;
import nl.andrewl.aos_core.model.Chunk; 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.model.World;
import nl.andrewl.aos_core.net.ChunkDataMessage; 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.aos_core.net.udp.PlayerUpdateMessage;
import nl.andrewl.record_net.Message; import nl.andrewl.record_net.Message;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -74,6 +76,9 @@ public class Client implements Runnable {
} }
public void onMessageReceived(Message msg) { public void onMessageReceived(Message msg) {
if (msg instanceof WorldInfoMessage worldInfo) {
world.setPalette(ColorPalette.fromArray(worldInfo.palette()));
}
if (msg instanceof ChunkDataMessage chunkDataMessage) { if (msg instanceof ChunkDataMessage chunkDataMessage) {
Chunk chunk = chunkDataMessage.toChunk(); Chunk chunk = chunkDataMessage.toChunk();
world.addChunk(chunk); world.addChunk(chunk);
@ -81,7 +86,8 @@ public class Client implements Runnable {
} }
if (msg instanceof PlayerUpdateMessage playerUpdate) { if (msg instanceof PlayerUpdateMessage playerUpdate) {
if (playerUpdate.clientId() == clientId) { 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()); gameRenderer.getCamera().setVelocity(playerUpdate.vx(), playerUpdate.vy(), playerUpdate.vz());
// TODO: Unload far away chunks and request close chunks we don't have. // 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(10, PlayerUpdateMessage.class);
serializer.registerType(11, PlayerJoinMessage.class); serializer.registerType(11, PlayerJoinMessage.class);
serializer.registerType(12, PlayerLeaveMessage.class); serializer.registerType(12, PlayerLeaveMessage.class);
serializer.registerType(13, WorldInfoMessage.class);
} }
public static ExtendedDataInputStream getInputStream(InputStream in) { public static ExtendedDataInputStream getInputStream(InputStream in) {

View File

@ -28,6 +28,24 @@ public class ColorPalette {
colors[value - 1].set(r, g, b); 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() { public static ColorPalette grayscale() {
ColorPalette palette = new ColorPalette(); ColorPalette palette = new ColorPalette();
for (int i = 0; i < MAX_COLORS; i++) { 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.Vector3i;
import org.joml.Vector3ic; import org.joml.Vector3ic;
import java.util.HashMap; import java.util.*;
import java.util.Map;
/** /**
* A world is just a collection of chunks that together form the environment * A world is just a collection of chunks that together form the environment
@ -14,7 +13,20 @@ import java.util.Map;
*/ */
public class World { public class World {
protected final Map<Vector3ic, Chunk> chunkMap = new HashMap<>(); 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) { public void addChunk(Chunk chunk) {
chunkMap.put(chunk.getPosition(), chunk); chunkMap.put(chunk.getPosition(), chunk);
@ -32,6 +44,10 @@ public class World {
return palette; return palette;
} }
public void setPalette(ColorPalette palette) {
this.palette = palette;
}
public byte getBlockAt(Vector3f pos) { public byte getBlockAt(Vector3f pos) {
return getBlockAt(pos, new Vector3i()); return getBlockAt(pos, new Vector3i());
} }
@ -62,6 +78,10 @@ public class World {
chunk.setBlockAt(blockPos.x, blockPos.y, blockPos.z, block); 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) { // public byte getBlockAt(int x, int y, int z) {
//// int chunkX = x / Chunk.SIZE; //// int chunkX = x / Chunk.SIZE;
//// int localX = 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; package nl.andrewl.aos_core.net.udp;
import nl.andrewl.aos_core.model.Player;
import nl.andrewl.record_net.Message; import nl.andrewl.record_net.Message;
/** /**
@ -11,14 +10,6 @@ public record PlayerUpdateMessage(
int clientId, int clientId,
float px, float py, float pz, float px, float py, float pz,
float vx, float vy, float vz, float vx, float vy, float vz,
float ox, float oy float ox, float oy,
) implements Message { boolean crouching
public PlayerUpdateMessage(Player player) { ) implements Message {}
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
);
}
}

View File

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

View File

@ -31,8 +31,14 @@ public class PlayerManager {
clientHandlers.put(player.getId(), handler); clientHandlers.put(player.getId(), handler);
log.info("Registered player \"{}\" with id {}", player.getUsername(), player.getId()); log.info("Registered player \"{}\" with id {}", player.getUsername(), player.getId());
player.setPosition(new Vector3f(0, 64, 0)); player.setPosition(new Vector3f(0, 64, 0));
broadcastTcpMessage(new PlayerJoinMessage(player)); broadcastTcpMessageToAllBut(new PlayerJoinMessage(player), player);
broadcastUdpMessage(new PlayerUpdateMessage(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; 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) { public void broadcastUdpMessage(Message msg) {
try { try {
byte[] data = Net.write(msg); byte[] data = Net.write(msg);

View File

@ -1,22 +1,18 @@
package nl.andrewl.aos2_server; 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.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.UdpReceiver;
import nl.andrewl.aos_core.net.udp.ClientInputState; import nl.andrewl.aos_core.net.udp.ClientInputState;
import nl.andrewl.aos_core.net.udp.ClientOrientationState; import nl.andrewl.aos_core.net.udp.ClientOrientationState;
import nl.andrewl.aos_core.net.udp.DatagramInit; import nl.andrewl.aos_core.net.udp.DatagramInit;
import nl.andrewl.aos_core.net.udp.PlayerUpdateMessage; import nl.andrewl.aos_core.net.udp.PlayerUpdateMessage;
import nl.andrewl.record_net.Message; import nl.andrewl.record_net.Message;
import org.joml.Vector3f;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.IOException; import java.io.IOException;
import java.net.*; import java.net.*;
import java.nio.file.Path;
import java.util.Random;
import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinPool;
public class Server implements Runnable { public class Server implements Runnable {
@ -31,65 +27,13 @@ public class Server implements Runnable {
private final WorldUpdater worldUpdater; private final WorldUpdater worldUpdater;
public Server() throws IOException { public Server() throws IOException {
this.serverSocket = new ServerSocket(24464, 5); this.serverSocket = new ServerSocket(25565, 5);
this.serverSocket.setReuseAddress(true); this.serverSocket.setReuseAddress(true);
this.datagramSocket = new DatagramSocket(24464); this.datagramSocket = new DatagramSocket(25565);
this.datagramSocket.setReuseAddress(true); this.datagramSocket.setReuseAddress(true);
this.playerManager = new PlayerManager(); this.playerManager = new PlayerManager();
this.worldUpdater = new WorldUpdater(this, 20); this.worldUpdater = new WorldUpdater(this, 20);
this.world = Worlds.testingWorld();
// 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"));
} }
@Override @Override
@ -118,12 +62,25 @@ public class Server implements Runnable {
ServerPlayer player = playerManager.getPlayer(inputState.clientId()); ServerPlayer player = playerManager.getPlayer(inputState.clientId());
if (player != null) { if (player != null) {
player.setLastInputState(inputState); 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) { } else if (msg instanceof ClientOrientationState orientationState) {
ServerPlayer player = playerManager.getPlayer(orientationState.clientId()); ServerPlayer player = playerManager.getPlayer(orientationState.clientId());
if (player != null) { if (player != null) {
player.setOrientation(orientationState.x(), orientationState.y()); 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) { 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. updated = false; // Reset the updated flag. This will be set to true if the player was updated in this tick.
checkBlockCollisions(dt, world); checkBlockCollisions(dt, world);
if (isGrounded(world)) { if (isGrounded(world)) {
// System.out.println("g");
tickHorizontalVelocity(); tickHorizontalVelocity();
if (lastInputState.jumping()) velocity.y = JUMP_SPEED; if (lastInputState.jumping()) velocity.y = JUMP_SPEED;
} else { } else {
@ -71,8 +69,6 @@ public class ServerPlayer extends Player {
position.add(scaledVelocity); position.add(scaledVelocity);
updated = true; updated = true;
} }
// System.out.printf("pos: [%.3f, %.3f, %.3f]%n", position.x, position.y, position.z);
} }
private void tickHorizontalVelocity() { private void tickHorizontalVelocity() {
@ -152,45 +148,42 @@ public class ServerPlayer extends Player {
// Check if the player is about to hit a wall. // Check if the player is about to hit a wall.
// -Z // -Z
if ( if (world.getBlockAt(nextTickPosition.x(), nextTickPosition.y() + 1, minZNextTick) != 0) {
world.getBlockAt(nextTickPosition.x(), nextTickPosition.y(), minZNextTick) != 0 &&
world.getBlockAt(nextTickPosition.x(), nextTickPosition.y() + 1, minZNextTick) != 0
) {
System.out.println("wall -z"); System.out.println("wall -z");
position.z = ((float) minZNextTick) + RADIUS + 0.001f; position.z = ((float) minZNextTick) + RADIUS + 0.001f;
velocity.z = 0; velocity.z = 0;
updated = true; updated = true;
} }
// +Z // +Z
if ( // if (
world.getBlockAt(nextTickPosition.x(), nextTickPosition.y(), maxZNextTick) != 0 && // world.getBlockAt(nextTickPosition.x(), nextTickPosition.y(), maxZNextTick) != 0 &&
world.getBlockAt(nextTickPosition.x(), nextTickPosition.y() + 1, maxZNextTick) != 0 // world.getBlockAt(nextTickPosition.x(), nextTickPosition.y() + 1, maxZNextTick) != 0
) { // ) {
System.out.println("wall +z"); // System.out.println("wall +z");
position.z = ((float) maxZNextTick) - RADIUS - 0.001f; // position.z = ((float) maxZNextTick) - RADIUS - 0.001f;
velocity.z = 0; // velocity.z = 0;
updated = true; // updated = true;
} // }
// -X // // -X
if ( // if (
world.getBlockAt(minXNextTick, nextTickPosition.y(), nextTickPosition.z()) != 0 && // world.getBlockAt(minXNextTick, nextTickPosition.y(), nextTickPosition.z()) != 0 &&
world.getBlockAt(minXNextTick, nextTickPosition.y() + 1, nextTickPosition.z()) != 0 // world.getBlockAt(minXNextTick, nextTickPosition.y() + 1, nextTickPosition.z()) != 0
) { // ) {
System.out.println("wall -x"); // System.out.println("wall -x");
position.x = ((float) minXNextTick) + RADIUS + 0.001f; // position.x = ((float) minXNextTick) + RADIUS + 0.001f;
velocity.x = 0; // velocity.x = 0;
updated = true; // updated = true;
} // }
// +X // // +X
if ( // if (
world.getBlockAt(maxXNextTick, nextTickPosition.y(), nextTickPosition.z()) != 0 && // world.getBlockAt(maxXNextTick, nextTickPosition.y(), nextTickPosition.z()) != 0 &&
world.getBlockAt(maxXNextTick, nextTickPosition.y() + 1, nextTickPosition.z()) != 0 // world.getBlockAt(maxXNextTick, nextTickPosition.y() + 1, nextTickPosition.z()) != 0
) { // ) {
System.out.println("wall +x"); // System.out.println("wall +x");
position.x = ((float) maxXNextTick) - RADIUS - 0.001f; // position.x = ((float) maxXNextTick) - RADIUS - 0.001f;
velocity.x = 0; // velocity.x = 0;
updated = true; // updated = true;
} // }
// Check if the player is going to hit a ceiling on the next tick, and cancel velocity and set position. // 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); final float nextTickHeadY = nextTickPosition.y() + (lastInputState.crouching() ? HEIGHT_CROUCH : HEIGHT);

View File

@ -57,100 +57,13 @@ public class WorldUpdater implements Runnable {
private void tick() { private void tick() {
for (var player : server.getPlayerManager().getPlayers()) { for (var player : server.getPlayerManager().getPlayers()) {
player.tick(secondsPerTick, server.getWorld()); player.tick(secondsPerTick, server.getWorld());
if (player.isUpdated()) 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,
private void updatePlayerMovement(ServerPlayer player) { player.getOrientation().x, player.getOrientation().y,
boolean updated = false; player.getLastInputState().crouching()
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));
} }
} }
} }