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.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.
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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++) {
|
||||||
|
|
|
@ -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.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;
|
||||||
|
|
|
@ -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;
|
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
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue