diff --git a/src/main/java/fr/themode/demo/PlayerInit.java b/src/main/java/fr/themode/demo/PlayerInit.java index d69ebe567..019e9386a 100644 --- a/src/main/java/fr/themode/demo/PlayerInit.java +++ b/src/main/java/fr/themode/demo/PlayerInit.java @@ -4,6 +4,7 @@ import fr.themode.demo.entity.ChickenCreature; import fr.themode.demo.generator.ChunkGeneratorDemo; import fr.themode.demo.generator.NoiseTestGenerator; import net.minestom.server.MinecraftServer; +import net.minestom.server.attribute.Attribute; import net.minestom.server.benchmark.BenchmarkManager; import net.minestom.server.benchmark.ThreadResult; import net.minestom.server.entity.*; @@ -39,6 +40,11 @@ import java.util.UUID; public class PlayerInit { + private static String textures = "ewogICJ0aW1lc3RhbXAiIDogMTU5MDg1NTI3NjIwNCwKICAicHJvZmlsZUlkIiA6ICI0NTY2ZTY5ZmM5MDc0OGVlOGQ3MWQ3YmE1YWEwMGQyMCIsCiAgInByb2ZpbGVOYW1lIiA6ICJUaGlua29mZGVhdGgiLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzRkMWUwOGIwYmI3ZTlmNTkwYWYyNzc1ODEyNWJiZWQxNzc4YWM2Y2VmNzI5YWVkZmNiOTYxM2U5OTExYWU3NSIKICAgIH0sCiAgICAiQ0FQRSIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYjBjYzA4ODQwNzAwNDQ3MzIyZDk1M2EwMmI5NjVmMWQ2NWExM2E2MDNiZjY0YjE3YzgwM2MyMTQ0NmZlMTYzNSIKICAgIH0KICB9Cn0="; + private static String signature = "rCVVwVpLF9ovy+Hm4cOXOLSPOMjNo5WoBfHo9K2OcTUPqcZJ1P/1k4lAnQkChD/Ri11iJ84PejWJzDkHMXM8196Wh+jf12d2GJVhca9/SRLms0cFJjdZZjs72+82AdX0OtO3+qzwKRHzHoEYb+ZVZLfgx37ZKKo4DD3IKmaSnwEjOVJ4BOhnsXLmcNW37kdZUmv2/hlg7ZuWZaayWPhadCYEMnkpVtDIpnpzAeV9EcRfg/ysQoynO2v6WEW0RtnfFEczMN6vXtfiuC8UqyA2SK9aiLnBgpGaehDfFIq/0dpo2uFilVDS/Il6uQ1JSwq7yNT5lNF+i1AlH9SGf1VVy5mT9ShmkVmRxCXX5cSNLXZD0acsNNJb/GAuDHuXpE32GsfgKxWAXMHLw0GnbADbFDfdl5nQyQTDS7FRfUjsFpF8a8Z83muFXaty2WLFy1zxy2JEkI/q+ltLaEG6mQbWI2zhOS7ARvK0OmPz4lDYVInfrwL93AIdMUg2Re817hsapkN6Dm1ND+iirvayR90gqQ9C9J0dMMBlSyTSoKBQeLsi8qETS+7LuhvletPTDFolnTIvP8hj2bWLmQ7LfXJ2arJCUw86YEavVYuF0gYrBuKcEYTC4DA0kO4yLj63gwEgOj9dEigCgyqUcenzmZBffSZ365/QF0cGrG7HC7HmF0w="; + + private static PlayerSkin skin = new PlayerSkin(textures, signature); + private static volatile InstanceContainer instanceContainer; private static volatile InstanceContainer netherTest; @@ -95,16 +101,15 @@ public class PlayerInit { benchmarkMessage += "&e" + MathUtils.round(result.getCpuPercentage(), 2) + "% CPU "; benchmarkMessage += "&c" + MathUtils.round(result.getUserPercentage(), 2) + "% USER "; benchmarkMessage += "&d" + MathUtils.round(result.getBlockedPercentage(), 2) + "% BLOCKED "; + benchmarkMessage += "&a" + MathUtils.round(result.getWaitedPercentage(), 2) + "% WAITED "; benchmarkMessage += "\n"; } - // if (benchmarkMessage.length() > 0) - // System.out.println(benchmarkMessage); for (Player player : connectionManager.getOnlinePlayers()) { player.sendHeaderFooter("RAM USAGE: " + ramUsage + " MB", benchmarkMessage, '&'); } } - }, new UpdateOption(5, TimeUnit.TICK)); + }, new UpdateOption(10, TimeUnit.TICK)); connectionManager.addPacketConsumer((player, packetController, packet) -> { // Listen to all received packet @@ -150,6 +155,7 @@ public class PlayerInit { ChickenCreature chickenCreature = new ChickenCreature(player.getPosition()); chickenCreature.setInstance(player.getInstance()); + chickenCreature.setAttribute(Attribute.MOVEMENT_SPEED, 0.4f); /*FakePlayer fakePlayer = new FakePlayer(UUID.randomUUID(), "test"); fakePlayer.addEventCallback(EntityDeathEvent.class, e -> { @@ -211,6 +217,10 @@ public class PlayerInit { scoreboard.setTitle("test");*/ }); + player.addEventCallback(PlayerSkinInitEvent.class, event -> { + event.setSkin(skin); + }); + player.addEventCallback(PlayerSpawnEvent.class, event -> { player.setGameMode(GameMode.CREATIVE); player.teleport(new Position(0, 41f, 0)); @@ -224,9 +234,10 @@ public class PlayerInit { item.setEnchantment(Enchantment.SHARPNESS, (short) 50); player.getInventory().addItemStack(item); + inventory.addItemStack(item.clone()); player.openInventory(inventory); - //player.getInventory().addItemStack(new ItemStack(Material.STONE, (byte) 100)); + player.getInventory().addItemStack(new ItemStack(Material.STONE, (byte) 100)); Instance instance = player.getInstance(); WorldBorder worldBorder = instance.getWorldBorder(); @@ -301,7 +312,7 @@ public class PlayerInit { public static ResponseDataConsumer getResponseDataConsumer() { return (playerConnection, responseData) -> { - responseData.setMaxPlayer(100); + responseData.setMaxPlayer(0); responseData.setOnline(MinecraftServer.getConnectionManager().getOnlinePlayers().size()); responseData.addPlayer("A name", UUID.randomUUID()); responseData.addPlayer("Could be some message", UUID.randomUUID()); diff --git a/src/main/java/net/minestom/server/entity/EntityCreature.java b/src/main/java/net/minestom/server/entity/EntityCreature.java index 6f2df3604..f3f7f5851 100644 --- a/src/main/java/net/minestom/server/entity/EntityCreature.java +++ b/src/main/java/net/minestom/server/entity/EntityCreature.java @@ -53,19 +53,7 @@ public abstract class EntityCreature extends LivingEntity { super.update(); // Path finding - if (blockPositions != null) { - if (targetPosition != null) { - float distance = getPosition().getDistance(targetPosition); - //System.out.println("test: "+distance); - if (distance < 0.7f) { - setNextPathPosition(); - //System.out.println("END TARGET"); - } else { - moveTowards(targetPosition, getAttributeValue(Attribute.MOVEMENT_SPEED)); - //System.out.println("MOVE TOWARD " + targetPosition); - } - } - } + pathProgress(); } @@ -250,7 +238,7 @@ public abstract class EntityCreature extends LivingEntity { public void jump(float height) { // FIXME magic value - Vector velocity = new Vector(0, height * 10, 0); + Vector velocity = new Vector(0, height * 5, 0); setVelocity(velocity); } @@ -281,7 +269,7 @@ public abstract class EntityCreature extends LivingEntity { } /** - * Used to move the entity toward {@code direction} in the axis X and Z + * Used to move the entity toward {@code direction} in the X and Z axis * Gravity is still applied but the entity will not attempt to jump * * @param direction the targeted position @@ -304,11 +292,27 @@ public abstract class EntityCreature extends LivingEntity { } this.targetPosition = blockPosition.toPosition();//.add(0.5f, 0, 0.5f); - // FIXME: jump support if (blockPosition.getY() > getPosition().getY()) jump(1); } + private void pathProgress() { + if (blockPositions != null) { + if (targetPosition != null) { + float distance = getPosition().getDistance(targetPosition); + //System.out.println("test: "+distance); + if (distance < 1f) { + setNextPathPosition(); + pathProgress(); + //System.out.println("END TARGET"); + } else { + moveTowards(targetPosition, getAttributeValue(Attribute.MOVEMENT_SPEED)); + //System.out.println("MOVE TOWARD " + targetPosition); + } + } + } + } + private ItemStack getEquipmentItem(ItemStack itemStack, ArmorEquipEvent.ArmorSlot armorSlot) { itemStack = ItemStackUtils.notNull(itemStack); diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index c12653afa..e6ef003a0 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -62,6 +62,7 @@ public class Player extends LivingEntity { private int latency; private String displayName; + private PlayerSkin skin; private Dimension dimension; private GameMode gameMode; @@ -165,6 +166,11 @@ public class Player extends LivingEntity { */ protected void init() { + // Init player (register events) + for (Consumer playerInitialization : MinecraftServer.getConnectionManager().getPlayerInitializations()) { + playerInitialization.accept(this); + } + // TODO complete login sequence with optionals packets JoinGamePacket joinGamePacket = new JoinGamePacket(); joinGamePacket.entityId = getEntityId(); @@ -189,21 +195,13 @@ public class Player extends LivingEntity { spawnPositionPacket.z = 0; playerConnection.sendPacket(spawnPositionPacket); - // Add player to list - String jsonDisplayName = displayName != null ? Chat.toJsonString(Chat.fromLegacyText(displayName)) : null; - String property = ""; - PlayerInfoPacket playerInfoPacket = new PlayerInfoPacket(PlayerInfoPacket.Action.ADD_PLAYER); - PlayerInfoPacket.AddPlayer addPlayer = new PlayerInfoPacket.AddPlayer(getUuid(), getUsername(), getGameMode(), getLatency()); - addPlayer.displayName = jsonDisplayName; - PlayerInfoPacket.AddPlayer.Property prop = new PlayerInfoPacket.AddPlayer.Property("textures", property); - addPlayer.properties.add(prop); - playerInfoPacket.playerInfos.add(addPlayer); - playerConnection.sendPacket(playerInfoPacket); - - // Init player - for (Consumer playerInitialization : MinecraftServer.getConnectionManager().getPlayerInitializations()) { - playerInitialization.accept(this); - } + // Add player to list with spawning skin + PlayerSkinInitEvent skinInitEvent = new PlayerSkinInitEvent(this); + callEvent(PlayerSkinInitEvent.class, skinInitEvent); + this.skin = skinInitEvent.getSkin(); + final String textures = skin == null ? "" : skin.getTextures(); + final String signature = skin == null ? null : skin.getSignature(); + playerConnection.sendPacket(getAddPlayerToList(textures, signature)); // Commands start { @@ -245,6 +243,7 @@ public class Player extends LivingEntity { playerConnection.sendPacket(getPropertiesPacket()); // Send default properties refreshHealth(); // Heal and send health packet refreshAbilities(); // Send abilities packet + getInventory().update(); } /** @@ -493,33 +492,7 @@ public class Player extends LivingEntity { return false; PlayerConnection viewerConnection = player.getPlayerConnection(); - String property = "eyJ0aW1lc3RhbXAiOjE1NjU0ODMwODQwOTYsInByb2ZpbGVJZCI6ImFiNzBlY2I0MjM0NjRjMTRhNTJkN2EwOTE1MDdjMjRlIiwicHJvZmlsZU5hbWUiOiJUaGVNb2RlOTExIiwidGV4dHVyZXMiOnsiU0tJTiI6eyJ1cmwiOiJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlL2RkOTE2NzJiNTE0MmJhN2Y3MjA2ZTRjN2IwOTBkNzhlM2Y1ZDc2NDdiNWFmZDIyNjFhZDk4OGM0MWI2ZjcwYTEifX19"; - SpawnPlayerPacket spawnPlayerPacket = new SpawnPlayerPacket(); - spawnPlayerPacket.entityId = getEntityId(); - spawnPlayerPacket.playerUuid = getUuid(); - spawnPlayerPacket.position = getPosition(); - - PlayerInfoPacket pInfoPacket = new PlayerInfoPacket(PlayerInfoPacket.Action.ADD_PLAYER); - PlayerInfoPacket.AddPlayer addP = new PlayerInfoPacket.AddPlayer(getUuid(), getUsername(), getGameMode(), 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); - - viewerConnection.sendPacket(pInfoPacket); - viewerConnection.sendPacket(spawnPlayerPacket); - viewerConnection.sendPacket(getVelocityPacket()); - viewerConnection.sendPacket(getMetadataPacket()); - - // Equipments synchronization - syncEquipments(); - - if (hasPassenger()) { - viewerConnection.sendPacket(getPassengersPacket()); - } - - // Team - if (team != null) - viewerConnection.sendPacket(team.getTeamsCreationPacket()); + showPlayer(viewerConnection); return result; } @@ -530,9 +503,7 @@ public class Player extends LivingEntity { boolean result = super.removeViewer(player); PlayerConnection viewerConnection = player.getPlayerConnection(); - PlayerInfoPacket playerInfoPacket = new PlayerInfoPacket(PlayerInfoPacket.Action.REMOVE_PLAYER); - playerInfoPacket.playerInfos.add(new PlayerInfoPacket.RemovePlayer(getUuid())); - viewerConnection.sendPacket(playerInfoPacket); + viewerConnection.sendPacket(getRemovePlayerToList()); // Team if (team != null && team.getPlayers().size() == 1) // If team only contains "this" player @@ -893,6 +864,66 @@ public class Player extends LivingEntity { sendPacketToViewersAndSelf(infoPacket); } + /** + * Get the player skin + * + * @return the player skin object, + * null means that the player has his {@link #getUuid()} default skin + */ + public PlayerSkin getSkin() { + return skin; + } + + /** + * Change the player skin + * + * @param skin the player skin, null to reset it to his {@link #getUuid()} default skin + */ + public synchronized void setSkin(PlayerSkin skin) { + this.skin = skin; + + final String textures = skin == null ? "" : skin.getTextures(); + final String signature = skin == null ? null : skin.getSignature(); + + DestroyEntitiesPacket destroyEntitiesPacket = new DestroyEntitiesPacket(); + destroyEntitiesPacket.entityIds = new int[]{getEntityId()}; + + PlayerInfoPacket removePlayerPacket = getRemovePlayerToList(); + PlayerInfoPacket addPlayerPacket = getAddPlayerToList(textures, signature); + + RespawnPacket respawnPacket = new RespawnPacket(); + respawnPacket.dimension = getDimension(); + respawnPacket.gameMode = getGameMode(); + respawnPacket.levelType = getLevelType(); + + playerConnection.sendPacket(removePlayerPacket); + playerConnection.sendPacket(destroyEntitiesPacket); + playerConnection.sendPacket(respawnPacket); + playerConnection.sendPacket(addPlayerPacket); + + for (Player viewer : getViewers()) { + PlayerConnection connection = viewer.getPlayerConnection(); + + connection.sendPacket(removePlayerPacket); + connection.sendPacket(destroyEntitiesPacket); + + showPlayer(connection); + } + + getInventory().update(); + teleport(getPosition()); + } + + /** + * Used to update the internal skin field + * + * @param skin the player skin + * @see #setSkin(PlayerSkin) instead + */ + protected void refreshSkin(PlayerSkin skin) { + this.skin = skin; + } + /** * Get if the player has the respawn screen enabled or disabled * @@ -1820,6 +1851,78 @@ public class Player extends LivingEntity { return lastKeepAlive; } + /** + * Get the packet to add the player from tab-list + * + * @param textures the textures value + * @param signature the textures signature + * @return a {@link PlayerInfoPacket} to add the player + */ + protected PlayerInfoPacket getAddPlayerToList(String textures, String signature) { + String jsonDisplayName = displayName != null ? Chat.toJsonString(Chat.fromLegacyText(displayName)) : null; + PlayerInfoPacket playerInfoPacket = new PlayerInfoPacket(PlayerInfoPacket.Action.ADD_PLAYER); + + PlayerInfoPacket.AddPlayer addPlayer = + new PlayerInfoPacket.AddPlayer(getUuid(), getUsername(), getGameMode(), getLatency()); + addPlayer.displayName = jsonDisplayName; + + PlayerInfoPacket.AddPlayer.Property prop = + new PlayerInfoPacket.AddPlayer.Property("textures", textures, signature); + addPlayer.properties.add(prop); + + playerInfoPacket.playerInfos.add(addPlayer); + return playerInfoPacket; + } + + /** + * Get the packet to remove the player from tab-list + * + * @return a {@link PlayerInfoPacket} to add the player + */ + protected PlayerInfoPacket getRemovePlayerToList() { + PlayerInfoPacket playerInfoPacket = new PlayerInfoPacket(PlayerInfoPacket.Action.REMOVE_PLAYER); + + PlayerInfoPacket.RemovePlayer removePlayer = + new PlayerInfoPacket.RemovePlayer(getUuid()); + + playerInfoPacket.playerInfos.add(removePlayer); + return playerInfoPacket; + } + + /** + * Send all the related packet to have the player sent to another with related data + * (create player, spawn position, velocity, metadata, equipments, passengers, team) + *

+ * WARNING: this does not sync the player, please use {@link #addViewer(Player)} + * + * @param connection the connection to show the player to + */ + protected void showPlayer(PlayerConnection connection) { + SpawnPlayerPacket spawnPlayerPacket = new SpawnPlayerPacket(); + spawnPlayerPacket.entityId = getEntityId(); + spawnPlayerPacket.playerUuid = getUuid(); + spawnPlayerPacket.position = getPosition(); + + final String textures = skin == null ? "" : skin.getTextures(); + final String signature = skin == null ? null : skin.getSignature(); + connection.sendPacket(getAddPlayerToList(textures, signature)); + + connection.sendPacket(spawnPlayerPacket); + connection.sendPacket(getVelocityPacket()); + connection.sendPacket(getMetadataPacket()); + + // Equipments synchronization + syncEquipments(); + + if (hasPassenger()) { + connection.sendPacket(getPassengersPacket()); + } + + // Team + if (team != null) + connection.sendPacket(team.getTeamsCreationPacket()); + } + @Override public ItemStack getItemInMainHand() { return inventory.getItemInMainHand(); diff --git a/src/main/java/net/minestom/server/entity/PlayerSkin.java b/src/main/java/net/minestom/server/entity/PlayerSkin.java new file mode 100644 index 000000000..d257c6380 --- /dev/null +++ b/src/main/java/net/minestom/server/entity/PlayerSkin.java @@ -0,0 +1,28 @@ +package net.minestom.server.entity; + +public class PlayerSkin { + + private String textures; + private String signature; + + public PlayerSkin(String textures, String signature) { + this.textures = textures; + this.signature = signature; + } + + public String getTextures() { + return textures; + } + + public void setTextures(String textures) { + this.textures = textures; + } + + public String getSignature() { + return signature; + } + + public void setSignature(String signature) { + this.signature = signature; + } +} diff --git a/src/main/java/net/minestom/server/event/player/PlayerLoginEvent.java b/src/main/java/net/minestom/server/event/player/PlayerLoginEvent.java index 8428fbf87..34f21ece9 100644 --- a/src/main/java/net/minestom/server/event/player/PlayerLoginEvent.java +++ b/src/main/java/net/minestom/server/event/player/PlayerLoginEvent.java @@ -3,16 +3,32 @@ package net.minestom.server.event.player; import net.minestom.server.event.Event; import net.minestom.server.instance.Instance; +/** + * Called at player login, used to define his spawn instance + *

+ * WARNING: defining the spawning instance is MANDATORY + */ public class PlayerLoginEvent extends Event { private Instance spawningInstance; + /** + * Get the spawning instance of the player + *

+ * WARNING: this must NOT be null, otherwise the player cannot spawn + * + * @return the spawning instance + */ public Instance getSpawningInstance() { return spawningInstance; } + /** + * Change the spawning instance + * + * @param instance the new spawning instance + */ public void setSpawningInstance(Instance instance) { this.spawningInstance = instance; } - } diff --git a/src/main/java/net/minestom/server/event/player/PlayerSkinInitEvent.java b/src/main/java/net/minestom/server/event/player/PlayerSkinInitEvent.java new file mode 100644 index 000000000..33e8b854e --- /dev/null +++ b/src/main/java/net/minestom/server/event/player/PlayerSkinInitEvent.java @@ -0,0 +1,45 @@ +package net.minestom.server.event.player; + +import net.minestom.server.entity.Player; +import net.minestom.server.entity.PlayerSkin; +import net.minestom.server.event.Event; + +/** + * Called at the player connection to initialize his skin + */ +public class PlayerSkinInitEvent extends Event { + + private Player player; + private PlayerSkin skin; + + public PlayerSkinInitEvent(Player player) { + this.player = player; + } + + /** + * Get the player whose the skin is getting initialized + * + * @return + */ + public Player getPlayer() { + return player; + } + + /** + * Get the spawning skin of the player + * + * @return the player skin, or null if not any + */ + public PlayerSkin getSkin() { + return skin; + } + + /** + * Set the spawning skin of the player + * + * @param skin the new player skin + */ + public void setSkin(PlayerSkin skin) { + this.skin = skin; + } +} diff --git a/src/main/java/net/minestom/server/network/packet/client/login/LoginStartPacket.java b/src/main/java/net/minestom/server/network/packet/client/login/LoginStartPacket.java index e596de8b8..619a69ba2 100644 --- a/src/main/java/net/minestom/server/network/packet/client/login/LoginStartPacket.java +++ b/src/main/java/net/minestom/server/network/packet/client/login/LoginStartPacket.java @@ -17,9 +17,6 @@ public class LoginStartPacket implements ClientPreplayPacket { public void process(PlayerConnection connection, ConnectionManager connectionManager) { // TODO send encryption request OR directly login success - // TODO: Skin - //UUID adam = UUID.fromString("58ffa9d8-aee1-4587-8b79-41b754f6f238"); - //UUID mode = UUID.fromString("ab70ecb4-2346-4c14-a52d-7a091507c24e"); UUID playerUuid = connectionManager.getPlayerConnectionUuid(connection); LoginSuccessPacket successPacket = new LoginSuccessPacket(playerUuid, username);