Added testing world and WorldInfoMessage
This commit is contained in:
parent
dd97f43c9a
commit
45834af62d
|
@ -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.
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
package nl.andrewl.aos_core.model;
|
||||
|
||||
public interface Iterator3D {
|
||||
void doAction(int x, int y, int z);
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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 {}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue