Multiplayer synchronization

This commit is contained in:
TheMode 2019-08-20 22:40:57 +02:00
parent 729d82abd4
commit 9d8e4e7ea2
18 changed files with 505 additions and 177 deletions

View File

@ -8,14 +8,13 @@ import fr.adamaq01.ozao.net.server.backend.tcp.TCPServer;
import fr.themode.minestom.entity.EntityManager;
import fr.themode.minestom.entity.Player;
import fr.themode.minestom.instance.BlockManager;
import fr.themode.minestom.instance.Instance;
import fr.themode.minestom.instance.InstanceManager;
import fr.themode.minestom.instance.demo.StoneBlock;
import fr.themode.minestom.listener.PacketListenerManager;
import fr.themode.minestom.net.ConnectionManager;
import fr.themode.minestom.net.PacketProcessor;
import fr.themode.minestom.net.packet.server.play.DestroyEntitiesPacket;
import fr.themode.minestom.net.packet.server.play.KeepAlivePacket;
import fr.themode.minestom.net.packet.server.play.PlayerInfoPacket;
import fr.themode.minestom.net.protocol.MinecraftProtocol;
import java.lang.reflect.InvocationTargetException;
@ -59,18 +58,13 @@ public class Main {
if (connectionManager.getPlayer(packetProcessor.getPlayerConnection(connection)) != null) {
Player player = connectionManager.getPlayer(packetProcessor.getPlayerConnection(connection));
player.remove();
connectionManager.removePlayer(packetProcessor.getPlayerConnection(connection));
PlayerInfoPacket playerInfoPacket = new PlayerInfoPacket(PlayerInfoPacket.Action.REMOVE_PLAYER);
playerInfoPacket.playerInfos.add(new PlayerInfoPacket.RemovePlayer(player.getUuid()));
DestroyEntitiesPacket destroyEntitiesPacket = new DestroyEntitiesPacket();
destroyEntitiesPacket.entityIds = new int[]{player.getEntityId()};
for (Player onlinePlayer : connectionManager.getOnlinePlayers()) {
if (!onlinePlayer.equals(player)) {
onlinePlayer.getPlayerConnection().sendPacket(destroyEntitiesPacket);
onlinePlayer.getPlayerConnection().sendPacket(playerInfoPacket);
}
Instance instance = player.getInstance();
if (instance != null) {
instance.removeEntity(player);
}
connectionManager.removePlayer(packetProcessor.getPlayerConnection(connection));
}
packetProcessor.removePlayerConnection(connection);
}

View File

@ -9,6 +9,8 @@ import fr.themode.minestom.event.Event;
import fr.themode.minestom.instance.Chunk;
import fr.themode.minestom.instance.Instance;
import fr.themode.minestom.net.packet.server.ServerPacket;
import fr.themode.minestom.net.packet.server.play.DestroyEntitiesPacket;
import fr.themode.minestom.net.packet.server.play.EntityMetaDataPacket;
import fr.themode.minestom.net.packet.server.play.SetPassengersPacket;
import fr.themode.minestom.utils.Utils;
@ -38,6 +40,7 @@ public abstract class Entity implements Viewable {
protected double lastX, lastY, lastZ;
protected double x, y, z;
protected float yaw, pitch;
protected float lastYaw, lastPitch;
private int id;
protected Entity vehicle;
@ -90,7 +93,9 @@ public abstract class Entity implements Viewable {
if (!viewers.contains(player))
return;
this.viewers.remove(player);
// TODO send packet to remove entity
DestroyEntitiesPacket destroyEntitiesPacket = new DestroyEntitiesPacket();
destroyEntitiesPacket.entityIds = new int[]{getEntityId()};
player.getPlayerConnection().sendPacket(destroyEntitiesPacket);
}
}
@ -183,6 +188,11 @@ public abstract class Entity implements Viewable {
return Collections.unmodifiableSet(passengers);
}
public void setOnFire(boolean fire) {
this.onFire = fire;
sendMetadata(0);
}
public void refreshPosition(double x, double y, double z) {
this.lastX = this.x;
this.lastY = this.y;
@ -206,6 +216,23 @@ public abstract class Entity implements Viewable {
}
}
public void refreshView(float yaw, float pitch) {
this.lastYaw = this.yaw;
this.lastPitch = this.pitch;
this.yaw = yaw;
this.pitch = pitch;
}
public void refreshSneaking(boolean sneaking) {
this.crouched = sneaking;
sendMetadata(0);
}
public void refreshSprinting(boolean sprinting) {
this.sprinting = sprinting;
sendMetadata(0);
}
public double getX() {
return x;
}
@ -230,30 +257,36 @@ public abstract class Entity implements Viewable {
this.shouldRemove = true;
}
protected void sendPacketToViewers(ServerPacket packet) {
public void sendPacketToViewers(ServerPacket packet) {
getViewers().forEach(player -> player.getPlayerConnection().sendPacket(packet));
}
public void sendPacketsToViewers(ServerPacket... packets) {
getViewers().forEach(player -> {
for (ServerPacket packet : packets)
player.getPlayerConnection().sendPacket(packet);
});
}
public Buffer getMetadataBuffer() {
Buffer buffer = Buffer.create();
fillMetadataIndex(buffer, 0);
/*Utils.writeVarInt(buffer, 0);
Utils.writeString(buffer, customName);
buffer.putBoolean(silent);
buffer.putBoolean(noGravity);
Utils.writeVarInt(buffer, pose.ordinal());*/
// Chicken test
/*buffer.putByte((byte) 0); // Hand states
buffer.putFloat(10f); // Health
Utils.writeVarInt(buffer, 0); // Potion effect color
buffer.putBoolean(false); // Potion effect ambient
Utils.writeVarInt(buffer, 0); // Arrow count in entity
buffer.putByte((byte) 0); // (Insentient)
buffer.putBoolean(false); // baby (Ageable)*/
return buffer;
}
protected void sendMetadata(int index) {
Buffer buffer = Buffer.create();
fillMetadataIndex(buffer, index);
EntityMetaDataPacket metaDataPacket = new EntityMetaDataPacket();
metaDataPacket.entityId = getEntityId();
metaDataPacket.data = buffer;
sendPacketToViewers(metaDataPacket);
if (this instanceof Player) {
((Player) this).getPlayerConnection().sendPacket(metaDataPacket);
}
}
private void fillMetadataIndex(Buffer buffer, int index) {
switch (index) {
case 0:

View File

@ -1,9 +1,7 @@
package fr.themode.minestom.entity;
import fr.themode.minestom.Main;
import fr.themode.minestom.bossbar.BossBar;
import fr.themode.minestom.chat.Chat;
import fr.themode.minestom.event.BlockPlaceEvent;
import fr.themode.minestom.event.PickupItemEvent;
import fr.themode.minestom.instance.Chunk;
import fr.themode.minestom.instance.CustomBlock;
@ -11,14 +9,15 @@ import fr.themode.minestom.inventory.Inventory;
import fr.themode.minestom.inventory.PlayerInventory;
import fr.themode.minestom.item.ItemStack;
import fr.themode.minestom.net.packet.client.ClientPlayPacket;
import fr.themode.minestom.net.packet.server.ServerPacket;
import fr.themode.minestom.net.packet.server.play.*;
import fr.themode.minestom.net.player.PlayerConnection;
import fr.themode.minestom.utils.Position;
import java.util.Collections;
import java.util.LinkedList;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArraySet;
public class Player extends LivingEntity {
@ -27,7 +26,7 @@ public class Player extends LivingEntity {
private String username;
private PlayerConnection playerConnection;
private ConcurrentLinkedQueue<ClientPlayPacket> packets = new ConcurrentLinkedQueue<>();
private LinkedList<ClientPlayPacket> packets = new LinkedList<>();
private GameMode gameMode;
private PlayerInventory inventory;
@ -40,13 +39,16 @@ public class Player extends LivingEntity {
private Set<BossBar> bossBars = new CopyOnWriteArraySet<>();
// Synchronization
private long synchronizationDelay = 1000; // In ms
private long lastSynchronizationTime;
// Vehicle
private float sideways;
private float forward;
// TODO set proper UUID
public Player(UUID uuid, String username, PlayerConnection playerConnection) {
super(93); // TODO correct ?
super(93); // FIXME verify
this.uuid = uuid;
this.username = username;
this.playerConnection = playerConnection;
@ -65,17 +67,19 @@ public class Player extends LivingEntity {
sendMessage("Cancelled: " + cancel);
});*/
setEventCallback(BlockPlaceEvent.class, event -> {
/*setEventCallback(BlockPlaceEvent.class, event -> {
event.setCancelled(true);
sendMessage("CANCELLED");
});
});*/
}
@Override
public void update() {
while (!packets.isEmpty()) {
ClientPlayPacket packet = packets.remove();
packet.process(this);
synchronized (packets) {
while (!packets.isEmpty()) {
ClientPlayPacket packet = packets.pollFirst();
packet.process(this);
}
}
// Target block stage
@ -86,15 +90,14 @@ public class Player extends LivingEntity {
byte stage = (byte) (since / (timeBreak / animationCount));
sendBlockBreakAnimation(targetBlockPosition, stage);// TODO send to all near players
if (stage > 9) {
instance.setBlock(targetBlockPosition.getX(), targetBlockPosition.getY(), targetBlockPosition.getZ(), (short) 0);
testParticle(targetBlockPosition.getX() + 0.5f, targetBlockPosition.getY(), targetBlockPosition.getZ() + 0.5f, targetCustomBlock.getType());
instance.breakBlock(this, targetBlockPosition, targetCustomBlock);
resetTargetBlock();
}
}
// Item pickup
if (instance != null) {
Chunk chunk = instance.getChunkAt(getX(), getZ());
Chunk chunk = instance.getChunkAt(getX(), getZ()); // TODO check surrounding chunks
Set<ObjectEntity> objectEntities = chunk.getObjectEntities();
for (ObjectEntity objectEntity : objectEntities) {
if (objectEntity instanceof ItemEntity) {
@ -125,19 +128,117 @@ public class Player extends LivingEntity {
// Multiplayer sync
EntityTeleportPacket entityTeleportPacket = new EntityTeleportPacket();
entityTeleportPacket.entityId = getEntityId();
entityTeleportPacket.x = x;
entityTeleportPacket.y = y;
entityTeleportPacket.z = z;
entityTeleportPacket.yaw = yaw;
entityTeleportPacket.pitch = pitch;
entityTeleportPacket.onGround = true;
for (Player onlinePlayer : Main.getConnectionManager().getOnlinePlayers()) {
if (!onlinePlayer.equals(this))
onlinePlayer.getPlayerConnection().sendPacket(entityTeleportPacket);
boolean positionChanged = x != lastX || z != lastZ || y != lastY;
boolean viewChanged = yaw != lastYaw || pitch != lastPitch;
ServerPacket updatePacket = null;
ServerPacket optionalUpdatePacket = null;
if (positionChanged && viewChanged) {
EntityLookAndRelativeMovePacket entityLookAndRelativeMovePacket = new EntityLookAndRelativeMovePacket();
entityLookAndRelativeMovePacket.entityId = getEntityId();
entityLookAndRelativeMovePacket.deltaX = (short) ((x * 32 - lastX * 32) * 128);
entityLookAndRelativeMovePacket.deltaY = (short) ((y * 32 - lastY * 32) * 128);
entityLookAndRelativeMovePacket.deltaZ = (short) ((z * 32 - lastZ * 32) * 128);
entityLookAndRelativeMovePacket.yaw = yaw;
entityLookAndRelativeMovePacket.pitch = pitch;
entityLookAndRelativeMovePacket.onGround = onGround;
EntityHeadLookPacket entityHeadLookPacket = new EntityHeadLookPacket();
entityHeadLookPacket.entityId = getEntityId();
entityHeadLookPacket.yaw = yaw;
lastX = x;
lastY = y;
lastZ = z;
lastYaw = yaw;
lastPitch = pitch;
updatePacket = entityLookAndRelativeMovePacket;
optionalUpdatePacket = entityHeadLookPacket;
} else if (positionChanged) {
EntityRelativeMovePacket entityRelativeMovePacket = new EntityRelativeMovePacket();
entityRelativeMovePacket.entityId = getEntityId();
entityRelativeMovePacket.deltaX = (short) ((x * 32 - lastX * 32) * 128);
entityRelativeMovePacket.deltaY = (short) ((y * 32 - lastY * 32) * 128);
entityRelativeMovePacket.deltaZ = (short) ((z * 32 - lastZ * 32) * 128);
entityRelativeMovePacket.onGround = onGround;
lastX = x;
lastY = y;
lastZ = z;
updatePacket = entityRelativeMovePacket;
} else if (viewChanged) {
EntityLookPacket entityLookPacket = new EntityLookPacket();
entityLookPacket.entityId = getEntityId();
entityLookPacket.yaw = yaw;
entityLookPacket.pitch = pitch;
entityLookPacket.onGround = onGround;
EntityHeadLookPacket entityHeadLookPacket = new EntityHeadLookPacket();
entityHeadLookPacket.entityId = getEntityId();
entityHeadLookPacket.yaw = yaw;
lastYaw = yaw;
lastPitch = pitch;
updatePacket = entityLookPacket;
optionalUpdatePacket = entityHeadLookPacket;
}
if (updatePacket != null) {
if (optionalUpdatePacket != null) {
sendPacketsToViewers(updatePacket, optionalUpdatePacket);
} else {
sendPacketToViewers(updatePacket);
}
}
playerConnection.sendPacket(new UpdateViewPositionPacket(Math.floorDiv((int) x, 16), Math.floorDiv((int) z, 16)));
// Synchronization
long time = System.currentTimeMillis();
if (time - lastSynchronizationTime >= synchronizationDelay) {
lastSynchronizationTime = System.currentTimeMillis();
for (Player viewer : getViewers()) {
EntityTeleportPacket teleportPacket = new EntityTeleportPacket();
teleportPacket.entityId = viewer.getEntityId();
teleportPacket.x = viewer.getX();
teleportPacket.y = viewer.getY();
teleportPacket.z = viewer.getZ();
teleportPacket.onGround = viewer.onGround;
playerConnection.sendPacket(teleportPacket);
}
}
}
@Override
public void addViewer(Player player) {
super.addViewer(player);
PlayerConnection connection = player.getPlayerConnection();
String property = "eyJ0aW1lc3RhbXAiOjE1NjU0ODMwODQwOTYsInByb2ZpbGVJZCI6ImFiNzBlY2I0MjM0NjRjMTRhNTJkN2EwOTE1MDdjMjRlIiwicHJvZmlsZU5hbWUiOiJUaGVNb2RlOTExIiwidGV4dHVyZXMiOnsiU0tJTiI6eyJ1cmwiOiJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlL2RkOTE2NzJiNTE0MmJhN2Y3MjA2ZTRjN2IwOTBkNzhlM2Y1ZDc2NDdiNWFmZDIyNjFhZDk4OGM0MWI2ZjcwYTEifX19";
SpawnPlayerPacket spawnPlayerPacket = new SpawnPlayerPacket();
spawnPlayerPacket.entityId = getEntityId();
spawnPlayerPacket.playerUuid = getUuid();
spawnPlayerPacket.x = x;
spawnPlayerPacket.y = y;
spawnPlayerPacket.z = z;
spawnPlayerPacket.yaw = yaw;
spawnPlayerPacket.pitch = pitch;
PlayerInfoPacket pInfoPacket = new PlayerInfoPacket(PlayerInfoPacket.Action.ADD_PLAYER);
PlayerInfoPacket.AddPlayer addP = new PlayerInfoPacket.AddPlayer(getUuid(), getUsername(), GameMode.CREATIVE, 10);
PlayerInfoPacket.AddPlayer.Property p = new PlayerInfoPacket.AddPlayer.Property("textures", property);//new PlayerInfoPacket.AddPlayer.Property("textures", properties.get(onlinePlayer.getUsername()));
addP.properties.add(p);
pInfoPacket.playerInfos.add(addP);
connection.sendPacket(pInfoPacket);
connection.sendPacket(spawnPlayerPacket);
for (EntityEquipmentPacket.Slot slot : EntityEquipmentPacket.Slot.values()) {
syncEquipment(slot); // TODO only send packets to "player" and not all viewers
}
}
@Override
public void removeViewer(Player player) {
super.removeViewer(player);
PlayerInfoPacket playerInfoPacket = new PlayerInfoPacket(PlayerInfoPacket.Action.REMOVE_PLAYER);
playerInfoPacket.playerInfos.add(new PlayerInfoPacket.RemovePlayer(getUuid()));
player.playerConnection.sendPacket(playerInfoPacket);
}
public void sendBlockBreakAnimation(Position blockPosition, byte destroyStage) {
@ -146,22 +247,7 @@ public class Player extends LivingEntity {
breakAnimationPacket.blockPosition = blockPosition;
breakAnimationPacket.destroyStage = destroyStage;
playerConnection.sendPacket(breakAnimationPacket);
}
private void testParticle(float x, float y, float z, int blockId) {
ParticlePacket particlePacket = new ParticlePacket();
particlePacket.particleId = 3; // Block particle
particlePacket.longDistance = false;
particlePacket.x = x;
particlePacket.y = y;
particlePacket.z = z;
particlePacket.offsetX = 0.4f;
particlePacket.offsetY = 0.65f;
particlePacket.offsetZ = 0.4f;
particlePacket.particleData = 0.3f;
particlePacket.particleCount = 75;
particlePacket.blockId = blockId;
playerConnection.sendPacket(particlePacket);
sendPacketToViewers(breakAnimationPacket);
}
public void sendMessage(String message) {
@ -197,6 +283,13 @@ public class Player extends LivingEntity {
return gameMode;
}
public void kick(String message) {
DisconnectPacket disconnectPacket = new DisconnectPacket();
disconnectPacket.message = message;
playerConnection.sendPacket(disconnectPacket);
playerConnection.getConnection().close();
}
public void setGameMode(GameMode gameMode) {
ChangeGameStatePacket changeGameStatePacket = new ChangeGameStatePacket();
changeGameStatePacket.reason = ChangeGameStatePacket.Reason.CHANGE_GAMEMODE;
@ -218,10 +311,6 @@ public class Player extends LivingEntity {
return heldSlot;
}
public ItemStack getHeldItemStack() {
return inventory.getItemStack(heldSlot);
}
public Inventory getOpenInventory() {
return openInventory;
}
@ -273,37 +362,35 @@ public class Player extends LivingEntity {
inventory.update();
}
public void syncEquipment(EntityEquipmentPacket.Slot slot) {
EntityEquipmentPacket equipmentPacket = new EntityEquipmentPacket();
equipmentPacket.entityId = getEntityId();
equipmentPacket.slot = slot;
equipmentPacket.itemStack = inventory.getEquipment(slot);
sendPacketToViewers(equipmentPacket);
}
public void addPacketToQueue(ClientPlayPacket packet) {
this.packets.add(packet);
synchronized (packets) {
this.packets.add(packet);
}
}
public void refreshGameMode(GameMode gameMode) {
this.gameMode = gameMode;
}
public void refreshView(float yaw, float pitch) {
this.yaw = yaw;
this.pitch = pitch;
}
public void refreshOnGround(boolean onGround) {
this.onGround = onGround;
}
public void refreshSneaking(boolean sneaking) {
sneaking = sneaking;
}
public void refreshSprinting(boolean sprinting) {
sprinting = sprinting;
}
public void refreshKeepAlive(long lastKeepAlive) {
this.lastKeepAlive = lastKeepAlive;
}
public void refreshHeldSlot(short slot) {
this.heldSlot = slot;
syncEquipment(EntityEquipmentPacket.Slot.MAIN_HAND);
}
public void refreshOpenInventory(Inventory openInventory) {

View File

@ -0,0 +1,16 @@
package fr.themode.minestom.event;
import fr.themode.minestom.utils.Position;
public class BlockBreakEvent extends CancellableEvent {
private Position position;
public BlockBreakEvent(Position position) {
this.position = position;
}
public Position getPosition() {
return position;
}
}

View File

@ -4,8 +4,10 @@ import fr.themode.minestom.entity.Entity;
import fr.themode.minestom.entity.EntityCreature;
import fr.themode.minestom.entity.ObjectEntity;
import fr.themode.minestom.entity.Player;
import fr.themode.minestom.event.BlockBreakEvent;
import fr.themode.minestom.net.packet.server.play.ChunkDataPacket;
import fr.themode.minestom.net.packet.server.play.DestroyEntitiesPacket;
import fr.themode.minestom.net.packet.server.play.ParticlePacket;
import fr.themode.minestom.utils.GroupedCollections;
import fr.themode.minestom.utils.Position;
@ -49,6 +51,37 @@ public class Instance implements BlockModifier {
}
}
public void breakBlock(Player player, Position position, short blockId) {
BlockBreakEvent blockBreakEvent = new BlockBreakEvent(position);
player.callEvent(BlockBreakEvent.class, blockBreakEvent);
if (!blockBreakEvent.isCancelled()) {
int x = position.getX();
int y = position.getY();
int z = position.getZ();
setBlock(x, y, z, (short) 0);
ParticlePacket particlePacket = new ParticlePacket(); // TODO change by a proper particle API
particlePacket.particleId = 3; // Block particle
particlePacket.longDistance = false;
particlePacket.x = x + 0.5f;
particlePacket.y = y;
particlePacket.z = z + 0.5f;
particlePacket.offsetX = 0.4f;
particlePacket.offsetY = 0.65f;
particlePacket.offsetZ = 0.4f;
particlePacket.particleData = 0.3f;
particlePacket.particleCount = 75;
particlePacket.blockId = blockId;
player.getPlayerConnection().sendPacket(particlePacket);
player.sendPacketToViewers(particlePacket);
} else {
sendChunkUpdate(player, getChunkAt(position));
}
}
public void breakBlock(Player player, Position position, CustomBlock customBlock) {
breakBlock(player, position, customBlock.getType());
}
public Chunk loadChunk(int chunkX, int chunkZ) {
Chunk chunk = getChunk(chunkX, chunkZ);
return chunk == null ? createChunk(chunkX, chunkZ) : chunk; // TODO load from file
@ -59,6 +92,10 @@ public class Instance implements BlockModifier {
return chunk.getBlockId((byte) (x % 16), (byte) y, (byte) (z % 16));
}
public short getBlockId(Position position) {
return getBlockId(position.getX(), position.getY(), position.getZ());
}
public CustomBlock getCustomBlock(int x, int y, int z) {
Chunk chunk = getChunkAt(x, z);
return chunk.getCustomBlock((byte) (x % 16), (byte) y, (byte) (z % 16));
@ -96,13 +133,14 @@ public class Instance implements BlockModifier {
lastInstance.removeEntity(entity);
}
// TODO based on distance with players
getPlayers().forEach(p -> entity.addViewer(p));
if (entity instanceof Player) {
Player player = (Player) entity;
sendChunks(player);
getCreatures().forEach(entityCreature -> entityCreature.addViewer(player));
} else {
// TODO based on distance with players
getPlayers().forEach(p -> entity.addViewer(p));
getPlayers().forEach(p -> p.addViewer(player));
}
Chunk chunk = getChunkAt(entity.getX(), entity.getZ());
@ -114,12 +152,13 @@ public class Instance implements BlockModifier {
if (entityInstance == null || entityInstance != this)
return;
entity.getViewers().forEach(p -> entity.removeViewer(p));
if (!(entity instanceof Player)) {
DestroyEntitiesPacket destroyEntitiesPacket = new DestroyEntitiesPacket();
destroyEntitiesPacket.entityIds = new int[]{entity.getEntityId()};
entity.getViewers().forEach(p -> p.getPlayerConnection().sendPacket(destroyEntitiesPacket)); // TODO destroy batch
entity.getViewers().forEach(p -> entity.removeViewer(p));
}
Chunk chunk = getChunkAt(entity.getX(), entity.getZ());

View File

@ -2,6 +2,7 @@ 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.EntityEquipmentPacket;
import fr.themode.minestom.net.packet.server.play.SetSlotPacket;
import fr.themode.minestom.net.packet.server.play.WindowItemsPacket;
import fr.themode.minestom.net.player.PlayerConnection;
@ -80,28 +81,58 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler
return false;
}
public void setHelmet(ItemStack itemStack) {
safeItemInsert(HELMET_SLOT, itemStack);
}
public void setChestplate(ItemStack itemStack) {
safeItemInsert(CHESTPLATE_SLOT, itemStack);
}
public void setLeggings(ItemStack itemStack) {
safeItemInsert(LEGGINGS_SLOT, itemStack);
}
public void setBoots(ItemStack itemStack) {
safeItemInsert(BOOTS_SLOT, itemStack);
public ItemStack getItemInMainHand() {
return getItemStack(player.getHeldSlot());
}
public void setItemInMainHand(ItemStack itemStack) {
safeItemInsert(player.getHeldSlot(), itemStack);
player.syncEquipment(EntityEquipmentPacket.Slot.MAIN_HAND);
}
public ItemStack getItemInOffHand() {
return getItemStack(OFFHAND_SLOT);
}
public void setItemInOffHand(ItemStack itemStack) {
safeItemInsert(OFFHAND_SLOT, itemStack);
player.syncEquipment(EntityEquipmentPacket.Slot.OFF_HAND);
}
public ItemStack getHelmet() {
return getItemStack(HELMET_SLOT);
}
public void setHelmet(ItemStack itemStack) {
safeItemInsert(HELMET_SLOT, itemStack);
player.syncEquipment(EntityEquipmentPacket.Slot.HELMET);
}
public ItemStack getChestplate() {
return getItemStack(CHESTPLATE_SLOT);
}
public void setChestplate(ItemStack itemStack) {
safeItemInsert(CHESTPLATE_SLOT, itemStack);
player.syncEquipment(EntityEquipmentPacket.Slot.CHESTPLATE);
}
public ItemStack getLeggings() {
return getItemStack(LEGGINGS_SLOT);
}
public void setLeggings(ItemStack itemStack) {
safeItemInsert(LEGGINGS_SLOT, itemStack);
player.syncEquipment(EntityEquipmentPacket.Slot.LEGGINGS);
}
public ItemStack getBoots() {
return getItemStack(BOOTS_SLOT);
}
public void setBoots(ItemStack itemStack) {
safeItemInsert(BOOTS_SLOT, itemStack);
player.syncEquipment(EntityEquipmentPacket.Slot.BOOTS);
}
public void update() {
@ -121,6 +152,24 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler
this.cursorItem = cursorItem;
}
public ItemStack getEquipment(EntityEquipmentPacket.Slot slot) {
switch (slot) {
case MAIN_HAND:
return getItemInMainHand();
case OFF_HAND:
return getItemInOffHand();
case HELMET:
return getHelmet();
case CHESTPLATE:
return getChestplate();
case LEGGINGS:
return getLeggings();
case BOOTS:
return getBoots();
}
return ItemStack.AIR_ITEM;
}
private void safeItemInsert(int slot, ItemStack itemStack) {
synchronized (this) {
itemStack = itemStack == null ? ItemStack.AIR_ITEM : itemStack;
@ -128,6 +177,19 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler
// System.out.println("INSERT: " + slot);
//sendSlotRefresh((short) slot, itemStack);
update();
if (slot == player.getHeldSlot()) {
player.syncEquipment(EntityEquipmentPacket.Slot.MAIN_HAND);
} else if (slot == OFFHAND_SLOT) {
player.syncEquipment(EntityEquipmentPacket.Slot.OFF_HAND);
} else if (slot == HELMET_SLOT) {
player.syncEquipment(EntityEquipmentPacket.Slot.HELMET);
} else if (slot == CHESTPLATE_SLOT) {
player.syncEquipment(EntityEquipmentPacket.Slot.CHESTPLATE);
} else if (slot == LEGGINGS_SLOT) {
player.syncEquipment(EntityEquipmentPacket.Slot.LEGGINGS);
} else if (slot == BOOTS_SLOT) {
player.syncEquipment(EntityEquipmentPacket.Slot.BOOTS);
}
}
}

View File

@ -0,0 +1,16 @@
package fr.themode.minestom.listener;
import fr.themode.minestom.entity.Player;
import fr.themode.minestom.net.packet.client.play.ClientAnimationPacket;
import fr.themode.minestom.net.packet.server.play.AnimationPacket;
public class AnimationListener {
public static void animationListener(ClientAnimationPacket packet, Player player) {
AnimationPacket animationPacket = new AnimationPacket();
animationPacket.playerId = player.getEntityId();
animationPacket.animation = packet.hand == Player.Hand.MAIN ? AnimationPacket.Animation.SWING_MAIN_ARM : AnimationPacket.Animation.SWING_OFF_HAND;
player.sendPacketToViewers(animationPacket);
}
}

View File

@ -1,16 +1,13 @@
package fr.themode.minestom.listener;
import fr.themode.minestom.chat.Chat;
import fr.themode.minestom.entity.Player;
import fr.themode.minestom.net.packet.client.play.ClientKeepAlivePacket;
import fr.themode.minestom.net.packet.server.play.DisconnectPacket;
public class KeepAliveListener {
public static void listener(ClientKeepAlivePacket packet, Player player) {
if (packet.id != player.getLastKeepAlive()) {
player.getPlayerConnection().sendPacket(new DisconnectPacket(Chat.rawText("Bad Keep Alive packet")));
player.getPlayerConnection().getConnection().close();
player.kick("Bad Keep Alive packet");
}
}

View File

@ -26,6 +26,7 @@ public class PacketListenerManager {
addListener(ClientPlayerPositionPacket.class, PlayerPositionListener::playerPositionListener);
addListener(ClientPlayerPositionAndLookPacket.class, PlayerPositionListener::playerPositionAndLookListener);
addListener(ClientPlayerDiggingPacket.class, PlayerDiggingListener::playerDiggingListener);
addListener(ClientAnimationPacket.class, AnimationListener::animationListener);
}

View File

@ -41,7 +41,6 @@ public class PlayerDiggingListener {
}
break;
case CANCELLED_DIGGING:
// TODO BlockBreakEvent
player.sendBlockBreakAnimation(position, (byte) -1);
player.resetTargetBlock();
removeEffect(player);
@ -53,7 +52,8 @@ public class PlayerDiggingListener {
} else {
Instance instance = player.getInstance();
if (instance != null) {
instance.setBlock(position.getX(), position.getY(), position.getZ(), (short) 0);
short blockId = instance.getBlockId(position);
instance.breakBlock(player, position, blockId);
}
}
break;

View File

@ -11,8 +11,6 @@ import fr.themode.minestom.entity.Player;
import fr.themode.minestom.entity.demo.ChickenCreature;
import fr.themode.minestom.instance.Instance;
import fr.themode.minestom.instance.demo.ChunkGeneratorDemo;
import fr.themode.minestom.inventory.Inventory;
import fr.themode.minestom.inventory.InventoryType;
import fr.themode.minestom.inventory.PlayerInventory;
import fr.themode.minestom.item.ItemStack;
import fr.themode.minestom.net.ConnectionManager;
@ -22,13 +20,11 @@ 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.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.utils.Utils;
import fr.themode.minestom.world.Dimension;
import java.util.HashMap;
import java.util.UUID;
public class LoginStartPacket implements ClientPreplayPacket {
@ -54,23 +50,29 @@ public class LoginStartPacket implements ClientPreplayPacket {
@Override
public void process(PlayerConnection connection, ConnectionManager connectionManager) {
HashMap<String, UUID> uuids = new HashMap<>();
String property = "eyJ0aW1lc3RhbXAiOjE1NjU0ODMwODQwOTYsInByb2ZpbGVJZCI6ImFiNzBlY2I0MjM0NjRjMTRhNTJkN2EwOTE1MDdjMjRlIiwicHJvZmlsZU5hbWUiOiJUaGVNb2RlOTExIiwidGV4dHVyZXMiOnsiU0tJTiI6eyJ1cmwiOiJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlL2RkOTE2NzJiNTE0MmJhN2Y3MjA2ZTRjN2IwOTBkNzhlM2Y1ZDc2NDdiNWFmZDIyNjFhZDk4OGM0MWI2ZjcwYTEifX19";
/*HashMap<String, UUID> uuids = new HashMap<>();
uuids.put("TheMode911", UUID.fromString("ab70ecb4-2346-4c14-a52d-7a091507c24e"));
uuids.put("Adamaq01", UUID.fromString("58ffa9d8-aee1-4587-8b79-41b754f6f238"));
HashMap<String, String> properties = new HashMap<>();
properties.put("TheMode911", "eyJ0aW1lc3RhbXAiOjE1NjU0ODMwODQwOTYsInByb2ZpbGVJZCI6ImFiNzBlY2I0MjM0NjRjMTRhNTJkN2EwOTE1MDdjMjRlIiwicHJvZmlsZU5hbWUiOiJUaGVNb2RlOTExIiwidGV4dHVyZXMiOnsiU0tJTiI6eyJ1cmwiOiJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlL2RkOTE2NzJiNTE0MmJhN2Y3MjA2ZTRjN2IwOTBkNzhlM2Y1ZDc2NDdiNWFmZDIyNjFhZDk4OGM0MWI2ZjcwYTEifX19");
properties.put("Adamaq01", "eyJ0aW1lc3RhbXAiOjE1NjU0NzgyODU4MTksInByb2ZpbGVJZCI6IjU4ZmZhOWQ4YWVlMTQ1ODc4Yjc5NDFiNzU0ZjZmMjM4IiwicHJvZmlsZU5hbWUiOiJBZGFtYXEwMSIsInRleHR1cmVzIjp7IlNLSU4iOnsidXJsIjoiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS9lMTNiNmMyMjNlMTFiYjM1Nzc5OTdkZWY3YzA2ZDUwZmM4NzMxYjBkZWQyOTRlZDQ2ZmM4ZDczNDI1NGM5ZTkifSwiQ0FQRSI6eyJ1cmwiOiJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlL2IwY2MwODg0MDcwMDQ0NzMyMmQ5NTNhMDJiOTY1ZjFkNjVhMTNhNjAzYmY2NGIxN2M4MDNjMjE0NDZmZTE2MzUifX19");
properties.put("Adamaq01", "eyJ0aW1lc3RhbXAiOjE1NjU0NzgyODU4MTksInByb2ZpbGVJZCI6IjU4ZmZhOWQ4YWVlMTQ1ODc4Yjc5NDFiNzU0ZjZmMjM4IiwicHJvZmlsZU5hbWUiOiJBZGFtYXEwMSIsInRleHR1cmVzIjp7IlNLSU4iOnsidXJsIjoiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS9lMTNiNmMyMjNlMTFiYjM1Nzc5OTdkZWY3YzA2ZDUwZmM4NzMxYjBkZWQyOTRlZDQ2ZmM4ZDczNDI1NGM5ZTkifSwiQ0FQRSI6eyJ1cmwiOiJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlL2IwY2MwODg0MDcwMDQ0NzMyMmQ5NTNhMDJiOTY1ZjFkNjVhMTNhNjAzYmY2NGIxN2M4MDNjMjE0NDZmZTE2MzUifX19");*/
// TODO send encryption request OR directly login success
LoginSuccessPacket successPacket = new LoginSuccessPacket(uuids.get(username), username);
UUID playerUuid = UUID.randomUUID();//UUID.fromString("OfflinePlayer:" + username);
LoginSuccessPacket successPacket = new LoginSuccessPacket(playerUuid, username);//new LoginSuccessPacket(uuids.get(username), username);
connection.sendPacket(successPacket);
connection.setConnectionState(ConnectionState.PLAY);
connectionManager.createPlayer(uuids.get(username), username, connection);
connectionManager.createPlayer(playerUuid, username, connection);
Player player = connectionManager.getPlayer(connection);
GameMode gameMode = GameMode.SURVIVAL;
float x = 5;
float y = 5;
float z = 5;
player.refreshGameMode(gameMode);
player.refreshPosition(x, y, z);
// TODO complete login sequence with optionals packets
JoinGamePacket joinGamePacket = new JoinGamePacket();
@ -89,8 +91,6 @@ public class LoginStartPacket implements ClientPreplayPacket {
// TODO player abilities
player.setInstance(instance);
SpawnPositionPacket spawnPositionPacket = new SpawnPositionPacket();
spawnPositionPacket.x = 0;
@ -99,9 +99,9 @@ public class LoginStartPacket implements ClientPreplayPacket {
connection.sendPacket(spawnPositionPacket);
PlayerPositionAndLookPacket playerPositionAndLookPacket = new PlayerPositionAndLookPacket();
playerPositionAndLookPacket.x = 0;
playerPositionAndLookPacket.y = 5;
playerPositionAndLookPacket.z = 0;
playerPositionAndLookPacket.x = x;
playerPositionAndLookPacket.y = y;
playerPositionAndLookPacket.z = z;
playerPositionAndLookPacket.yaw = 0;
playerPositionAndLookPacket.pitch = 0;
playerPositionAndLookPacket.flags = 0;
@ -109,70 +109,43 @@ public class LoginStartPacket implements ClientPreplayPacket {
connection.sendPacket(playerPositionAndLookPacket);
PlayerInfoPacket playerInfoPacket = new PlayerInfoPacket(PlayerInfoPacket.Action.ADD_PLAYER);
PlayerInfoPacket.AddPlayer addPlayer = new PlayerInfoPacket.AddPlayer(uuids.get(username), username, GameMode.CREATIVE, 10);
PlayerInfoPacket.AddPlayer.Property property = new PlayerInfoPacket.AddPlayer.Property("textures", properties.get(username));
addPlayer.properties.add(property);
PlayerInfoPacket.AddPlayer addPlayer = new PlayerInfoPacket.AddPlayer(player.getUuid(), username, GameMode.CREATIVE, 10);
PlayerInfoPacket.AddPlayer.Property prop = new PlayerInfoPacket.AddPlayer.Property("textures", property); //new PlayerInfoPacket.AddPlayer.Property("textures", properties.get(username));
addPlayer.properties.add(prop);
playerInfoPacket.playerInfos.add(addPlayer);
connection.sendPacket(playerInfoPacket);
for (int x = 0; x < 4; x++)
for (int z = 0; z < 4; z++) {
player.setInstance(instance);
for (int cx = 0; cx < 4; cx++)
for (int cz = 0; cz < 4; cz++) {
ChickenCreature chickenCreature = new ChickenCreature();
chickenCreature.refreshPosition(0 + (double) x * 1, 5, 0 + (double) z * 1);
chickenCreature.refreshPosition(0 + (double) cx * 1, 5, 0 + (double) cz * 1);
chickenCreature.setOnFire(true);
chickenCreature.setInstance(instance);
if (x == 3 && z == 3) {
if (cx == 3 && cz == 3) {
//chickenCreature.addPassenger(player);
}
}
SpawnPlayerPacket spawnPlayerPacket = new SpawnPlayerPacket();
spawnPlayerPacket.entityId = player.getEntityId();
spawnPlayerPacket.playerUuid = uuids.get(username);
spawnPlayerPacket.x = 0;
spawnPlayerPacket.y = 5;
spawnPlayerPacket.z = 0;
for (Player onlinePlayer : connectionManager.getOnlinePlayers()) {
if (onlinePlayer.getUsername().equals(username)) continue;
onlinePlayer.getPlayerConnection().sendPacket(playerInfoPacket);
onlinePlayer.getPlayerConnection().sendPacket(spawnPlayerPacket);
SpawnPlayerPacket spawnPacket = new SpawnPlayerPacket();
spawnPacket.entityId = onlinePlayer.getEntityId();
spawnPacket.playerUuid = uuids.get(onlinePlayer.getUsername());
spawnPacket.x = onlinePlayer.getX();
spawnPacket.y = onlinePlayer.getY();
spawnPacket.z = onlinePlayer.getZ();
PlayerInfoPacket pInfoPacket = new PlayerInfoPacket(PlayerInfoPacket.Action.ADD_PLAYER);
PlayerInfoPacket.AddPlayer addP = new PlayerInfoPacket.AddPlayer(uuids.get(onlinePlayer.getUsername()), onlinePlayer.getUsername(), GameMode.CREATIVE, 10);
PlayerInfoPacket.AddPlayer.Property p = new PlayerInfoPacket.AddPlayer.Property("textures", properties.get(onlinePlayer.getUsername()));
addP.properties.add(p);
pInfoPacket.playerInfos.add(addP);
connection.sendPacket(pInfoPacket);
connection.sendPacket(spawnPacket);
}
//System.out.println("HAHAHAHHAHHAH " + player.getUuid());
PlayerInventory inventory = player.getInventory();
for (int i = 0; i < 20; i++) {
inventory.addItemStack(new ItemStack(1, (byte) 64));
}
Inventory inv = new Inventory(InventoryType.WINDOW_3X3, "Salut je suis le titre");
/*Inventory inv = new Inventory(InventoryType.WINDOW_3X3, "Salut je suis le titre");
inv.setItemStack(0, new ItemStack(1, (byte) 1));
player.openInventory(inv);
inv.setItemStack(1, new ItemStack(1, (byte) 2));
//inv.updateItems();
inv.setItemStack(1, new ItemStack(1, (byte) 2));*/
BossBar bossBar = new BossBar("Le titre", BarColor.BLUE, BarDivision.SEGMENT_12);
bossBar.setProgress(0.75f);
bossBar.addViewer(player);
for (int x = 0; x < 4; x++)
for (int z = 0; z < 4; z++) {
for (int ix = 0; ix < 4; ix++)
for (int iz = 0; iz < 4; iz++) {
ItemEntity itemEntity = new ItemEntity(new ItemStack(1, (byte) 32));
itemEntity.refreshPosition(x, 5, z);
itemEntity.refreshPosition(ix, 5, iz);
itemEntity.setInstance(instance);
//itemEntity.remove();
}

View File

@ -0,0 +1,31 @@
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 AnimationPacket implements ServerPacket {
public int playerId; // FIXME verify if this is only player?
public Animation animation;
@Override
public void write(Buffer buffer) {
Utils.writeVarInt(buffer, playerId);
buffer.putByte((byte) animation.ordinal());
}
@Override
public int getId() {
return 0x06;
}
public enum Animation {
SWING_MAIN_ARM,
TAKE_DAMAGE,
LEAVE_BED,
SWING_OFF_HAND,
CRITICAL_EFFECT,
MAGICAL_CRITICAL_EFFECT;
}
}

View File

@ -1,20 +1,17 @@
package fr.themode.minestom.net.packet.server.play;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.chat.Chat;
import fr.themode.minestom.net.packet.server.ServerPacket;
import fr.themode.minestom.utils.Utils;
public class DisconnectPacket implements ServerPacket {
private String message;
public DisconnectPacket(String message) {
this.message = message;
}
public String message;
@Override
public void write(Buffer buffer) {
Utils.writeString(buffer, this.message);
Utils.writeString(buffer, Chat.rawText(message));
}
@Override

View File

@ -0,0 +1,35 @@
package fr.themode.minestom.net.packet.server.play;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.item.ItemStack;
import fr.themode.minestom.net.packet.server.ServerPacket;
import fr.themode.minestom.utils.Utils;
public class EntityEquipmentPacket implements ServerPacket {
public int entityId;
public Slot slot;
public ItemStack itemStack;
@Override
public void write(Buffer buffer) {
Utils.writeVarInt(buffer, entityId);
Utils.writeVarInt(buffer, slot.ordinal());
Utils.writeItemStack(buffer, itemStack);
}
@Override
public int getId() {
return 0x46;
}
public enum Slot {
MAIN_HAND,
OFF_HAND,
BOOTS,
LEGGINGS,
CHESTPLATE,
HELMET;
}
}

View File

@ -0,0 +1,22 @@
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 EntityHeadLookPacket implements ServerPacket {
public int entityId;
public float yaw;
@Override
public void write(Buffer buffer) {
Utils.writeVarInt(buffer, entityId);
buffer.putByte((byte) (this.yaw * 256 / 360));
}
@Override
public int getId() {
return 0x3B;
}
}

View File

@ -0,0 +1,25 @@
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 EntityLookPacket implements ServerPacket {
public int entityId;
public float yaw, pitch;
public boolean onGround;
@Override
public void write(Buffer buffer) {
Utils.writeVarInt(buffer, entityId);
buffer.putByte((byte) (this.yaw * 256 / 360));
buffer.putByte((byte) (this.pitch * 256 / 360));
buffer.putBoolean(onGround);
}
@Override
public int getId() {
return 0x2A;
}
}

View File

@ -35,7 +35,7 @@ public class PlayerInfoPacket implements ServerPacket {
return 0x33;
}
public static enum Action {
public enum Action {
ADD_PLAYER(AddPlayer.class),
UPDATE_GAMEMODE(UpdateGamemode.class),

View File

@ -13,8 +13,8 @@ public class SpawnPlayerPacket implements ServerPacket {
public double x;
public double y;
public double z;
// public float yaw;
// public float pitch;
public float yaw;
public float pitch;
@Override
public void write(Buffer buffer) {
@ -24,8 +24,8 @@ public class SpawnPlayerPacket implements ServerPacket {
buffer.putDouble(x);
buffer.putDouble(y);
buffer.putDouble(z);
buffer.getData().writeByte(0);
buffer.getData().writeByte(0);
buffer.putByte((byte) (yaw * 256 / 360));
buffer.putByte((byte) (pitch * 256 / 360));
buffer.putByte((byte) 0xff); // TODO Metadata
}