From 55628434a93802e184ec7e478b04d31d66ae5561 Mon Sep 17 00:00:00 2001 From: Andrew Lalis Date: Sun, 10 Jul 2022 12:49:51 +0200 Subject: [PATCH] Added basic physics for walls, still highly broken. --- .../nl/andrewl/aos_core/model/WorldIO.java | 8 + .../nl/andrewl/aos_core/model/Worlds.java | 6 + .../ClientCommunicationHandler.java | 2 +- .../java/nl/andrewl/aos2_server/Server.java | 4 + .../nl/andrewl/aos2_server/ServerPlayer.java | 202 ++++++++++-------- worlds/testingWorld | Bin 0 -> 112444 bytes 6 files changed, 136 insertions(+), 86 deletions(-) create mode 100644 worlds/testingWorld diff --git a/core/src/main/java/nl/andrewl/aos_core/model/WorldIO.java b/core/src/main/java/nl/andrewl/aos_core/model/WorldIO.java index 10b1813..ff2d16f 100644 --- a/core/src/main/java/nl/andrewl/aos_core/model/WorldIO.java +++ b/core/src/main/java/nl/andrewl/aos_core/model/WorldIO.java @@ -14,6 +14,9 @@ public final class WorldIO { var chunks = world.getChunkMap().values(); try (var os = Files.newOutputStream(path)) { var out = new DataOutputStream(os); + for (var v : world.getPalette().toArray()) { + out.writeFloat(v); + } out.writeInt(chunks.size()); for (var chunk : chunks) { out.writeInt(chunk.getPosition().x); @@ -28,6 +31,11 @@ public final class WorldIO { World world = new World(); try (var is = Files.newInputStream(path)) { var in = new DataInputStream(is); + ColorPalette palette = new ColorPalette(); + for (int i = 0; i < ColorPalette.MAX_COLORS; i++) { + palette.setColor((byte) (i + 1), in.readFloat(), in.readFloat(), in.readFloat()); + } + world.setPalette(palette); int chunkCount = in.readInt(); for (int i = 0; i < chunkCount; i++) { Chunk chunk = new Chunk( diff --git a/core/src/main/java/nl/andrewl/aos_core/model/Worlds.java b/core/src/main/java/nl/andrewl/aos_core/model/Worlds.java index 387d64c..76f80ed 100644 --- a/core/src/main/java/nl/andrewl/aos_core/model/Worlds.java +++ b/core/src/main/java/nl/andrewl/aos_core/model/Worlds.java @@ -121,6 +121,12 @@ public final class Worlds { world.setBlockAt(15, 1, 20, (byte) 1); world.setBlockAt(15, 1, 21, (byte) 1); world.setBlockAt(15, 1, 22, (byte) 1); + // Add a small floor area. + for (int x = 17; x < 26; x++) { + for (int z = 17; z < 26; z++) { + world.setBlockAt(x, 3, z, (byte) 1); + } + } return world; } diff --git a/server/src/main/java/nl/andrewl/aos2_server/ClientCommunicationHandler.java b/server/src/main/java/nl/andrewl/aos2_server/ClientCommunicationHandler.java index ae9d2ac..18e1d93 100644 --- a/server/src/main/java/nl/andrewl/aos2_server/ClientCommunicationHandler.java +++ b/server/src/main/java/nl/andrewl/aos2_server/ClientCommunicationHandler.java @@ -78,7 +78,7 @@ public class ClientCommunicationHandler { socket.setSoTimeout(1000); boolean connectionEstablished = false; int attempts = 0; - while (!connectionEstablished && attempts < 100) { + while (!connectionEstablished && attempts < 10) { try { Message msg = Net.read(in); if (msg instanceof ConnectRequestMessage connectMsg) { diff --git a/server/src/main/java/nl/andrewl/aos2_server/Server.java b/server/src/main/java/nl/andrewl/aos2_server/Server.java index 59477af..c53e812 100644 --- a/server/src/main/java/nl/andrewl/aos2_server/Server.java +++ b/server/src/main/java/nl/andrewl/aos2_server/Server.java @@ -1,6 +1,7 @@ package nl.andrewl.aos2_server; 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; @@ -13,6 +14,7 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.net.*; +import java.nio.file.Path; import java.util.concurrent.ForkJoinPool; public class Server implements Runnable { @@ -34,6 +36,8 @@ public class Server implements Runnable { this.playerManager = new PlayerManager(); this.worldUpdater = new WorldUpdater(this, 20); this.world = Worlds.testingWorld(); + WorldIO.write(world, Path.of("worlds", "testingWorld")); +// this.world = WorldIO.read(Path.of("worlds", "testingWorld")); } @Override diff --git a/server/src/main/java/nl/andrewl/aos2_server/ServerPlayer.java b/server/src/main/java/nl/andrewl/aos2_server/ServerPlayer.java index 12475f6..8fa9493 100644 --- a/server/src/main/java/nl/andrewl/aos2_server/ServerPlayer.java +++ b/server/src/main/java/nl/andrewl/aos2_server/ServerPlayer.java @@ -54,7 +54,10 @@ public class ServerPlayer extends Player { if (isGrounded(world)) { tickHorizontalVelocity(); - if (lastInputState.jumping()) velocity.y = JUMP_SPEED; + if (lastInputState.jumping()) { + velocity.y = JUMP_SPEED; + updated = true; + } } else { velocity.y -= GRAVITY * dt; updated = true; @@ -64,7 +67,7 @@ public class ServerPlayer extends Player { if (velocity.lengthSquared() > 0) { Vector3f movement = new Vector3f(velocity).mul(dt); // Check for collisions if we try to move according to what the player wants. - checkBlockCollisions(dt, movement, world); + checkBlockCollisions(movement, world); position.add(movement); updated = true; } @@ -85,7 +88,6 @@ public class ServerPlayer extends Player { acceleration.normalize(); acceleration.rotateAxis(orientation.x, 0, 1, 0); acceleration.mul(MOVEMENT_ACCELERATION); - System.out.println(acceleration); horizontalVelocity.add(acceleration); final float maxSpeed; if (lastInputState.crouching()) { @@ -119,17 +121,24 @@ public class ServerPlayer extends Player { // Player must be flat on the top of a block. if (Math.floor(position.y) != position.y) return false; // Check to see if there's a block under any of the spaces the player is over. - return getHorizontalSpaceOccupied().stream() + return getHorizontalSpaceOccupied(position).stream() .anyMatch(point -> world.getBlockAt(point.x, position.y - 0.1f, point.y) != 0); } - private List getHorizontalSpaceOccupied() { + /** + * Gets the list of all spaces occupied by a player's position, in the + * horizontal XZ plane. This can be between 1 and 4 spaces, depending on + * if the player's position is overlapping with a few blocks. + * @param pos The position. + * @return The list of 2d positions occupied. + */ + private List getHorizontalSpaceOccupied(Vector3f pos) { // Get the list of 2d x,z coordinates that we overlap with. List points = new ArrayList<>(4); // Due to the size of radius, there can only be a max of 4 blocks. - int minX = (int) Math.floor(position.x - RADIUS); - int minZ = (int) Math.floor(position.z - RADIUS); - int maxX = (int) Math.floor(position.x + RADIUS); - int maxZ = (int) Math.floor(position.z + RADIUS); + int minX = (int) Math.floor(pos.x - RADIUS); + int minZ = (int) Math.floor(pos.z - RADIUS); + int maxX = (int) Math.floor(pos.x + RADIUS); + int maxZ = (int) Math.floor(pos.z + RADIUS); for (int x = minX; x <= maxX; x++) { for (int z = minZ; z <= maxZ; z++) { points.add(new Vector2i(x, z)); @@ -138,88 +147,111 @@ public class ServerPlayer extends Player { return points; } - private void checkBlockCollisions(float dt, Vector3f movement, World world) { + private void checkBlockCollisions(Vector3f movement, World world) { final Vector3f nextTickPosition = new Vector3f(position).add(movement); - List horizontalSpaces = getHorizontalSpaceOccupied(); - int minXNextTick = (int) Math.floor(nextTickPosition.x() - RADIUS); - int minZNextTick = (int) Math.floor(nextTickPosition.z() - RADIUS); - int maxXNextTick = (int) Math.floor(nextTickPosition.x() + RADIUS); - int maxZNextTick = (int) Math.floor(nextTickPosition.z() + RADIUS); +// System.out.printf("Pos:\t\t%.3f, %.3f, %.3f%nmov:\t\t%.3f, %.3f, %.3f%nNexttick:\t%.3f, %.3f, %.3f%n", +// position.x, position.y, position.z, +// movement.x, movement.y, movement.z, +// nextTickPosition.x, nextTickPosition.y, nextTickPosition.z +// ); + checkWallCollision(world, nextTickPosition, movement); + checkCeilingCollision(world, nextTickPosition, movement); + checkFloorCollision(world, nextTickPosition, movement); + } - // Check if the player is about to hit a wall. - // -Z - if (nextTickPosition.z < position.z && world.getBlockAt(nextTickPosition) != 0) { - Vector3f normal = new Vector3f(0, 0, 1); - movement.sub(normal.mul(movement.dot(normal))); + private void checkFloorCollision(World world, Vector3f nextTickPosition, Vector3f movement) { + // If the player is moving up or not falling out of their current y level, no point in checking. + if (velocity.y >= 0 || Math.floor(position.y) == Math.floor(nextTickPosition.y)) return; + float dropHeight = Math.abs(movement.y); + int steps = (int) Math.ceil(dropHeight); +// System.out.printf(" dropHeight=%.3f, steps=%d%n", dropHeight, steps); + // Get a vector describing how much we move for each 1 unit Y decreases. + Vector3f stepSize = new Vector3f(movement).div(dropHeight); + Vector3f potentialPosition = new Vector3f(position); + for (int i = 0; i < steps; i++) { + potentialPosition.add(stepSize); +// System.out.printf(" Checking: %.3f, %.3f, %.3f%n", potentialPosition.x, potentialPosition.y, potentialPosition.z); + if (getHorizontalSpaceOccupied(potentialPosition).stream() + .anyMatch(p -> world.getBlockAt(p.x, potentialPosition.y, p.y) != 0)) { +// System.out.println(" Occupied!"); + position.y = Math.ceil(potentialPosition.y); + velocity.y = 0; + movement.y = 0; + updated = true; + return; // Exit before doing any extra work. + } } -// 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; -// } + } - // 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); - boolean playerWillHitCeiling = horizontalSpaces.stream() - .anyMatch(point -> world.getBlockAt(point.x, nextTickHeadY, point.y) != 0); - if (playerWillHitCeiling) { - position.y = Math.floor(nextTickPosition.y()); - if (velocity.y > 0) velocity.y = 0; - updated = true; + private void checkCeilingCollision(World world, Vector3f nextTickPosition, Vector3f movement) { + // If the player is moving down, or not moving out of their current y level, no point in checking. + if (velocity.y <= 0 || Math.floor(position.y) == Math.floor(nextTickPosition.y)) return; + float riseHeight = Math.abs(movement.y); + int steps = (int) Math.ceil(riseHeight); + Vector3f stepSize = new Vector3f(movement).div(riseHeight); + Vector3f potentialPosition = new Vector3f(position); + float playerHeight = lastInputState.crouching() ? HEIGHT_CROUCH : HEIGHT; + for (int i = 0; i < steps; i++) { + potentialPosition.add(stepSize); + if (getHorizontalSpaceOccupied(potentialPosition).stream() + .anyMatch(p -> world.getBlockAt(p.x, potentialPosition.y + playerHeight, p.y) != 0)) { + position.y = Math.floor(potentialPosition.y); + velocity.y = 0; + movement.y = 0; + updated = true; + return; // Exit before doing any extra work. + } } + } - // If the player is in the ground, or will be on the next tick, then move them up to the first valid space. - boolean playerFootInBlock = horizontalSpaces.stream() - .anyMatch(point -> world.getBlockAt(point.x, position.y, point.y) != 0 || - world.getBlockAt(point.x, nextTickPosition.y(), point.y) != 0); - if (playerFootInBlock) { - int nextY = (int) Math.floor(nextTickPosition.y()); - while (true) { - int finalNextY = nextY; - boolean isOpen = horizontalSpaces.stream() - .allMatch(point -> { - return world.getBlockAt(point.x, finalNextY, point.y) == 0; - }); - if (isOpen) { - // Move the player to that spot, and cancel out their velocity. - position.y = nextY; - velocity.y = 0; - movement.y = 0; // Cancel out y movement for this tick. - updated = true; - break; - } - nextY++; + private void checkWallCollision(World world, Vector3f nextTickPosition, Vector3f movement) { + // If the player isn't moving horizontally, no point in checking. + if (velocity.x == 0 && velocity.z == 0) return; + Vector3f potentialPosition = new Vector3f(position); + Vector3f stepSize = new Vector3f(movement).normalize(); // Step by 1 meter each time. This will guarantee we check everything, no matter what. + int steps = (int) Math.ceil(movement.length()); + for (int i = 0; i < steps; i++) { + potentialPosition.add(stepSize); + float x = potentialPosition.x; + float y = potentialPosition.y + 1f; + float z = potentialPosition.z; + + float borderMinZ = z - RADIUS; + float borderMaxZ = z + RADIUS; + float borderMinX = x - RADIUS; + float borderMaxX = x + RADIUS; + + // -Z + if (world.getBlockAt(x, y, borderMinZ) != 0) { + System.out.println("-z"); + position.z = Math.ceil(borderMinZ) + RADIUS; + velocity.z = 0; + movement.z = 0; + updated = true; + } + // +Z + if (world.getBlockAt(x, y, borderMaxZ) != 0) { + System.out.println("+z"); + position.z = Math.floor(borderMaxZ) - RADIUS; + velocity.z = 0; + movement.z = 0; + updated = true; + } + // -X + if (world.getBlockAt(borderMinX, y, z) != 0) { + System.out.println("-x"); + position.x = Math.ceil(borderMinX) + RADIUS; + velocity.x = 0; + movement.z = 0; + updated = true; + } + // +X + if (world.getBlockAt(borderMaxX, y, z) != 0) { + System.out.println("+x"); + position.x = Math.floor(borderMaxX) - RADIUS; + velocity.x = 0; + movement.x = 0; + updated = true; } } } diff --git a/worlds/testingWorld b/worlds/testingWorld new file mode 100644 index 0000000000000000000000000000000000000000..83ddd361729ef5ac0b1c2a803caf572f67d9e605 GIT binary patch literal 112444 zcmeI*(Q(^G6a`Rm6idSvk`$0Cq6SiwTSx`z#1$l^JY=30%?pckz zT)~w2&Ru}g4gkOW_|so6-~aaR=fD5__2uouLw!CBZTs5TJ~bYnH*SBa@Bhp1e~jP5 zV*&&S5cppNe)#9!F8})Jbk6=bhqvtXPvPmG!}osq+yAo<0rHoB@*K!c-}}G(llOi< z|7|M}+j79xr_F@tr@5sa3!ayP1FO8JG_W!CkcmKatzAXXNxdCO8 z+NaHhwcga+tM1h!ZCnnieOlgYy{WlZ-K$61xExaZw7l1PQ**DnSC6!DIi&V!d9U@R z=3aHL9%+< zPfL8RSN^j{+9*B$`acA!-_8%v2oNAZfB*pk1PBlyK!5;&z5><&`ev�t5&UAV7cs z0RjXF5FoH8U`=38$2I~42oNAZfB*pk1PBly&_`fc1K9p=L5%bPq1yOaqHhy>yQqMSc|60B4cJeqZ<8_Qq*H-VZ#$L?R7 z@9TE+-~8`1|Bt=*di4Yd5Fqfi0{8m_oZK(qYXv=z009C72oNAZfB*pk1PB~Upw7T>*{@nZ@?=wGj|9`yC{L}-0{BJW~{;5ZR?EGuqXQ!Y0{x5&~|27A< zb%4vd#dceyk8X{0l=Td+lK=q%1PBlyK!5-N0t5&U_+kO;0AEbz2?PibAV7cs0RjXF z5FkL{umaa}0`BJr9JUBAoB#m=1PBlyK!5-N0t5&U_-_JruYm1ur(G}fX1PMTuA52e z-za>Ao5%f8dGeS4I4Ne0{v+@IWBPgbi;=(l$D`b=(SPLqe@s8`ei3W_ z+ZsTv2h1}4N8c^2|K7h}VgJPUE8N%Vzja@y|JMEq?^oDA;r$Btb^34J*Xh5tf5Q6} z_D^`f!hN0oTlaPPZ|$G(eue!L-mh?9r~lS%E#T7JJMiY