mirror of https://github.com/Minestom/Minestom.git
WIP entity implementation
This commit is contained in:
parent
5130931b29
commit
d4bf3bd2ec
|
@ -5,6 +5,7 @@ import fr.adamaq01.ozao.net.server.Connection;
|
|||
import fr.adamaq01.ozao.net.server.Server;
|
||||
import fr.adamaq01.ozao.net.server.ServerHandler;
|
||||
import fr.adamaq01.ozao.net.server.backend.tcp.TCPServer;
|
||||
import fr.themode.minestom.entity.EntityManager;
|
||||
import fr.themode.minestom.net.ConnectionManager;
|
||||
import fr.themode.minestom.net.PacketProcessor;
|
||||
import fr.themode.minestom.net.packet.server.play.KeepAlivePacket;
|
||||
|
@ -14,10 +15,15 @@ import java.lang.reflect.InvocationTargetException;
|
|||
|
||||
public class Main {
|
||||
|
||||
// In-Game Manager
|
||||
private static EntityManager entityManager;
|
||||
|
||||
// Others
|
||||
private static ConnectionManager connectionManager;
|
||||
private static PacketProcessor packetProcessor;
|
||||
|
||||
public static void main(String[] args) {
|
||||
entityManager = new EntityManager();
|
||||
|
||||
connectionManager = new ConnectionManager();
|
||||
packetProcessor = new PacketProcessor(connectionManager);
|
||||
|
@ -70,7 +76,18 @@ public class Main {
|
|||
KeepAlivePacket keepAlivePacket = new KeepAlivePacket(id);
|
||||
player.getPlayerConnection().sendPacket(keepAlivePacket);
|
||||
});
|
||||
|
||||
// Entities update
|
||||
entityManager.update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static EntityManager getEntityManager() {
|
||||
return entityManager;
|
||||
}
|
||||
|
||||
public static ConnectionManager getConnectionManager() {
|
||||
return connectionManager;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
package fr.themode.minestom.entity;
|
||||
|
||||
import fr.themode.minestom.Main;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class Entity {
|
||||
|
||||
private static volatile int lastEntityId;
|
||||
protected double x, y, z;
|
||||
private int id;
|
||||
private UUID uuid;
|
||||
private boolean isActive; // False if entity has only been instanced without being added somewhere
|
||||
private boolean shouldRemove;
|
||||
|
||||
public Entity() {
|
||||
this.id = generateId();
|
||||
this.uuid = UUID.randomUUID();
|
||||
}
|
||||
|
||||
private static int generateId() {
|
||||
return ++lastEntityId;
|
||||
}
|
||||
|
||||
public int getEntityId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public UUID getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return isActive;
|
||||
}
|
||||
|
||||
public void addToWorld() {
|
||||
this.isActive = true;
|
||||
EntityManager entityManager = Main.getEntityManager();
|
||||
if (this instanceof LivingEntity) {
|
||||
entityManager.addLivingEntity((LivingEntity) this);
|
||||
}
|
||||
}
|
||||
|
||||
public void setPosition(double x, double y, double z) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public double getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public void setX(double x) {
|
||||
this.x = x;
|
||||
}
|
||||
|
||||
public double getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public void setY(double y) {
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public double getZ() {
|
||||
return z;
|
||||
}
|
||||
|
||||
public void setZ(double z) {
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
this.shouldRemove = true;
|
||||
}
|
||||
|
||||
protected boolean shouldRemove() {
|
||||
return shouldRemove;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package fr.themode.minestom.entity;
|
||||
|
||||
import fr.themode.minestom.net.packet.server.play.EntityPacket;
|
||||
import fr.themode.minestom.net.packet.server.play.SpawnMobPacket;
|
||||
import fr.themode.minestom.net.player.PlayerConnection;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public abstract class EntityCreature extends LivingEntity {
|
||||
|
||||
private Set<Player> viewers = Collections.synchronizedSet(new HashSet<>());
|
||||
|
||||
private int entityType;
|
||||
|
||||
public EntityCreature(int entityType) {
|
||||
super();
|
||||
this.entityType = entityType;
|
||||
}
|
||||
|
||||
public void addViewer(Player player) {
|
||||
this.viewers.add(player);
|
||||
PlayerConnection playerConnection = player.getPlayerConnection();
|
||||
|
||||
EntityPacket entityPacket = new EntityPacket();
|
||||
entityPacket.entityId = getEntityId();
|
||||
SpawnMobPacket spawnMobPacket = new SpawnMobPacket();
|
||||
spawnMobPacket.entityId = getEntityId();
|
||||
spawnMobPacket.entityUuid = getUuid();
|
||||
spawnMobPacket.entityType = getEntityType();
|
||||
spawnMobPacket.x = getX();
|
||||
spawnMobPacket.y = getY();
|
||||
spawnMobPacket.z = getZ();
|
||||
spawnMobPacket.yaw = getYaw();
|
||||
spawnMobPacket.pitch = getPitch();
|
||||
spawnMobPacket.headPitch = 0;
|
||||
playerConnection.sendPacket(entityPacket);
|
||||
playerConnection.sendPacket(spawnMobPacket);
|
||||
}
|
||||
|
||||
public void removeViewer(Player player) {
|
||||
if (!viewers.contains(player))
|
||||
return;
|
||||
// TODO send packet to remove entity
|
||||
}
|
||||
|
||||
public Set<Player> getViewers() {
|
||||
return Collections.unmodifiableSet(viewers);
|
||||
}
|
||||
|
||||
public int getEntityType() {
|
||||
return entityType;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package fr.themode.minestom.entity;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public class EntityManager {
|
||||
|
||||
private Set<LivingEntity> livingEntities = Collections.synchronizedSet(new HashSet<>());
|
||||
|
||||
private ExecutorService pool = Executors.newFixedThreadPool(5);
|
||||
|
||||
public void update() {
|
||||
|
||||
livingEntities.removeIf(livingEntity -> livingEntity.shouldRemove());
|
||||
|
||||
synchronized (livingEntities) {
|
||||
Iterator<LivingEntity> iterator = livingEntities.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
LivingEntity entity = iterator.next();
|
||||
pool.submit(entity::update);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Set<LivingEntity> getEntities() {
|
||||
return Collections.unmodifiableSet(livingEntities);
|
||||
}
|
||||
|
||||
protected void addLivingEntity(LivingEntity livingEntity) {
|
||||
this.livingEntities.add(livingEntity);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package fr.themode.minestom.entity;
|
||||
|
||||
public abstract class LivingEntity extends Entity {
|
||||
|
||||
protected float yaw, pitch;
|
||||
protected boolean onGround;
|
||||
|
||||
public LivingEntity() {
|
||||
super();
|
||||
}
|
||||
|
||||
public abstract void update();
|
||||
|
||||
public float getYaw() {
|
||||
return yaw;
|
||||
}
|
||||
|
||||
public float getPitch() {
|
||||
return pitch;
|
||||
}
|
||||
|
||||
}
|
|
@ -2,19 +2,25 @@ package fr.themode.minestom.entity;
|
|||
|
||||
import fr.themode.minestom.net.player.PlayerConnection;
|
||||
|
||||
public class Player {
|
||||
public class Player extends LivingEntity {
|
||||
|
||||
private boolean isSneaking;
|
||||
private boolean isSprinting;
|
||||
|
||||
private double x, y, z;
|
||||
private float yaw, pitch;
|
||||
private boolean onGround;
|
||||
private long lastKeepAlive;
|
||||
|
||||
private PlayerConnection playerConnection;
|
||||
|
||||
// TODO set proper UUID
|
||||
public Player(PlayerConnection playerConnection) {
|
||||
this.playerConnection = playerConnection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
// System.out.println("Je suis l'update");
|
||||
}
|
||||
|
||||
public PlayerConnection getPlayerConnection() {
|
||||
return playerConnection;
|
||||
}
|
||||
|
@ -34,6 +40,14 @@ public class Player {
|
|||
this.onGround = onGround;
|
||||
}
|
||||
|
||||
public void refreshSneaking(boolean sneaking) {
|
||||
isSneaking = sneaking;
|
||||
}
|
||||
|
||||
public void refreshSprinting(boolean sprinting) {
|
||||
isSprinting = sprinting;
|
||||
}
|
||||
|
||||
public void refreshKeepAlive(long lastKeepAlive) {
|
||||
this.lastKeepAlive = lastKeepAlive;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
package fr.themode.minestom.entity.demo;
|
||||
|
||||
import fr.themode.minestom.entity.EntityCreature;
|
||||
import fr.themode.minestom.net.packet.server.play.EntityRelativeMovePacket;
|
||||
|
||||
public class ChickenCreature extends EntityCreature {
|
||||
|
||||
public ChickenCreature() {
|
||||
super(8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
//System.out.println("Update poulet");
|
||||
onGround = true;
|
||||
|
||||
double speed = 0.01;
|
||||
double newPos = getZ() + speed;
|
||||
|
||||
EntityRelativeMovePacket entityRelativeMovePacket = new EntityRelativeMovePacket();
|
||||
entityRelativeMovePacket.entityId = getEntityId();
|
||||
entityRelativeMovePacket.deltaZ = (short) ((newPos * 32 - getZ() * 32) * 128);
|
||||
entityRelativeMovePacket.onGround = true;
|
||||
getViewers().forEach(player -> player.getPlayerConnection().sendPacket(entityRelativeMovePacket));
|
||||
setZ(newPos);
|
||||
}
|
||||
}
|
|
@ -3,10 +3,7 @@ package fr.themode.minestom.net;
|
|||
import fr.themode.minestom.entity.Player;
|
||||
import fr.themode.minestom.net.player.PlayerConnection;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
public class ConnectionManager {
|
||||
|
||||
|
@ -17,6 +14,10 @@ public class ConnectionManager {
|
|||
return connectionPlayerMap.get(connection);
|
||||
}
|
||||
|
||||
public Collection<Player> getOnlinePlayers() {
|
||||
return Collections.unmodifiableCollection(connectionPlayerMap.values());
|
||||
}
|
||||
|
||||
// Is only used at LoginStartPacket#process
|
||||
public void createPlayer(PlayerConnection connection) {
|
||||
this.connectionPlayerMap.put(connection, new Player(connection));
|
||||
|
|
|
@ -15,5 +15,6 @@ public class ClientPlayPacketsHandler extends ClientPacketsHandler {
|
|||
register(0x13, ClientPlayerLookPacket.class);
|
||||
register(0x14, ClientPlayerPacket.class);
|
||||
register(0x2A, ClientAnimationPacket.class);
|
||||
register(0x1B, ClientEntityActionPacket.class);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ package fr.themode.minestom.net.packet.client.login;
|
|||
import fr.adamaq01.ozao.net.Buffer;
|
||||
import fr.adamaq01.ozao.net.packet.Packet;
|
||||
import fr.themode.minestom.entity.GameMode;
|
||||
import fr.themode.minestom.entity.Player;
|
||||
import fr.themode.minestom.entity.demo.ChickenCreature;
|
||||
import fr.themode.minestom.net.ConnectionManager;
|
||||
import fr.themode.minestom.net.ConnectionState;
|
||||
import fr.themode.minestom.net.packet.client.ClientPreplayPacket;
|
||||
|
@ -26,10 +28,12 @@ public class LoginStartPacket implements ClientPreplayPacket {
|
|||
|
||||
connection.setConnectionState(ConnectionState.PLAY);
|
||||
connectionManager.createPlayer(connection);
|
||||
Player player = connectionManager.getPlayer(connection);
|
||||
player.addToWorld();
|
||||
|
||||
// TODO complete login sequence with optionals packets
|
||||
JoinGamePacket joinGamePacket = new JoinGamePacket();
|
||||
joinGamePacket.entityId = 32;
|
||||
joinGamePacket.entityId = player.getEntityId();
|
||||
joinGamePacket.gameMode = GameMode.CREATIVE;
|
||||
joinGamePacket.dimension = Dimension.OVERWORLD;
|
||||
joinGamePacket.maxPlayers = 0;
|
||||
|
@ -71,6 +75,15 @@ public class LoginStartPacket implements ClientPreplayPacket {
|
|||
playerPositionAndLookPacket.flags = 0;
|
||||
playerPositionAndLookPacket.teleportId = 42;
|
||||
connection.sendPacket(playerPositionAndLookPacket);
|
||||
|
||||
for (int x = -20; x < 20; x++)
|
||||
for (int z = -20; z < 20; z++) {
|
||||
// TODO test entity
|
||||
ChickenCreature chickenCreature = new ChickenCreature();
|
||||
chickenCreature.setPosition(50 + (double) x * 1, 5, 50 + (double) z * 1);
|
||||
connectionManager.getOnlinePlayers().forEach(p -> chickenCreature.addViewer(p));
|
||||
chickenCreature.addToWorld();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -18,8 +18,7 @@ public class ClientAnimationPacket implements ClientPlayPacket {
|
|||
this.hand = Hand.values()[Utils.readVarInt(buffer)];
|
||||
}
|
||||
|
||||
public static enum Hand {
|
||||
|
||||
public enum Hand {
|
||||
MAIN,
|
||||
OFF
|
||||
}
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
package fr.themode.minestom.net.packet.client.play;
|
||||
|
||||
import fr.adamaq01.ozao.net.Buffer;
|
||||
import fr.themode.minestom.entity.Player;
|
||||
import fr.themode.minestom.net.packet.client.ClientPlayPacket;
|
||||
import fr.themode.minestom.utils.Utils;
|
||||
|
||||
public class ClientEntityActionPacket implements ClientPlayPacket {
|
||||
|
||||
public int playerId;
|
||||
public Action action;
|
||||
public int horseJumpBoost;
|
||||
|
||||
@Override
|
||||
public void process(Player player) {
|
||||
switch (action) {
|
||||
case START_SNEAKING:
|
||||
player.refreshSneaking(true);
|
||||
break;
|
||||
case STOP_SNEAKING:
|
||||
player.refreshSneaking(false);
|
||||
break;
|
||||
case START_SPRINTING:
|
||||
player.refreshSprinting(true);
|
||||
break;
|
||||
case STOP_SPRINTING:
|
||||
player.refreshSprinting(false);
|
||||
break;
|
||||
// TODO do remaining actions
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(Buffer buffer) {
|
||||
this.playerId = Utils.readVarInt(buffer);
|
||||
this.action = Action.values()[Utils.readVarInt(buffer)];
|
||||
this.horseJumpBoost = Utils.readVarInt(buffer);
|
||||
}
|
||||
|
||||
public enum Action {
|
||||
START_SNEAKING,
|
||||
STOP_SNEAKING,
|
||||
LEAVE_BED,
|
||||
START_SPRINTING,
|
||||
STOP_SPRINTING,
|
||||
START_JUMP_HORSE,
|
||||
STOP_JUMP_HORSE,
|
||||
OPEN_HORSE_INVENTORY,
|
||||
START_FLYING_ELYTRA;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package fr.themode.minestom.net.packet.server.play;
|
||||
|
||||
import fr.adamaq01.ozao.net.Buffer;
|
||||
import fr.themode.minestom.net.packet.server.ServerPacket;
|
||||
import fr.themode.minestom.utils.Utils;
|
||||
|
||||
public class EntityLookAndRelativeMovePacket implements ServerPacket {
|
||||
|
||||
public int entityId;
|
||||
public short deltaX, deltaY, deltaZ;
|
||||
public float yaw, pitch;
|
||||
public boolean onGround;
|
||||
|
||||
@Override
|
||||
public void write(Buffer buffer) {
|
||||
Utils.writeVarInt(buffer, entityId);
|
||||
buffer.putShort(deltaX);
|
||||
buffer.putShort(deltaY);
|
||||
buffer.putShort(deltaZ);
|
||||
buffer.putFloat(yaw);
|
||||
buffer.putFloat(pitch);
|
||||
buffer.putBoolean(onGround);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return 0x29;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package fr.themode.minestom.net.packet.server.play;
|
||||
|
||||
import fr.adamaq01.ozao.net.Buffer;
|
||||
import fr.themode.minestom.net.packet.server.ServerPacket;
|
||||
import fr.themode.minestom.utils.Utils;
|
||||
|
||||
public class EntityPacket implements ServerPacket {
|
||||
|
||||
public int entityId;
|
||||
|
||||
@Override
|
||||
public void write(Buffer buffer) {
|
||||
Utils.writeVarInt(buffer, entityId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return 0x2B;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package fr.themode.minestom.net.packet.server.play;
|
||||
|
||||
import fr.adamaq01.ozao.net.Buffer;
|
||||
import fr.themode.minestom.net.packet.server.ServerPacket;
|
||||
import fr.themode.minestom.utils.Utils;
|
||||
|
||||
public class EntityRelativeMovePacket implements ServerPacket {
|
||||
|
||||
public int entityId;
|
||||
public short deltaX, deltaY, deltaZ;
|
||||
public boolean onGround;
|
||||
|
||||
@Override
|
||||
public void write(Buffer buffer) {
|
||||
Utils.writeVarInt(buffer, entityId);
|
||||
buffer.putShort(deltaX);
|
||||
buffer.putShort(deltaY);
|
||||
buffer.putShort(deltaZ);
|
||||
buffer.putBoolean(onGround);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return 0x28;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package fr.themode.minestom.net.packet.server.play;
|
||||
|
||||
import fr.adamaq01.ozao.net.Buffer;
|
||||
import fr.themode.minestom.net.packet.server.ServerPacket;
|
||||
import fr.themode.minestom.utils.Utils;
|
||||
|
||||
public class EntityVelocityPacket implements ServerPacket {
|
||||
|
||||
public int entityId;
|
||||
public short velocityX, velocityY, velocityZ;
|
||||
|
||||
@Override
|
||||
public void write(Buffer buffer) {
|
||||
Utils.writeVarInt(buffer, entityId);
|
||||
buffer.putShort(velocityX);
|
||||
buffer.putShort(velocityY);
|
||||
buffer.putShort(velocityZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return 0x45;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package fr.themode.minestom.net.packet.server.play;
|
||||
|
||||
import fr.adamaq01.ozao.net.Buffer;
|
||||
import fr.themode.minestom.net.packet.server.ServerPacket;
|
||||
import fr.themode.minestom.utils.Utils;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class SpawnMobPacket implements ServerPacket {
|
||||
|
||||
public int entityId;
|
||||
public UUID entityUuid;
|
||||
public int entityType;
|
||||
public double x, y, z;
|
||||
public float yaw, pitch;
|
||||
public float headPitch;
|
||||
public short velocityX, velocityY, velocityZ;
|
||||
// TODO metadata
|
||||
|
||||
@Override
|
||||
public void write(Buffer buffer) {
|
||||
Utils.writeVarInt(buffer, entityId);
|
||||
buffer.putLong(entityUuid.getMostSignificantBits());
|
||||
buffer.putLong(entityUuid.getLeastSignificantBits());
|
||||
Utils.writeVarInt(buffer, entityType);
|
||||
buffer.putDouble(x);
|
||||
buffer.putDouble(y);
|
||||
buffer.putDouble(z);
|
||||
buffer.putFloat(yaw);
|
||||
buffer.putFloat(pitch);
|
||||
buffer.putFloat(headPitch);
|
||||
buffer.putShort(velocityX);
|
||||
buffer.putShort(velocityY);
|
||||
buffer.putShort(velocityZ);
|
||||
buffer.putByte((byte) 0xff); // TODO metadata
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return 0x03;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue