Inventory and block breaking/placing

This commit is contained in:
TheMode 2019-08-12 08:30:59 +02:00
parent 3b6b5676ed
commit e0172022b2
16 changed files with 429 additions and 35 deletions

View File

@ -4,10 +4,12 @@ import fr.themode.minestom.instance.Chunk;
import fr.themode.minestom.instance.Instance; import fr.themode.minestom.instance.Instance;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
public class Entity { public class Entity {
private static volatile int lastEntityId; private static AtomicInteger lastEntityId = new AtomicInteger();
protected Instance instance; protected Instance instance;
protected double lastX, lastY, lastZ; protected double lastX, lastY, lastZ;
protected double x, y, z; protected double x, y, z;
@ -22,7 +24,7 @@ public class Entity {
} }
private static int generateId() { private static int generateId() {
return ++lastEntityId; return lastEntityId.incrementAndGet();
} }
public int getEntityId() { public int getEntityId() {

View File

@ -18,7 +18,6 @@ public class EntityManager {
for (Instance instance : instanceManager.getInstances()) { for (Instance instance : instanceManager.getInstances()) {
// Creatures // Creatures
//long time = System.nanoTime();
for (EntityCreature creature : instance.getCreatures()) { for (EntityCreature creature : instance.getCreatures()) {
creaturesPool.submit(() -> { creaturesPool.submit(() -> {
boolean shouldRemove = creature.shouldRemove(); boolean shouldRemove = creature.shouldRemove();
@ -31,19 +30,16 @@ public class EntityManager {
} }
}); });
} }
/*creaturesPool.shutdown();
while (!creaturesPool.isTerminated()) {
}
System.out.println("delay: " + (System.nanoTime() - time));
creaturesPool = Executors.newFixedThreadPool(4);*/
// Players // Players
for (Player player : instance.getPlayers()) { for (Player player : instance.getPlayers()) {
playersPool.submit(() -> { playersPool.submit(() -> {
player.update();
boolean shouldRemove = player.shouldRemove(); boolean shouldRemove = player.shouldRemove();
if (shouldRemove) { if (!shouldRemove) {
player.update();
}
if (player.shouldRemove()) {
instance.removeEntity(player); instance.removeEntity(player);
} }
}); });

View File

@ -1,9 +1,9 @@
package fr.themode.minestom.entity; package fr.themode.minestom.entity;
import fr.themode.minestom.Main; import fr.themode.minestom.Main;
import fr.themode.minestom.net.packet.server.play.EntityTeleportPacket; import fr.themode.minestom.inventory.PlayerInventory;
import fr.themode.minestom.net.packet.server.play.PlayerPositionAndLookPacket; import fr.themode.minestom.item.ItemStack;
import fr.themode.minestom.net.packet.server.play.UpdateViewPositionPacket; import fr.themode.minestom.net.packet.server.play.*;
import fr.themode.minestom.net.player.PlayerConnection; import fr.themode.minestom.net.player.PlayerConnection;
import java.util.UUID; import java.util.UUID;
@ -18,11 +18,17 @@ public class Player extends LivingEntity {
private String username; private String username;
private PlayerConnection playerConnection; private PlayerConnection playerConnection;
private GameMode gameMode;
private PlayerInventory inventory;
private short heldSlot;
// TODO set proper UUID // TODO set proper UUID
public Player(UUID uuid, String username, PlayerConnection playerConnection) { public Player(UUID uuid, String username, PlayerConnection playerConnection) {
this.uuid = uuid; this.uuid = uuid;
this.username = username; this.username = username;
this.playerConnection = playerConnection; this.playerConnection = playerConnection;
this.inventory = new PlayerInventory(this);
} }
@Override @Override
@ -63,6 +69,43 @@ public class Player extends LivingEntity {
return playerConnection; return playerConnection;
} }
public PlayerInventory getInventory() {
return inventory;
}
public GameMode getGameMode() {
return gameMode;
}
public void setGameMode(GameMode gameMode) {
ChangeGameStatePacket changeGameStatePacket = new ChangeGameStatePacket();
changeGameStatePacket.reason = ChangeGameStatePacket.Reason.CHANGE_GAMEMODE;
changeGameStatePacket.value = gameMode.getId();
playerConnection.sendPacket(changeGameStatePacket);
refreshGameMode(gameMode);
}
public void setHeldItemSlot(short slot) {
if (slot < 0 || slot > 8)
throw new IllegalArgumentException("Slot has to be between 0 and 8");
HeldItemChangePacket heldItemChangePacket = new HeldItemChangePacket();
heldItemChangePacket.slot = slot;
playerConnection.sendPacket(heldItemChangePacket);
refreshHeldSlot(slot);
}
public short getHeldSlot() {
return heldSlot;
}
public ItemStack getHeldItemStack() {
return inventory.getItemStack(heldSlot);
}
public void refreshGameMode(GameMode gameMode) {
this.gameMode = gameMode;
}
public void refreshView(float yaw, float pitch) { public void refreshView(float yaw, float pitch) {
this.yaw = yaw; this.yaw = yaw;
this.pitch = pitch; this.pitch = pitch;
@ -84,6 +127,10 @@ public class Player extends LivingEntity {
this.lastKeepAlive = lastKeepAlive; this.lastKeepAlive = lastKeepAlive;
} }
public void refreshHeldSlot(short slot) {
this.heldSlot = slot;
}
public long getLastKeepAlive() { public long getLastKeepAlive() {
return lastKeepAlive; return lastKeepAlive;
} }

View File

@ -3,18 +3,19 @@ package fr.themode.minestom.instance;
import fr.themode.minestom.entity.Entity; import fr.themode.minestom.entity.Entity;
import fr.themode.minestom.entity.EntityCreature; import fr.themode.minestom.entity.EntityCreature;
import fr.themode.minestom.entity.Player; import fr.themode.minestom.entity.Player;
import fr.themode.minestom.net.packet.server.play.ChunkDataPacket;
import fr.themode.minestom.utils.GroupedCollections; import fr.themode.minestom.utils.GroupedCollections;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
public class Instance { public class Instance {
private UUID uniqueId; private UUID uniqueId;
private Set<Chunk> chunksSet = Collections.synchronizedSet(new HashSet<>()); private Set<Chunk> chunksSet = new CopyOnWriteArraySet<>();
public Instance(UUID uniqueId) { public Instance(UUID uniqueId) {
this.uniqueId = uniqueId; this.uniqueId = uniqueId;
@ -30,6 +31,7 @@ public class Instance {
} }
synchronized (chunk) { synchronized (chunk) {
chunk.setBlock(x % 16, y, z % 16, block); chunk.setBlock(x % 16, y, z % 16, block);
sendChunkUpdate(chunk); // TODO partial chunk data
} }
} }
@ -61,7 +63,9 @@ public class Instance {
// TODO based on distance with players // TODO based on distance with players
getPlayers().forEach(p -> ((EntityCreature) entity).addViewer(p)); getPlayers().forEach(p -> ((EntityCreature) entity).addViewer(p));
} else if (entity instanceof Player) { } else if (entity instanceof Player) {
getCreatures().forEach(entityCreature -> entityCreature.addViewer((Player) entity)); Player player = (Player) entity;
sendChunks(player);
getCreatures().forEach(entityCreature -> entityCreature.addViewer(player));
} }
Chunk chunk = getChunkAt(entity.getX(), entity.getZ()); Chunk chunk = getChunkAt(entity.getX(), entity.getZ());
@ -107,4 +111,20 @@ public class Instance {
this.chunksSet.add(chunk); this.chunksSet.add(chunk);
return chunk; return chunk;
} }
private void sendChunks(Player player) {
ChunkDataPacket chunkDataPacket = new ChunkDataPacket();
chunkDataPacket.fullChunk = true;
for (Chunk chunk : getChunks()) {
chunkDataPacket.chunk = chunk;
player.getPlayerConnection().sendPacket(chunkDataPacket);
}
}
private void sendChunkUpdate(Chunk chunk) {
ChunkDataPacket chunkDataPacket = new ChunkDataPacket();
chunkDataPacket.fullChunk = true;
chunkDataPacket.chunk = chunk;
getPlayers().forEach(player -> player.getPlayerConnection().sendPacket(chunkDataPacket));
}
} }

View File

@ -0,0 +1,62 @@
package fr.themode.minestom.inventory;
import fr.themode.minestom.entity.Player;
import fr.themode.minestom.item.ItemStack;
import fr.themode.minestom.net.packet.server.play.WindowItemsPacket;
import fr.themode.minestom.net.player.PlayerConnection;
public class PlayerInventory {
private Player player;
private ItemStack[] items = new ItemStack[45];
public PlayerInventory(Player player) {
this.player = player;
}
public ItemStack getItemStack(int slot) {
ItemStack item = this.items[convertSlot(slot)];
return item != null ? item : ItemStack.AIR_ITEM;
}
public void setItemStack(int slot, ItemStack itemStack) {
this.items[convertSlot(slot)] = itemStack;
}
public void setHelmet(ItemStack itemStack) {
this.items[5] = itemStack;
}
public void setChestplate(ItemStack itemStack) {
this.items[6] = itemStack;
}
public void setLeggings(ItemStack itemStack) {
this.items[7] = itemStack;
}
public void setBoots(ItemStack itemStack) {
this.items[8] = itemStack;
}
public void update() {
PlayerConnection playerConnection = player.getPlayerConnection();
WindowItemsPacket windowItemsPacket = new WindowItemsPacket();
windowItemsPacket.windowId = 0; // player inventory ID
windowItemsPacket.count = (short) items.length;
windowItemsPacket.items = items;
playerConnection.sendPacket(windowItemsPacket);
}
private int convertSlot(int slot) {
if (slot >= 0 && slot <= 9 * 4) {
int row = slot / 9;
int place = slot % 9;
int converted = 9 * (4 - 1 - row) + place + 9;
return converted;
}
return slot;
}
}

View File

@ -0,0 +1,22 @@
package fr.themode.minestom.item;
public class ItemStack {
public static final ItemStack AIR_ITEM = new ItemStack(0, (byte) 1);
private int itemId;
private byte count;
public ItemStack(int itemId, byte count) {
this.itemId = itemId;
this.count = count;
}
public int getItemId() {
return itemId;
}
public byte getCount() {
return count;
}
}

View File

@ -18,5 +18,8 @@ public class ClientPlayPacketsHandler extends ClientPacketsHandler {
register(0x1B, ClientEntityActionPacket.class); register(0x1B, ClientEntityActionPacket.class);
register(0x0E, ClientUseEntityPacket.class); register(0x0E, ClientUseEntityPacket.class);
register(0x03, ClientChatMessagePacket.class); register(0x03, ClientChatMessagePacket.class);
register(0x1A, ClientPlayerDiggingPacket.class);
register(0x2C, ClientPlayerBlockPlacementPacket.class);
register(0x23, ClientHeldItemChangePacket.class);
} }
} }

View File

@ -6,14 +6,18 @@ import fr.themode.minestom.entity.GameMode;
import fr.themode.minestom.entity.Player; import fr.themode.minestom.entity.Player;
import fr.themode.minestom.entity.demo.ChickenCreature; import fr.themode.minestom.entity.demo.ChickenCreature;
import fr.themode.minestom.instance.Block; import fr.themode.minestom.instance.Block;
import fr.themode.minestom.instance.Chunk;
import fr.themode.minestom.instance.Instance; import fr.themode.minestom.instance.Instance;
import fr.themode.minestom.inventory.PlayerInventory;
import fr.themode.minestom.item.ItemStack;
import fr.themode.minestom.net.ConnectionManager; import fr.themode.minestom.net.ConnectionManager;
import fr.themode.minestom.net.ConnectionState; import fr.themode.minestom.net.ConnectionState;
import fr.themode.minestom.net.packet.client.ClientPreplayPacket; import fr.themode.minestom.net.packet.client.ClientPreplayPacket;
import fr.themode.minestom.net.packet.server.login.JoinGamePacket; import fr.themode.minestom.net.packet.server.login.JoinGamePacket;
import fr.themode.minestom.net.packet.server.login.LoginSuccessPacket; import fr.themode.minestom.net.packet.server.login.LoginSuccessPacket;
import fr.themode.minestom.net.packet.server.play.*; import fr.themode.minestom.net.packet.server.play.PlayerInfoPacket;
import fr.themode.minestom.net.packet.server.play.PlayerPositionAndLookPacket;
import fr.themode.minestom.net.packet.server.play.SpawnPlayerPacket;
import fr.themode.minestom.net.packet.server.play.SpawnPositionPacket;
import fr.themode.minestom.net.player.PlayerConnection; import fr.themode.minestom.net.player.PlayerConnection;
import fr.themode.minestom.utils.Utils; import fr.themode.minestom.utils.Utils;
import fr.themode.minestom.world.Dimension; import fr.themode.minestom.world.Dimension;
@ -32,7 +36,7 @@ public class LoginStartPacket implements ClientPreplayPacket {
instance = Main.getInstanceManager().createInstance(); instance = Main.getInstanceManager().createInstance();
for (int x = -64; x < 64; x++) for (int x = -64; x < 64; x++)
for (int z = -64; z < 64; z++) { for (int z = -64; z < 64; z++) {
instance.setBlock(x, 4, z, new Block(1)); instance.setBlock(x, 4, z, new Block(10));
} }
} }
@ -52,11 +56,14 @@ public class LoginStartPacket implements ClientPreplayPacket {
connection.setConnectionState(ConnectionState.PLAY); connection.setConnectionState(ConnectionState.PLAY);
connectionManager.createPlayer(uuids.get(username), username, connection); connectionManager.createPlayer(uuids.get(username), username, connection);
Player player = connectionManager.getPlayer(connection); Player player = connectionManager.getPlayer(connection);
GameMode gameMode = GameMode.CREATIVE;
player.refreshGameMode(gameMode);
// TODO complete login sequence with optionals packets // TODO complete login sequence with optionals packets
JoinGamePacket joinGamePacket = new JoinGamePacket(); JoinGamePacket joinGamePacket = new JoinGamePacket();
joinGamePacket.entityId = player.getEntityId(); joinGamePacket.entityId = player.getEntityId();
joinGamePacket.gameMode = GameMode.CREATIVE; joinGamePacket.gameMode = gameMode;
joinGamePacket.dimension = Dimension.OVERWORLD; joinGamePacket.dimension = Dimension.OVERWORLD;
joinGamePacket.maxPlayers = 0; // Unused joinGamePacket.maxPlayers = 0; // Unused
joinGamePacket.levelType = "default"; joinGamePacket.levelType = "default";
@ -71,12 +78,6 @@ public class LoginStartPacket implements ClientPreplayPacket {
// TODO player abilities // TODO player abilities
player.setInstance(instance); player.setInstance(instance);
ChunkDataPacket chunkDataPacket = new ChunkDataPacket();
chunkDataPacket.fullChunk = true;
for (Chunk chunk : instance.getChunks()) {
chunkDataPacket.chunk = chunk;
connection.sendPacket(chunkDataPacket);
}
SpawnPositionPacket spawnPositionPacket = new SpawnPositionPacket(); SpawnPositionPacket spawnPositionPacket = new SpawnPositionPacket();
@ -102,13 +103,12 @@ public class LoginStartPacket implements ClientPreplayPacket {
playerInfoPacket.playerInfos.add(addPlayer); playerInfoPacket.playerInfos.add(addPlayer);
connection.sendPacket(playerInfoPacket); connection.sendPacket(playerInfoPacket);
for (int x = -10; x < 10; x++) for (int x = -5; x < 5; x++)
for (int z = -10; z < 10; z++) { for (int z = -5; z < 5; z++) {
// TODO test entity // TODO test entity
ChickenCreature chickenCreature = new ChickenCreature(); ChickenCreature chickenCreature = new ChickenCreature();
chickenCreature.refreshPosition(0 + (double) x * 1, 5, 0 + (double) z * 1); chickenCreature.refreshPosition(0 + (double) x * 1, 5, 0 + (double) z * 1);
chickenCreature.setInstance(instance); chickenCreature.setInstance(instance);
//instance.addEntity(chickenCreature);
} }
@ -138,7 +138,12 @@ public class LoginStartPacket implements ClientPreplayPacket {
connection.sendPacket(pInfoPacket); connection.sendPacket(pInfoPacket);
connection.sendPacket(spawnPacket); connection.sendPacket(spawnPacket);
} }
System.out.println("HAHAHAHHAHHAH " + player.getUuid()); //System.out.println("HAHAHAHHAHHAH " + player.getUuid());
PlayerInventory inventory = player.getInventory();
inventory.setItemStack(1, new ItemStack(1, (byte) 1));
inventory.update();
} }
@Override @Override

View File

@ -0,0 +1,22 @@
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;
public class ClientHeldItemChangePacket implements ClientPlayPacket {
public short slot;
@Override
public void process(Player player) {
if (slot < 0 || slot > 8)
return;
player.refreshHeldSlot(slot);
}
@Override
public void read(Buffer buffer) {
buffer.getShort();
}
}

View File

@ -0,0 +1,43 @@
package fr.themode.minestom.net.packet.client.play;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.entity.Player;
import fr.themode.minestom.instance.Block;
import fr.themode.minestom.instance.Instance;
import fr.themode.minestom.net.packet.client.ClientPlayPacket;
import fr.themode.minestom.utils.Position;
import fr.themode.minestom.utils.Utils;
public class ClientPlayerBlockPlacementPacket implements ClientPlayPacket {
public Hand hand;
public Position position;
public ClientPlayerDiggingPacket.BlockFace blockFace;
public float cursorPositionX, cursorPositionY, cursorPositionZ;
public boolean insideBlock;
@Override
public void process(Player player) {
Instance instance = player.getInstance();
if (instance == null)
return;
instance.setBlock(position.getX(), position.getY(), position.getZ(), new Block(2));
}
@Override
public void read(Buffer buffer) {
this.hand = Hand.values()[Utils.readVarInt(buffer)];
this.position = Utils.readPosition(buffer);
this.blockFace = ClientPlayerDiggingPacket.BlockFace.values()[Utils.readVarInt(buffer)];
this.cursorPositionX = buffer.getFloat();
this.cursorPositionY = buffer.getFloat();
this.cursorPositionZ = buffer.getFloat();
this.insideBlock = buffer.getBoolean();
}
public enum Hand {
MAIN_HAND,
OFF_HAND;
}
}

View File

@ -0,0 +1,58 @@
package fr.themode.minestom.net.packet.client.play;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.entity.GameMode;
import fr.themode.minestom.entity.Player;
import fr.themode.minestom.instance.Block;
import fr.themode.minestom.instance.Instance;
import fr.themode.minestom.net.packet.client.ClientPlayPacket;
import fr.themode.minestom.utils.Position;
import fr.themode.minestom.utils.Utils;
public class ClientPlayerDiggingPacket implements ClientPlayPacket {
public Status status;
public Position position;
public BlockFace blockFace;
@Override
public void process(Player player) {
switch (status) {
case STARTED_DIGGING:
if (player.getGameMode() == GameMode.CREATIVE) {
Instance instance = player.getInstance();
if (instance != null) {
instance.setBlock(position.getX(), position.getY(), position.getZ(), new Block(0));
}
}
break;
}
}
@Override
public void read(Buffer buffer) {
this.status = Status.values()[Utils.readVarInt(buffer)];
this.position = Utils.readPosition(buffer);
this.blockFace = BlockFace.values()[Utils.readVarInt(buffer)];
}
public enum Status {
STARTED_DIGGING,
CANCELLED_DIGGING,
FINISHED_DIGGING,
DROP_ITEM_STACK,
DROP_ITEM,
UPDATE_ITEM_STATE,
SWAP_ITEM_HAND;
}
public enum BlockFace {
BOTTOM,
TOP,
NORTH,
SOUTH,
WEST,
EAST;
}
}

View File

@ -0,0 +1,36 @@
package fr.themode.minestom.net.packet.server.play;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.net.packet.server.ServerPacket;
public class ChangeGameStatePacket implements ServerPacket {
public Reason reason;
public float value;
@Override
public void write(Buffer buffer) {
buffer.putByte((byte) reason.ordinal());
buffer.putFloat(value);
}
@Override
public int getId() {
return 0x1E;
}
public enum Reason {
INVALID_BED,
END_RAINING,
BEGIN_RAINING,
CHANGE_GAMEMODE,
EXIT_END,
DEMO_MESSAGE,
ARROW_HITTING_PLAYER,
FADE_VALUE,
FADE_TIME,
PLAY_PUFFERFISH_STING_SOUND,
PLAYER_ELDER_GUARDIAN_MOB_APPEARANCE;
}
}

View File

@ -0,0 +1,19 @@
package fr.themode.minestom.net.packet.server.play;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.net.packet.server.ServerPacket;
public class HeldItemChangePacket implements ServerPacket {
public short slot;
@Override
public void write(Buffer buffer) {
buffer.putShort(slot);
}
@Override
public int getId() {
return 0x3F;
}
}

View File

@ -1,12 +1,15 @@
package fr.themode.minestom.net.packet.server.play; package fr.themode.minestom.net.packet.server.play;
import fr.adamaq01.ozao.net.Buffer; import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.item.ItemStack;
import fr.themode.minestom.net.packet.server.ServerPacket; import fr.themode.minestom.net.packet.server.ServerPacket;
import fr.themode.minestom.utils.Utils;
public class WindowItemsPacket implements ServerPacket { public class WindowItemsPacket implements ServerPacket {
public byte windowId; public byte windowId;
public short count; public short count;
public ItemStack[] items;
// TODO slot data (Array of Slot) // TODO slot data (Array of Slot)
@ -14,12 +17,24 @@ public class WindowItemsPacket implements ServerPacket {
public void write(Buffer buffer) { public void write(Buffer buffer) {
buffer.putByte(windowId); buffer.putByte(windowId);
buffer.putShort(count); buffer.putShort(count);
// TODO replace with actual array of slot
buffer.putBoolean(false); // Not present if (items == null)
return;
for (int i = 0; i < items.length; i++) {
ItemStack item = items[i];
if (item == null) {
buffer.putBoolean(false);
} else {
buffer.putBoolean(true);
Utils.writeVarInt(buffer, item.getItemId());
buffer.putByte(item.getCount());
buffer.putByte((byte) 0); // End nbt TODO
}
}
} }
@Override @Override
public int getId() { public int getId() {
return 0x15; return 0x14;
} }
} }

View File

@ -0,0 +1,36 @@
package fr.themode.minestom.utils;
public class Position {
private int x, y, z;
public Position(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public int getZ() {
return z;
}
public void setZ(int z) {
this.z = z;
}
}

View File

@ -121,7 +121,15 @@ public class Utils {
} }
public static void writePosition(Buffer buffer, int x, int y, int z) { public static void writePosition(Buffer buffer, int x, int y, int z) {
buffer.putLong(((x & 0x3FFFFFF) << 38) | ((y & 0xFFF) << 26) | (z & 0x3FFFFFF)); buffer.putLong(((x & 0x3FFFFFF) << 38) | ((z & 0x3FFFFFF) << 12) | (y & 0xFFF));
}
public static Position readPosition(Buffer buffer) {
long val = buffer.getLong();
int x = (int) (val >> 38);
int y = (int) (val & 0xFFF);
int z = (int) (val << 26 >> 38);
return new Position(x, y, z);
} }
public static void writeBlocks(Buffer buffer, Block[] blocks, int bitsPerEntry) { public static void writeBlocks(Buffer buffer, Block[] blocks, int bitsPerEntry) {