Added better client state information, more message reorganization.
This commit is contained in:
parent
d83ff8a816
commit
8bd88b849c
|
@ -1,6 +1,7 @@
|
|||
package nl.andrewl.aos2_client;
|
||||
|
||||
import nl.andrewl.aos_core.MathUtils;
|
||||
import nl.andrewl.aos_core.model.Player;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector2f;
|
||||
import org.joml.Vector3f;
|
||||
|
@ -46,6 +47,11 @@ public class Camera {
|
|||
this.viewTransform = new Matrix4f();
|
||||
}
|
||||
|
||||
public void setToPlayer(Player p) {
|
||||
position.set(p.getPosition());
|
||||
velocity.set(p.getVelocity());
|
||||
}
|
||||
|
||||
public Matrix4f getViewTransform() {
|
||||
return viewTransform;
|
||||
}
|
||||
|
|
|
@ -5,12 +5,15 @@ import nl.andrewl.aos2_client.control.InputHandler;
|
|||
import nl.andrewl.aos2_client.control.PlayerInputKeyCallback;
|
||||
import nl.andrewl.aos2_client.control.PlayerInputMouseClickCallback;
|
||||
import nl.andrewl.aos2_client.control.PlayerViewCursorCallback;
|
||||
import nl.andrewl.aos2_client.model.ClientPlayer;
|
||||
import nl.andrewl.aos2_client.render.GameRenderer;
|
||||
import nl.andrewl.aos_core.config.Config;
|
||||
import nl.andrewl.aos_core.model.world.ColorPalette;
|
||||
import nl.andrewl.aos_core.net.*;
|
||||
import nl.andrewl.aos_core.net.udp.ChunkUpdateMessage;
|
||||
import nl.andrewl.aos_core.net.udp.PlayerUpdateMessage;
|
||||
import nl.andrewl.aos_core.net.client.*;
|
||||
import nl.andrewl.aos_core.net.world.ChunkDataMessage;
|
||||
import nl.andrewl.aos_core.net.world.ChunkHashMessage;
|
||||
import nl.andrewl.aos_core.net.world.ChunkUpdateMessage;
|
||||
import nl.andrewl.aos_core.net.world.WorldInfoMessage;
|
||||
import nl.andrewl.record_net.Message;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -28,13 +31,13 @@ public class Client implements Runnable {
|
|||
private final InputHandler inputHandler;
|
||||
private final GameRenderer gameRenderer;
|
||||
|
||||
private int clientId;
|
||||
private final ClientWorld world;
|
||||
private ClientPlayer player;
|
||||
|
||||
public Client(ClientConfig config) {
|
||||
this.config = config;
|
||||
this.communicationHandler = new CommunicationHandler(this);
|
||||
this.inputHandler = new InputHandler(communicationHandler);
|
||||
this.inputHandler = new InputHandler(this, communicationHandler);
|
||||
this.world = new ClientWorld();
|
||||
this.gameRenderer = new GameRenderer(config.display, world);
|
||||
}
|
||||
|
@ -43,17 +46,30 @@ public class Client implements Runnable {
|
|||
return config;
|
||||
}
|
||||
|
||||
public ClientPlayer getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the {@link CommunicationHandler} when a connection is
|
||||
* established, and we need to begin tracking the player's state.
|
||||
* @param player The player.
|
||||
*/
|
||||
public void setPlayer(ClientPlayer player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
this.clientId = communicationHandler.establishConnection();
|
||||
communicationHandler.establishConnection();
|
||||
} catch (IOException e) {
|
||||
log.error("Couldn't connect to the server: {}", e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
gameRenderer.setupWindow(
|
||||
new PlayerViewCursorCallback(config.input, gameRenderer.getCamera(), communicationHandler),
|
||||
new PlayerViewCursorCallback(config.input, this, gameRenderer.getCamera(), communicationHandler),
|
||||
new PlayerInputKeyCallback(inputHandler),
|
||||
new PlayerInputMouseClickCallback(inputHandler)
|
||||
);
|
||||
|
@ -75,30 +91,37 @@ 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) {
|
||||
} else if (msg instanceof ChunkDataMessage chunkDataMessage) {
|
||||
world.addChunk(chunkDataMessage);
|
||||
}
|
||||
if (msg instanceof ChunkUpdateMessage u) {
|
||||
} else if (msg instanceof ChunkUpdateMessage u) {
|
||||
world.updateChunk(u);
|
||||
// If we received an update for a chunk we don't have, request it!
|
||||
if (world.getChunkAt(u.getChunkPos()) == null) {
|
||||
communicationHandler.sendMessage(new ChunkHashMessage(u.cx(), u.cy(), u.cz(), -1));
|
||||
}
|
||||
}
|
||||
if (msg instanceof PlayerUpdateMessage playerUpdate) {
|
||||
if (playerUpdate.clientId() == clientId) {
|
||||
} else if (msg instanceof PlayerUpdateMessage playerUpdate) {
|
||||
if (playerUpdate.clientId() == player.getId()) {
|
||||
player.getPosition().set(playerUpdate.px(), playerUpdate.py(), playerUpdate.pz());
|
||||
player.getVelocity().set(playerUpdate.vx(), playerUpdate.vy(), playerUpdate.vz());
|
||||
gameRenderer.getCamera().setToPlayer(player);
|
||||
// TODO: Add getEyeHeight() and isCrouching() to main Player class.
|
||||
float eyeHeight = playerUpdate.crouching() ? 1.3f : 1.7f;
|
||||
gameRenderer.getCamera().setPosition(playerUpdate.px(), playerUpdate.py() + eyeHeight, playerUpdate.pz());
|
||||
gameRenderer.getCamera().setVelocity(playerUpdate.vx(), playerUpdate.vy(), playerUpdate.vz());
|
||||
gameRenderer.getCamera().getPosition().y += eyeHeight;
|
||||
} else {
|
||||
world.playerUpdated(playerUpdate);
|
||||
}
|
||||
}
|
||||
if (msg instanceof PlayerJoinMessage joinMessage) {
|
||||
} else if (msg instanceof ClientInventoryMessage inventoryMessage) {
|
||||
player.setInventory(inventoryMessage.inv());
|
||||
System.out.println("Got inventory!");
|
||||
} else if (msg instanceof InventorySelectedStackMessage selectedStackMessage) {
|
||||
player.getInventory().setSelectedIndex(selectedStackMessage.index());
|
||||
System.out.println("Selected item stack: " + player.getInventory().getSelectedItemStack().getType().getName());
|
||||
} else if (msg instanceof ItemStackMessage itemStackMessage) {
|
||||
player.getInventory().getItemStacks().set(itemStackMessage.index(), itemStackMessage.stack());
|
||||
System.out.println("Item stack updated: " + itemStackMessage.index());
|
||||
} else if (msg instanceof PlayerJoinMessage joinMessage) {
|
||||
world.playerJoined(joinMessage);
|
||||
}
|
||||
if (msg instanceof PlayerLeaveMessage leaveMessage) {
|
||||
} else if (msg instanceof PlayerLeaveMessage leaveMessage) {
|
||||
world.playerLeft(leaveMessage);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,11 +5,11 @@ import nl.andrewl.aos2_client.render.chunk.ChunkMeshGenerator;
|
|||
import nl.andrewl.aos_core.model.world.Chunk;
|
||||
import nl.andrewl.aos_core.model.Player;
|
||||
import nl.andrewl.aos_core.model.world.World;
|
||||
import nl.andrewl.aos_core.net.ChunkDataMessage;
|
||||
import nl.andrewl.aos_core.net.PlayerJoinMessage;
|
||||
import nl.andrewl.aos_core.net.PlayerLeaveMessage;
|
||||
import nl.andrewl.aos_core.net.udp.ChunkUpdateMessage;
|
||||
import nl.andrewl.aos_core.net.udp.PlayerUpdateMessage;
|
||||
import nl.andrewl.aos_core.net.world.ChunkDataMessage;
|
||||
import nl.andrewl.aos_core.net.client.PlayerJoinMessage;
|
||||
import nl.andrewl.aos_core.net.client.PlayerLeaveMessage;
|
||||
import nl.andrewl.aos_core.net.world.ChunkUpdateMessage;
|
||||
import nl.andrewl.aos_core.net.client.PlayerUpdateMessage;
|
||||
import org.joml.Vector3f;
|
||||
import org.joml.Vector3i;
|
||||
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
package nl.andrewl.aos2_client;
|
||||
|
||||
import nl.andrewl.aos2_client.model.ClientPlayer;
|
||||
import nl.andrewl.aos_core.Net;
|
||||
import nl.andrewl.aos_core.net.*;
|
||||
import nl.andrewl.aos_core.net.udp.DatagramInit;
|
||||
import nl.andrewl.aos_core.net.connect.ConnectAcceptMessage;
|
||||
import nl.andrewl.aos_core.net.connect.ConnectRejectMessage;
|
||||
import nl.andrewl.aos_core.net.connect.ConnectRequestMessage;
|
||||
import nl.andrewl.aos_core.net.connect.DatagramInit;
|
||||
import nl.andrewl.record_net.Message;
|
||||
import nl.andrewl.record_net.util.ExtendedDataInputStream;
|
||||
import nl.andrewl.record_net.util.ExtendedDataOutputStream;
|
||||
|
@ -33,7 +37,7 @@ public class CommunicationHandler {
|
|||
this.client = client;
|
||||
}
|
||||
|
||||
public int establishConnection() throws IOException {
|
||||
public void establishConnection() throws IOException {
|
||||
if (socket != null && !socket.isClosed()) {
|
||||
socket.close();
|
||||
}
|
||||
|
@ -54,11 +58,11 @@ public class CommunicationHandler {
|
|||
}
|
||||
if (response instanceof ConnectAcceptMessage acceptMessage) {
|
||||
this.clientId = acceptMessage.clientId();
|
||||
client.setPlayer(new ClientPlayer(clientId, username));
|
||||
establishDatagramConnection();
|
||||
log.info("Connection to server established. My client id is {}.", clientId);
|
||||
new Thread(new TcpReceiver(in, client::onMessageReceived)).start();
|
||||
new Thread(new UdpReceiver(datagramSocket, (msg, packet) -> client.onMessageReceived(msg))).start();
|
||||
return acceptMessage.clientId();
|
||||
} else {
|
||||
throw new IOException("Server returned an unexpected message: " + response);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ public class ClientConfig {
|
|||
|
||||
public static class DisplayConfig {
|
||||
public boolean fullscreen = false;
|
||||
public boolean captureCursor = true;
|
||||
public boolean captureCursor = false;
|
||||
public float fov = 70;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
package nl.andrewl.aos2_client.control;
|
||||
|
||||
import nl.andrewl.aos2_client.CommunicationHandler;
|
||||
import nl.andrewl.aos_core.net.udp.ClientInputState;
|
||||
import nl.andrewl.aos_core.net.client.ClientInputState;
|
||||
import nl.andrewl.aos2_client.Client;
|
||||
|
||||
import static org.lwjgl.glfw.GLFW.*;
|
||||
|
||||
|
@ -9,16 +10,24 @@ import static org.lwjgl.glfw.GLFW.*;
|
|||
* Class which manages the player's input, and sending it to the server.
|
||||
*/
|
||||
public class InputHandler {
|
||||
private final Client client;
|
||||
private final CommunicationHandler comm;
|
||||
|
||||
private ClientInputState lastInputState = null;
|
||||
|
||||
public InputHandler(CommunicationHandler comm) {
|
||||
public InputHandler(Client client, CommunicationHandler comm) {
|
||||
this.client = client;
|
||||
this.comm = comm;
|
||||
}
|
||||
|
||||
public void updateInputState(long window) {
|
||||
// TODO: Allow customized keybindings.
|
||||
int selectedInventoryIndex;
|
||||
selectedInventoryIndex = client.getPlayer().getInventory().getSelectedIndex();
|
||||
if (glfwGetKey(window, GLFW_KEY_1) == GLFW_PRESS) selectedInventoryIndex = 0;
|
||||
if (glfwGetKey(window, GLFW_KEY_2) == GLFW_PRESS) selectedInventoryIndex = 1;
|
||||
if (glfwGetKey(window, GLFW_KEY_3) == GLFW_PRESS) selectedInventoryIndex = 2;
|
||||
|
||||
ClientInputState currentInputState = new ClientInputState(
|
||||
comm.getClientId(),
|
||||
glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS,
|
||||
|
@ -29,7 +38,8 @@ public class InputHandler {
|
|||
glfwGetKey(window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS,
|
||||
glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS,
|
||||
glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_1) == GLFW_PRESS,
|
||||
glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_2) == GLFW_PRESS
|
||||
glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_2) == GLFW_PRESS,
|
||||
selectedInventoryIndex
|
||||
);
|
||||
if (!currentInputState.equals(lastInputState)) {
|
||||
comm.sendDatagramPacket(currentInputState);
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
package nl.andrewl.aos2_client.control;
|
||||
|
||||
import nl.andrewl.aos2_client.Camera;
|
||||
import nl.andrewl.aos2_client.Client;
|
||||
import nl.andrewl.aos2_client.CommunicationHandler;
|
||||
import nl.andrewl.aos2_client.config.ClientConfig;
|
||||
import nl.andrewl.aos_core.net.udp.ClientOrientationState;
|
||||
import nl.andrewl.aos_core.net.client.ClientOrientationState;
|
||||
import org.lwjgl.glfw.GLFWCursorPosCallbackI;
|
||||
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
|
@ -22,15 +23,17 @@ public class PlayerViewCursorCallback implements GLFWCursorPosCallbackI {
|
|||
private static final int ORIENTATION_UPDATE_LIMIT = 20;
|
||||
|
||||
private final ClientConfig.InputConfig config;
|
||||
private final Client client;
|
||||
private final Camera camera;
|
||||
private final CommunicationHandler comm;
|
||||
private float lastMouseCursorX;
|
||||
private float lastMouseCursorY;
|
||||
private long lastOrientationUpdateSentAt = 0L;
|
||||
|
||||
public PlayerViewCursorCallback(ClientConfig.InputConfig config, Camera camera, CommunicationHandler comm) {
|
||||
public PlayerViewCursorCallback(ClientConfig.InputConfig config, Client client, Camera cam, CommunicationHandler comm) {
|
||||
this.config = config;
|
||||
this.camera = camera;
|
||||
this.client = client;
|
||||
this.camera = cam;
|
||||
this.comm = comm;
|
||||
}
|
||||
|
||||
|
@ -45,13 +48,18 @@ public class PlayerViewCursorCallback implements GLFWCursorPosCallbackI {
|
|||
float dy = y - lastMouseCursorY;
|
||||
lastMouseCursorX = x;
|
||||
lastMouseCursorY = y;
|
||||
camera.setOrientation(
|
||||
camera.getOrientation().x - dx * config.mouseSensitivity,
|
||||
camera.getOrientation().y - dy * config.mouseSensitivity
|
||||
client.getPlayer().setOrientation(
|
||||
client.getPlayer().getOrientation().x - dx * config.mouseSensitivity,
|
||||
client.getPlayer().getOrientation().y - dy * config.mouseSensitivity
|
||||
);
|
||||
camera.setOrientation(client.getPlayer().getOrientation().x, client.getPlayer().getOrientation().y);
|
||||
long now = System.currentTimeMillis();
|
||||
if (lastOrientationUpdateSentAt + ORIENTATION_UPDATE_LIMIT < now) {
|
||||
ForkJoinPool.commonPool().submit(() -> comm.sendDatagramPacket(new ClientOrientationState(comm.getClientId(), camera.getOrientation().x, camera.getOrientation().y)));
|
||||
ForkJoinPool.commonPool().submit(() -> comm.sendDatagramPacket(new ClientOrientationState(
|
||||
client.getPlayer().getId(),
|
||||
client.getPlayer().getOrientation().x,
|
||||
client.getPlayer().getOrientation().y
|
||||
)));
|
||||
lastOrientationUpdateSentAt = now;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package nl.andrewl.aos2_client.model;
|
||||
|
||||
import nl.andrewl.aos_core.model.Player;
|
||||
import nl.andrewl.aos_core.model.item.Inventory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class ClientPlayer extends Player {
|
||||
private final Inventory inventory;
|
||||
|
||||
public ClientPlayer(int id, String username) {
|
||||
super(id, username);
|
||||
this.inventory = new Inventory(new ArrayList<>(), 0);
|
||||
}
|
||||
|
||||
public Inventory getInventory() {
|
||||
return inventory;
|
||||
}
|
||||
|
||||
public void setInventory(Inventory inv) {
|
||||
this.inventory.getItemStacks().clear();
|
||||
this.inventory.getItemStacks().addAll(inv.getItemStacks());
|
||||
this.inventory.setSelectedIndex(inv.getSelectedIndex());
|
||||
}
|
||||
}
|
|
@ -1,7 +1,14 @@
|
|||
package nl.andrewl.aos_core;
|
||||
|
||||
import nl.andrewl.aos_core.net.*;
|
||||
import nl.andrewl.aos_core.net.udp.*;
|
||||
import nl.andrewl.aos_core.net.client.*;
|
||||
import nl.andrewl.aos_core.net.connect.ConnectAcceptMessage;
|
||||
import nl.andrewl.aos_core.net.connect.ConnectRejectMessage;
|
||||
import nl.andrewl.aos_core.net.connect.ConnectRequestMessage;
|
||||
import nl.andrewl.aos_core.net.connect.DatagramInit;
|
||||
import nl.andrewl.aos_core.net.world.ChunkDataMessage;
|
||||
import nl.andrewl.aos_core.net.world.ChunkHashMessage;
|
||||
import nl.andrewl.aos_core.net.world.ChunkUpdateMessage;
|
||||
import nl.andrewl.aos_core.net.world.WorldInfoMessage;
|
||||
import nl.andrewl.record_net.Message;
|
||||
import nl.andrewl.record_net.Serializer;
|
||||
import nl.andrewl.record_net.util.ExtendedDataInputStream;
|
||||
|
@ -33,6 +40,10 @@ public final class Net {
|
|||
serializer.registerType(11, PlayerJoinMessage.class);
|
||||
serializer.registerType(12, PlayerLeaveMessage.class);
|
||||
serializer.registerType(13, WorldInfoMessage.class);
|
||||
// Separate serializers for client inventory messages.
|
||||
serializer.registerTypeSerializer(14, new InventorySerializer());
|
||||
serializer.registerTypeSerializer(15, new ItemStackSerializer());
|
||||
serializer.registerType(16, InventorySelectedStackMessage.class);
|
||||
}
|
||||
|
||||
public static ExtendedDataInputStream getInputStream(InputStream in) {
|
||||
|
|
|
@ -1,13 +1,23 @@
|
|||
package nl.andrewl.aos_core.model.item;
|
||||
|
||||
public class BlockItemStack extends ItemStack {
|
||||
private int selectedValue = 1;
|
||||
private byte selectedValue = 1;
|
||||
|
||||
public BlockItemStack(BlockItem item, int amount) {
|
||||
public BlockItemStack(BlockItem item, int amount, byte selectedValue) {
|
||||
super(item, amount);
|
||||
this.selectedValue = selectedValue;
|
||||
}
|
||||
|
||||
public int getSelectedValue() {
|
||||
public BlockItemStack(BlockItem item) {
|
||||
this(item, 50, (byte) 1);
|
||||
}
|
||||
|
||||
public byte getSelectedValue() {
|
||||
return selectedValue;
|
||||
}
|
||||
|
||||
public void setSelectedValue(byte selectedValue) {
|
||||
if (selectedValue < 1) return;
|
||||
this.selectedValue = selectedValue;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,9 +4,29 @@ public class GunItemStack extends ItemStack {
|
|||
private int bulletCount;
|
||||
private int clipCount;
|
||||
|
||||
public GunItemStack(Gun gun) {
|
||||
public GunItemStack(Gun gun, int bulletCount, int clipCount) {
|
||||
super(gun, 1);
|
||||
bulletCount = gun.getMaxBulletCount();
|
||||
clipCount = gun.getMaxClipCount();
|
||||
this.bulletCount = bulletCount;
|
||||
this.clipCount = clipCount;
|
||||
}
|
||||
|
||||
public GunItemStack(Gun gun) {
|
||||
this(gun, gun.getMaxBulletCount(), gun.getMaxClipCount());
|
||||
}
|
||||
|
||||
public int getBulletCount() {
|
||||
return bulletCount;
|
||||
}
|
||||
|
||||
public void setBulletCount(int bulletCount) {
|
||||
this.bulletCount = bulletCount;
|
||||
}
|
||||
|
||||
public int getClipCount() {
|
||||
return clipCount;
|
||||
}
|
||||
|
||||
public void setClipCount(int clipCount) {
|
||||
this.clipCount = clipCount;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,13 +25,17 @@ public class Inventory {
|
|||
return itemStacks;
|
||||
}
|
||||
|
||||
public int getSelectedIndex() {
|
||||
return selectedIndex;
|
||||
}
|
||||
|
||||
public ItemStack getSelectedItemStack() {
|
||||
return itemStacks.get(selectedIndex);
|
||||
}
|
||||
|
||||
public void setSelectedIndex(int newIndex) {
|
||||
while (newIndex < 0) newIndex += itemStacks.size();
|
||||
while (newIndex > itemStacks.size() - 1) newIndex -= itemStacks.size();
|
||||
if (newIndex < 0) newIndex = 0;
|
||||
if (newIndex > itemStacks.size() - 1) newIndex = itemStacks.size() - 1;
|
||||
this.selectedIndex = newIndex;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
package nl.andrewl.aos_core.model.item;
|
||||
|
||||
import nl.andrewl.record_net.util.ExtendedDataInputStream;
|
||||
import nl.andrewl.record_net.util.ExtendedDataOutputStream;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Represents a stack of items in the player's inventory. This is generally
|
||||
* a type of item, and the amount of it.
|
||||
|
@ -22,6 +27,53 @@ public class ItemStack {
|
|||
}
|
||||
|
||||
public void setAmount(int amount) {
|
||||
this.amount = amount;
|
||||
if (amount > -1) {
|
||||
this.amount = amount;
|
||||
}
|
||||
}
|
||||
|
||||
public void incrementAmount() {
|
||||
setAmount(amount + 1);
|
||||
}
|
||||
|
||||
public void decrementAmount() {
|
||||
setAmount(amount - 1);
|
||||
}
|
||||
|
||||
public static int byteSize(ItemStack stack) {
|
||||
int bytes = 2 * Integer.BYTES;
|
||||
if (stack instanceof BlockItemStack) {
|
||||
bytes += Byte.BYTES;
|
||||
} else if (stack instanceof GunItemStack) {
|
||||
bytes += 2 * Integer.BYTES;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public static void write(ItemStack stack, ExtendedDataOutputStream out) throws IOException {
|
||||
out.writeInt(stack.type.id);
|
||||
out.writeInt(stack.amount);
|
||||
if (stack instanceof BlockItemStack b) {
|
||||
out.writeByte(b.getSelectedValue());
|
||||
} else if (stack instanceof GunItemStack g) {
|
||||
out.writeInt(g.getClipCount());
|
||||
out.writeInt(g.getBulletCount());
|
||||
}
|
||||
}
|
||||
|
||||
public static ItemStack read(ExtendedDataInputStream in) throws IOException {
|
||||
int typeId = in.readInt();
|
||||
Item item = ItemTypes.get(typeId);
|
||||
int amount = in.readInt();
|
||||
if (item instanceof Gun g) {
|
||||
int clipCount = in.readInt();
|
||||
int bulletCount = in.readInt();
|
||||
return new GunItemStack(g, bulletCount, clipCount);
|
||||
} else if (item instanceof BlockItem b) {
|
||||
byte selectedValue = in.readByte();
|
||||
return new BlockItemStack(b, amount, selectedValue);
|
||||
} else {
|
||||
throw new IOException("Invalid item stack.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,9 +12,12 @@ public final class ItemTypes {
|
|||
private static final Map<Integer, Item> TYPES_BY_ID = new HashMap<>();
|
||||
private static final Map<String, Item> TYPES_BY_NAME = new HashMap<>();
|
||||
|
||||
public static final BlockItem BLOCK = new BlockItem(1);
|
||||
public static final Rifle RIFLE = new Rifle(2);
|
||||
|
||||
static {
|
||||
registerType(new BlockItem(1));
|
||||
registerType(new Rifle(2));
|
||||
registerType(BLOCK);
|
||||
registerType(RIFLE);
|
||||
}
|
||||
|
||||
public static void registerType(Item type) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package nl.andrewl.aos_core.net.udp;
|
||||
package nl.andrewl.aos_core.net.client;
|
||||
|
||||
import nl.andrewl.record_net.Message;
|
||||
|
||||
|
@ -19,5 +19,6 @@ public record ClientInputState(
|
|||
|
||||
// Interaction
|
||||
boolean hitting, // Usually a "left-click" action.
|
||||
boolean interacting // Usually a "right-click" action.
|
||||
boolean interacting, // Usually a "right-click" action.
|
||||
int selectedInventoryIndex // The selected index in the player's inventory.
|
||||
) implements Message {}
|
|
@ -0,0 +1,15 @@
|
|||
package nl.andrewl.aos_core.net.client;
|
||||
|
||||
import nl.andrewl.aos_core.model.item.Inventory;
|
||||
import nl.andrewl.record_net.Message;
|
||||
|
||||
/**
|
||||
* A message that's sent by the server to a client with information about the
|
||||
* client's full inventory configuration. Here, we use a custom serializer
|
||||
* since the inventory object contains a lot of inheritance.
|
||||
*
|
||||
* @see InventorySerializer
|
||||
*/
|
||||
public record ClientInventoryMessage(
|
||||
Inventory inv
|
||||
) implements Message {}
|
|
@ -1,4 +1,4 @@
|
|||
package nl.andrewl.aos_core.net.udp;
|
||||
package nl.andrewl.aos_core.net.client;
|
||||
|
||||
import nl.andrewl.record_net.Message;
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package nl.andrewl.aos_core.net.client;
|
||||
|
||||
import nl.andrewl.record_net.Message;
|
||||
|
||||
/**
|
||||
* A message that's sent when a player's selected inventory stack changes.
|
||||
* @param index The selected index.
|
||||
*/
|
||||
public record InventorySelectedStackMessage(
|
||||
int index
|
||||
) implements Message {}
|
|
@ -0,0 +1,56 @@
|
|||
package nl.andrewl.aos_core.net.client;
|
||||
|
||||
import nl.andrewl.aos_core.model.item.Inventory;
|
||||
import nl.andrewl.aos_core.model.item.ItemStack;
|
||||
import nl.andrewl.record_net.MessageReader;
|
||||
import nl.andrewl.record_net.MessageTypeSerializer;
|
||||
import nl.andrewl.record_net.MessageWriter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class InventorySerializer implements MessageTypeSerializer<ClientInventoryMessage> {
|
||||
@Override
|
||||
public Class<ClientInventoryMessage> messageClass() {
|
||||
return ClientInventoryMessage.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function<ClientInventoryMessage, Integer> byteSizeFunction() {
|
||||
return msg -> {
|
||||
int bytes = Integer.BYTES; // For the stack count size.
|
||||
for (var stack : msg.inv().getItemStacks()) {
|
||||
bytes += ItemStack.byteSize(stack);
|
||||
}
|
||||
bytes += Integer.BYTES; // Selected index.
|
||||
return bytes;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageReader<ClientInventoryMessage> reader() {
|
||||
return in -> {
|
||||
int stacksCount = in.readInt();
|
||||
List<ItemStack> stacks = new ArrayList<>(stacksCount);
|
||||
for (int i = 0; i < stacksCount; i++) {
|
||||
stacks.add(ItemStack.read(in));
|
||||
}
|
||||
int selectedIndex = in.readInt();
|
||||
Inventory inv = new Inventory(stacks, selectedIndex);
|
||||
return new ClientInventoryMessage(inv);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageWriter<ClientInventoryMessage> writer() {
|
||||
return (msg, out) -> {
|
||||
Inventory inv = msg.inv();
|
||||
out.writeInt(inv.getItemStacks().size());
|
||||
for (var stack : inv.getItemStacks()) {
|
||||
ItemStack.write(stack, out);
|
||||
}
|
||||
out.writeInt(inv.getSelectedIndex());
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package nl.andrewl.aos_core.net.client;
|
||||
|
||||
import nl.andrewl.aos_core.model.item.Inventory;
|
||||
import nl.andrewl.aos_core.model.item.ItemStack;
|
||||
import nl.andrewl.record_net.Message;
|
||||
|
||||
/**
|
||||
* Lightweight packet that's sent when a single item stack in a player's
|
||||
* inventory updates.
|
||||
*/
|
||||
public record ItemStackMessage(
|
||||
int index,
|
||||
ItemStack stack
|
||||
) implements Message {
|
||||
public ItemStackMessage(Inventory inv) {
|
||||
this(inv.getSelectedIndex(), inv.getSelectedItemStack());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package nl.andrewl.aos_core.net.client;
|
||||
|
||||
import nl.andrewl.aos_core.model.item.ItemStack;
|
||||
import nl.andrewl.record_net.MessageReader;
|
||||
import nl.andrewl.record_net.MessageTypeSerializer;
|
||||
import nl.andrewl.record_net.MessageWriter;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
public class ItemStackSerializer implements MessageTypeSerializer<ItemStackMessage> {
|
||||
@Override
|
||||
public Class<ItemStackMessage> messageClass() {
|
||||
return ItemStackMessage.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function<ItemStackMessage, Integer> byteSizeFunction() {
|
||||
return msg -> Integer.BYTES + ItemStack.byteSize(msg.stack());
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageReader<ItemStackMessage> reader() {
|
||||
return in -> {
|
||||
int index = in.readInt();
|
||||
ItemStack stack = ItemStack.read(in);
|
||||
return new ItemStackMessage(index, stack);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageWriter<ItemStackMessage> writer() {
|
||||
return (msg, out) -> {
|
||||
out.writeInt(msg.index());
|
||||
ItemStack.write(msg.stack(), out);
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package nl.andrewl.aos_core.net;
|
||||
package nl.andrewl.aos_core.net.client;
|
||||
|
||||
import nl.andrewl.aos_core.model.Player;
|
||||
import nl.andrewl.record_net.Message;
|
|
@ -1,4 +1,4 @@
|
|||
package nl.andrewl.aos_core.net;
|
||||
package nl.andrewl.aos_core.net.client;
|
||||
|
||||
import nl.andrewl.record_net.Message;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package nl.andrewl.aos_core.net.udp;
|
||||
package nl.andrewl.aos_core.net.client;
|
||||
|
||||
import nl.andrewl.aos_core.model.Player;
|
||||
import nl.andrewl.record_net.Message;
|
|
@ -1,4 +1,4 @@
|
|||
package nl.andrewl.aos_core.net;
|
||||
package nl.andrewl.aos_core.net.connect;
|
||||
|
||||
import nl.andrewl.record_net.Message;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package nl.andrewl.aos_core.net;
|
||||
package nl.andrewl.aos_core.net.connect;
|
||||
|
||||
import nl.andrewl.record_net.Message;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package nl.andrewl.aos_core.net;
|
||||
package nl.andrewl.aos_core.net.connect;
|
||||
|
||||
import nl.andrewl.record_net.Message;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package nl.andrewl.aos_core.net.udp;
|
||||
package nl.andrewl.aos_core.net.connect;
|
||||
|
||||
import nl.andrewl.record_net.Message;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package nl.andrewl.aos_core.net;
|
||||
package nl.andrewl.aos_core.net.world;
|
||||
|
||||
import nl.andrewl.aos_core.model.world.Chunk;
|
||||
import nl.andrewl.record_net.Message;
|
|
@ -1,4 +1,4 @@
|
|||
package nl.andrewl.aos_core.net;
|
||||
package nl.andrewl.aos_core.net.world;
|
||||
|
||||
import nl.andrewl.aos_core.model.world.Chunk;
|
||||
import nl.andrewl.record_net.Message;
|
|
@ -1,4 +1,4 @@
|
|||
package nl.andrewl.aos_core.net.udp;
|
||||
package nl.andrewl.aos_core.net.world;
|
||||
|
||||
import nl.andrewl.aos_core.model.world.World;
|
||||
import nl.andrewl.record_net.Message;
|
|
@ -1,4 +1,4 @@
|
|||
package nl.andrewl.aos_core.net;
|
||||
package nl.andrewl.aos_core.net.world;
|
||||
|
||||
import nl.andrewl.aos_core.model.world.World;
|
||||
import nl.andrewl.record_net.Message;
|
|
@ -3,6 +3,14 @@ package nl.andrewl.aos2_server;
|
|||
import nl.andrewl.aos_core.Net;
|
||||
import nl.andrewl.aos_core.model.world.Chunk;
|
||||
import nl.andrewl.aos_core.net.*;
|
||||
import nl.andrewl.aos_core.net.client.PlayerJoinMessage;
|
||||
import nl.andrewl.aos_core.net.connect.ConnectAcceptMessage;
|
||||
import nl.andrewl.aos_core.net.connect.ConnectRejectMessage;
|
||||
import nl.andrewl.aos_core.net.connect.ConnectRequestMessage;
|
||||
import nl.andrewl.aos_core.net.client.ClientInventoryMessage;
|
||||
import nl.andrewl.aos_core.net.world.ChunkDataMessage;
|
||||
import nl.andrewl.aos_core.net.world.ChunkHashMessage;
|
||||
import nl.andrewl.aos_core.net.world.WorldInfoMessage;
|
||||
import nl.andrewl.record_net.Message;
|
||||
import nl.andrewl.record_net.util.ExtendedDataInputStream;
|
||||
import nl.andrewl.record_net.util.ExtendedDataOutputStream;
|
||||
|
@ -81,6 +89,7 @@ public class ClientCommunicationHandler {
|
|||
try {
|
||||
Message msg = Net.read(in);
|
||||
if (msg instanceof ConnectRequestMessage connectMsg) {
|
||||
log.debug("Received connect request from player \"{}\"", connectMsg.username());
|
||||
// Try to set the TCP timeout back to 0 now that we've got the correct request.
|
||||
socket.setSoTimeout(0);
|
||||
this.clientAddress = socket.getInetAddress();
|
||||
|
@ -90,7 +99,10 @@ public class ClientCommunicationHandler {
|
|||
log.debug("Sent connect accept message.");
|
||||
|
||||
sendTcpMessage(new WorldInfoMessage(server.getWorld()));
|
||||
// Send join info for all players that are already connected.
|
||||
// Send player's inventory information.
|
||||
sendTcpMessage(new ClientInventoryMessage(player.getInventory()));
|
||||
|
||||
// Send "join" info about all the players that are already connected, so the client is aware of them.
|
||||
for (var player : server.getPlayerManager().getPlayers()) {
|
||||
if (player.getId() != this.player.getId()) {
|
||||
sendTcpMessage(new PlayerJoinMessage(player));
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package nl.andrewl.aos2_server;
|
||||
|
||||
import nl.andrewl.aos_core.Net;
|
||||
import nl.andrewl.aos_core.net.PlayerJoinMessage;
|
||||
import nl.andrewl.aos_core.net.PlayerLeaveMessage;
|
||||
import nl.andrewl.aos_core.net.udp.DatagramInit;
|
||||
import nl.andrewl.aos_core.net.client.PlayerJoinMessage;
|
||||
import nl.andrewl.aos_core.net.client.PlayerLeaveMessage;
|
||||
import nl.andrewl.aos_core.net.connect.DatagramInit;
|
||||
import nl.andrewl.record_net.Message;
|
||||
import org.joml.Vector3f;
|
||||
import org.slf4j.Logger;
|
||||
|
|
|
@ -6,9 +6,9 @@ import nl.andrewl.aos_core.config.Config;
|
|||
import nl.andrewl.aos_core.model.world.World;
|
||||
import nl.andrewl.aos_core.model.world.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.client.ClientInputState;
|
||||
import nl.andrewl.aos_core.net.client.ClientOrientationState;
|
||||
import nl.andrewl.aos_core.net.connect.DatagramInit;
|
||||
import nl.andrewl.record_net.Message;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -66,8 +66,9 @@ public class Server implements Runnable {
|
|||
} else if (msg instanceof ClientInputState inputState) {
|
||||
ServerPlayer player = playerManager.getPlayer(inputState.clientId());
|
||||
if (player != null) {
|
||||
player.getActionManager().setLastInputState(inputState);
|
||||
playerManager.broadcastUdpMessage(player.getUpdateMessage());
|
||||
if (player.getActionManager().setLastInputState(inputState)) {
|
||||
playerManager.broadcastUdpMessage(player.getUpdateMessage());
|
||||
}
|
||||
}
|
||||
} else if (msg instanceof ClientOrientationState orientationState) {
|
||||
ServerPlayer player = playerManager.getPlayer(orientationState.clientId());
|
||||
|
@ -86,7 +87,7 @@ public class Server implements Runnable {
|
|||
ForkJoinPool.commonPool().submit(() -> {
|
||||
try {
|
||||
handler.establishConnection();
|
||||
} catch (IOException e) {
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -2,15 +2,20 @@ package nl.andrewl.aos2_server;
|
|||
|
||||
import nl.andrewl.aos2_server.logic.PlayerActionManager;
|
||||
import nl.andrewl.aos_core.model.Player;
|
||||
import nl.andrewl.aos_core.model.item.*;
|
||||
import nl.andrewl.aos_core.model.item.gun.Rifle;
|
||||
import nl.andrewl.aos_core.net.udp.PlayerUpdateMessage;
|
||||
import nl.andrewl.aos_core.model.item.BlockItemStack;
|
||||
import nl.andrewl.aos_core.model.item.GunItemStack;
|
||||
import nl.andrewl.aos_core.model.item.Inventory;
|
||||
import nl.andrewl.aos_core.model.item.ItemTypes;
|
||||
import nl.andrewl.aos_core.net.client.PlayerUpdateMessage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An extension of the base player class with additional information that's
|
||||
* needed for the server.
|
||||
*/
|
||||
public class ServerPlayer extends Player {
|
||||
private static final Logger log = LoggerFactory.getLogger(ServerPlayer.class);
|
||||
|
||||
|
@ -26,16 +31,20 @@ public class ServerPlayer extends Player {
|
|||
|
||||
public ServerPlayer(int id, String username) {
|
||||
super(id, username);
|
||||
this.actionManager = new PlayerActionManager(this);
|
||||
this.inventory = new Inventory(new ArrayList<>(), 0);
|
||||
this.actionManager = new PlayerActionManager(this);
|
||||
inventory.getItemStacks().add(new GunItemStack(ItemTypes.get("Rifle")));
|
||||
inventory.getItemStacks().add(new BlockItemStack(ItemTypes.get("Block"), 50));
|
||||
inventory.getItemStacks().add(new BlockItemStack(ItemTypes.get("Block"), 50, (byte) 1));
|
||||
}
|
||||
|
||||
public PlayerActionManager getActionManager() {
|
||||
return actionManager;
|
||||
}
|
||||
|
||||
public Inventory getInventory() {
|
||||
return inventory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to build an update message for this player, to be sent to
|
||||
* various clients.
|
||||
|
|
|
@ -3,9 +3,13 @@ package nl.andrewl.aos2_server.logic;
|
|||
import nl.andrewl.aos2_server.Server;
|
||||
import nl.andrewl.aos2_server.ServerPlayer;
|
||||
import nl.andrewl.aos2_server.config.ServerConfig;
|
||||
import nl.andrewl.aos_core.model.item.BlockItemStack;
|
||||
import nl.andrewl.aos_core.model.item.ItemTypes;
|
||||
import nl.andrewl.aos_core.model.world.World;
|
||||
import nl.andrewl.aos_core.net.udp.ChunkUpdateMessage;
|
||||
import nl.andrewl.aos_core.net.udp.ClientInputState;
|
||||
import nl.andrewl.aos_core.net.client.ClientInputState;
|
||||
import nl.andrewl.aos_core.net.client.InventorySelectedStackMessage;
|
||||
import nl.andrewl.aos_core.net.client.ItemStackMessage;
|
||||
import nl.andrewl.aos_core.net.world.ChunkUpdateMessage;
|
||||
import org.joml.Math;
|
||||
import org.joml.Vector2i;
|
||||
import org.joml.Vector3f;
|
||||
|
@ -30,15 +34,23 @@ public class PlayerActionManager {
|
|||
|
||||
public PlayerActionManager(ServerPlayer player) {
|
||||
this.player = player;
|
||||
lastInputState = new ClientInputState(player.getId(), false, false, false, false, false, false, false, false, false);
|
||||
lastInputState = new ClientInputState(
|
||||
player.getId(),
|
||||
false, false, false, false,
|
||||
false, false, false,
|
||||
false, false,
|
||||
player.getInventory().getSelectedIndex()
|
||||
);
|
||||
}
|
||||
|
||||
public ClientInputState getLastInputState() {
|
||||
return lastInputState;
|
||||
}
|
||||
|
||||
public void setLastInputState(ClientInputState lastInputState) {
|
||||
this.lastInputState = lastInputState;
|
||||
public boolean setLastInputState(ClientInputState lastInputState) {
|
||||
boolean change = !lastInputState.equals(this.lastInputState);
|
||||
if (change) this.lastInputState = lastInputState;
|
||||
return change;
|
||||
}
|
||||
|
||||
public boolean isUpdated() {
|
||||
|
@ -46,36 +58,65 @@ public class PlayerActionManager {
|
|||
}
|
||||
|
||||
public void tick(float dt, World world, Server server) {
|
||||
updated = false; // Reset the updated flag. This will be set to true if the player was updated in this tick.
|
||||
long now = System.currentTimeMillis();
|
||||
if (player.getInventory().getSelectedIndex() != lastInputState.selectedInventoryIndex()) {
|
||||
player.getInventory().setSelectedIndex(lastInputState.selectedInventoryIndex());
|
||||
// Tell the client that their inventory slot has been updated properly.
|
||||
server.getPlayerManager().getHandler(player.getId()).sendDatagramPacket(new InventorySelectedStackMessage(player.getInventory().getSelectedIndex()));
|
||||
updated = true; // Tell everyone else that this player's selected item has changed.
|
||||
}
|
||||
|
||||
if (player.getInventory().getSelectedItemStack().getType().equals(ItemTypes.BLOCK)) {
|
||||
tickBlockAction(now, server, world);
|
||||
}
|
||||
|
||||
tickMovement(dt, world, server.getConfig().physics);
|
||||
}
|
||||
|
||||
private void tickBlockAction(long now, Server server, World world) {
|
||||
BlockItemStack stack = (BlockItemStack) player.getInventory().getSelectedItemStack();
|
||||
// Check for breaking blocks.
|
||||
if (lastInputState.hitting() && now - lastBlockRemovedAt > server.getConfig().actions.blockRemoveCooldown * 1000) {
|
||||
if (
|
||||
lastInputState.hitting() &&
|
||||
stack.getAmount() < stack.getType().getMaxAmount() &&
|
||||
now - lastBlockRemovedAt > server.getConfig().actions.blockRemoveCooldown * 1000
|
||||
) {
|
||||
Vector3f eyePos = new Vector3f(player.getPosition());
|
||||
eyePos.y += getEyeHeight();
|
||||
var hit = world.getLookingAtPos(eyePos, player.getViewVector(), 10);
|
||||
if (hit != null) {
|
||||
world.setBlockAt(hit.pos().x, hit.pos().y, hit.pos().z, (byte) 0);
|
||||
lastBlockRemovedAt = now;
|
||||
stack.incrementAmount();
|
||||
server.getPlayerManager().getHandler(player.getId()).sendDatagramPacket(new ItemStackMessage(player.getInventory()));
|
||||
server.getPlayerManager().broadcastUdpMessage(ChunkUpdateMessage.fromWorld(hit.pos(), world));
|
||||
}
|
||||
}
|
||||
// Check for placing blocks.
|
||||
if (lastInputState.interacting() && now - lastBlockPlacedAt > server.getConfig().actions.blockPlaceCooldown * 1000) {
|
||||
if (
|
||||
lastInputState.interacting() &&
|
||||
stack.getAmount() > 0 &&
|
||||
now - lastBlockPlacedAt > server.getConfig().actions.blockPlaceCooldown * 1000
|
||||
) {
|
||||
Vector3f eyePos = new Vector3f(player.getPosition());
|
||||
eyePos.y += getEyeHeight();
|
||||
var hit = world.getLookingAtPos(eyePos, player.getViewVector(), 10);
|
||||
if (hit != null) {
|
||||
Vector3i placePos = new Vector3i(hit.pos());
|
||||
placePos.add(hit.norm());
|
||||
world.setBlockAt(placePos.x, placePos.y, placePos.z, (byte) 1);
|
||||
lastBlockPlacedAt = now;
|
||||
server.getPlayerManager().broadcastUdpMessage(ChunkUpdateMessage.fromWorld(placePos, world));
|
||||
if (!isSpaceOccupied(placePos)) { // Ensure that we can't place blocks in space we're occupying.
|
||||
world.setBlockAt(placePos.x, placePos.y, placePos.z, stack.getSelectedValue());
|
||||
lastBlockPlacedAt = now;
|
||||
stack.decrementAmount();
|
||||
server.getPlayerManager().getHandler(player.getId()).sendDatagramPacket(new ItemStackMessage(player.getInventory()));
|
||||
server.getPlayerManager().broadcastUdpMessage(ChunkUpdateMessage.fromWorld(placePos, world));
|
||||
}
|
||||
}
|
||||
}
|
||||
tickMovement(dt, world, server.getConfig().physics);
|
||||
}
|
||||
|
||||
private void tickMovement(float dt, World world, ServerConfig.PhysicsConfig config) {
|
||||
updated = false; // Reset the updated flag. This will be set to true if the player was updated in this tick.
|
||||
var velocity = player.getVelocity();
|
||||
var position = player.getPosition();
|
||||
boolean grounded = isGrounded(world);
|
||||
|
@ -178,15 +219,30 @@ public class PlayerActionManager {
|
|||
return points;
|
||||
}
|
||||
|
||||
private boolean isSpaceOccupied(Vector3i pos) {
|
||||
var playerPos = player.getPosition();
|
||||
float playerBodyMinZ = playerPos.z - RADIUS;
|
||||
float playerBodyMaxZ = playerPos.z + RADIUS;
|
||||
float playerBodyMinX = playerPos.x - RADIUS;
|
||||
float playerBodyMaxX = playerPos.x + RADIUS;
|
||||
float playerBodyMinY = playerPos.y;
|
||||
float playerBodyMaxY = playerPos.y + getCurrentHeight();
|
||||
|
||||
// Compute the bounds of all blocks the player is intersecting with.
|
||||
int minX = (int) Math.floor(playerBodyMinX);
|
||||
int minZ = (int) Math.floor(playerBodyMinZ);
|
||||
int minY = (int) Math.floor(playerBodyMinY);
|
||||
int maxX = (int) Math.floor(playerBodyMaxX);
|
||||
int maxZ = (int) Math.floor(playerBodyMaxZ);
|
||||
int maxY = (int) Math.floor(playerBodyMaxY);
|
||||
|
||||
return pos.x >= minX && pos.x <= maxX && pos.y >= minY && pos.y <= maxY && pos.z >= minZ && pos.z <= maxZ;
|
||||
}
|
||||
|
||||
private void checkBlockCollisions(Vector3f movement, World world) {
|
||||
var position = player.getPosition();
|
||||
var velocity = player.getVelocity();
|
||||
final Vector3f nextTickPosition = new Vector3f(position).add(movement);
|
||||
// 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
|
||||
// );
|
||||
float height = getCurrentHeight();
|
||||
float delta = 0.00001f;
|
||||
final Vector3f stepSize = new Vector3f(movement).normalize(1.0f);
|
||||
|
@ -205,7 +261,6 @@ public class PlayerActionManager {
|
|||
|
||||
// Check if we collide with anything at this new position.
|
||||
|
||||
|
||||
float playerBodyPrevMinZ = lastPos.z - RADIUS;
|
||||
float playerBodyPrevMaxZ = lastPos.z + RADIUS;
|
||||
float playerBodyPrevMinX = lastPos.x - RADIUS;
|
||||
|
|
Loading…
Reference in New Issue