From 0b193c04e74cf46f41688fbeff38e2e5b0151cc7 Mon Sep 17 00:00:00 2001 From: Tim203 Date: Wed, 25 Sep 2019 23:52:28 +0200 Subject: [PATCH 1/8] Players spawn, player skins, scoreboard, bossbar and updated protocol lib --- connector/pom.xml | 2 +- .../org/geysermc/connector/entity/Entity.java | 32 +- .../connector/entity/PlayerEntity.java | 21 +- .../network/UpstreamPacketHandler.java | 31 +- .../network/session/GeyserSession.java | 2 +- .../network/session/cache/EntityCache.java | 21 +- .../session/cache/ScoreboardCache.java | 24 +- .../network/translators/Registry.java | 8 +- .../network/translators/TranslatorsInit.java | 10 +- .../bedrock/BedrockMovePlayerTranslator.java | 12 +- .../java/JavaBossBarTranslator.java | 77 ++++ .../entity/JavaEntityDestroyTranslator.java | 5 +- .../entity/JavaEntityHeadLookTranslator.java | 4 +- .../entity/JavaEntityMetadataTranslator.java | 3 +- .../JavaEntityPositionRotationTranslator.java | 3 +- .../entity/JavaEntityPositionTranslator.java | 3 +- .../JavaEntityPropertiesTranslator.java | 6 +- .../entity/JavaEntityRotationTranslator.java | 3 +- .../entity/JavaEntityVelocityTranslator.java | 3 +- .../spawn/JavaPlayerListEntryTranslator.java | 24 +- .../spawn/JavaSpawnPlayerTranslator.java | 61 ++- .../JavaDisplayScoreboardTranslator.java | 12 +- .../JavaScoreboardObjectiveTranslator.java | 18 +- .../java/scoreboard/JavaTeamTranslator.java | 39 ++ .../scoreboard/JavaUpdateScoreTranslator.java | 20 +- .../connector/scoreboard/Objective.java | 126 ++++++ .../geysermc/connector/scoreboard/Score.java | 47 +-- .../connector/scoreboard/Scoreboard.java | 219 ++++++---- .../scoreboard/ScoreboardObjective.java | 130 ------ .../geysermc/connector/scoreboard/Team.java | 64 +++ .../connector/scoreboard/UpdateType.java | 15 + .../connector/utils/ProvidedSkinData.java | 39 -- .../connector/utils/SkinProvider.java | 190 +++++++++ .../resources/bedrock/skin/model_steve.json | 383 ------------------ 34 files changed, 871 insertions(+), 786 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/java/JavaBossBarTranslator.java create mode 100644 connector/src/main/java/org/geysermc/connector/scoreboard/Objective.java delete mode 100644 connector/src/main/java/org/geysermc/connector/scoreboard/ScoreboardObjective.java create mode 100644 connector/src/main/java/org/geysermc/connector/scoreboard/Team.java create mode 100644 connector/src/main/java/org/geysermc/connector/scoreboard/UpdateType.java delete mode 100644 connector/src/main/java/org/geysermc/connector/utils/ProvidedSkinData.java create mode 100644 connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java delete mode 100644 connector/src/main/resources/bedrock/skin/model_steve.json diff --git a/connector/pom.xml b/connector/pom.xml index cea7aebfc..e4b0b358f 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -74,7 +74,7 @@ com.nukkitx.protocol bedrock-v361 - 2.1.2 + 2.1.3 compile diff --git a/connector/src/main/java/org/geysermc/connector/entity/Entity.java b/connector/src/main/java/org/geysermc/connector/entity/Entity.java index 2b2ad0544..735d5ac65 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/Entity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/Entity.java @@ -49,7 +49,6 @@ import java.util.*; @Getter @Setter public class Entity { - protected long entityId; protected long geyserId; @@ -111,24 +110,21 @@ public class Entity { } public void moveRelative(double relX, double relY, double relZ, float pitch, float yaw) { - moveRelative(relX, relY, relZ, new Vector3f(pitch, yaw, 0)); + moveRelative(relX, relY, relZ, new Vector3f(pitch, yaw, yaw)); } public void moveRelative(double relX, double relY, double relZ, Vector3f rotation) { - if (relX == 0 && relY == 0 && relZ == 0 && position.getX() == 0 && position.getY() == 0) - return; - this.rotation = rotation; this.position = new Vector3f(position.getX() + relX, position.getY() + relY, position.getZ() + relZ); this.movePending = true; } public void moveAbsolute(Vector3f position, float pitch, float yaw) { - moveAbsolute(position, new Vector3f(pitch, yaw, 0)); + moveAbsolute(position, new Vector3f(pitch, yaw, yaw)); } public void moveAbsolute(Vector3f position, Vector3f rotation) { - this.position = position; + setPosition(position); this.rotation = rotation; this.movePending = true; } @@ -138,14 +134,13 @@ public class Entity { flags.setFlag(EntityFlag.HAS_GRAVITY, true); flags.setFlag(EntityFlag.HAS_COLLISION, true); flags.setFlag(EntityFlag.CAN_SHOW_NAME, true); - flags.setFlag(EntityFlag.NO_AI, false); + flags.setFlag(EntityFlag.CAN_CLIMB, true); EntityDataDictionary dictionary = new EntityDataDictionary(); - dictionary.put(EntityData.NAMETAG, ""); - dictionary.put(EntityData.ENTITY_AGE, 0); dictionary.put(EntityData.SCALE, 1f); dictionary.put(EntityData.MAX_AIR, (short) 400); dictionary.put(EntityData.AIR, (short) 0); + dictionary.put(EntityData.LEAD_HOLDER_EID, -1L); dictionary.put(EntityData.BOUNDING_BOX_HEIGHT, entityType.getHeight()); dictionary.put(EntityData.BOUNDING_BOX_WIDTH, entityType.getWidth()); dictionary.putFlags(flags); @@ -185,4 +180,21 @@ public class Entity { ServerEntityPropertiesPacket entityPropertiesPacket = new ServerEntityPropertiesPacket((int) entityId, attributes); session.getDownstream().getSession().send(entityPropertiesPacket); } + + public void setPosition(Vector3f position) { + if (is(PlayerEntity.class)) { + this.position = position.add(0, entityType.getOffset(), 0); + return; + } + this.position = position; + } + + @SuppressWarnings("unchecked") + public I as(Class entityClass) { + return entityClass.isInstance(this) ? (I) this : null; + } + + public boolean is(Class entityClass) { + return entityClass.isInstance(this); + } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java index ff3a2ed4d..4c65fdabe 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java @@ -39,10 +39,12 @@ import java.util.UUID; @Getter @Setter public class PlayerEntity extends Entity { + private GameProfile profile; private UUID uuid; private String username; + private long lastSkinUpdate = -1; - private ItemData hand; + private ItemData hand = ItemData.of(0, (short) 0, 0); private ItemData helmet; private ItemData chestplate; @@ -52,8 +54,10 @@ public class PlayerEntity extends Entity { public PlayerEntity(GameProfile gameProfile, long entityId, long geyserId, Vector3f position, Vector3f motion, Vector3f rotation) { super(entityId, geyserId, EntityType.PLAYER, position, motion, rotation); + profile = gameProfile; uuid = gameProfile.getId(); username = gameProfile.getName(); + if (geyserId == 1) valid = true; } // TODO: Break this into an EquippableEntity class @@ -72,25 +76,28 @@ public class PlayerEntity extends Entity { @Override public void spawnEntity(GeyserSession session) { + if (geyserId == 1) return; + AddPlayerPacket addPlayerPacket = new AddPlayerPacket(); - addPlayerPacket.setRuntimeEntityId(geyserId); - addPlayerPacket.setUniqueEntityId(geyserId); addPlayerPacket.setUuid(uuid); addPlayerPacket.setUsername(username); - addPlayerPacket.setPlatformChatId(""); + addPlayerPacket.setRuntimeEntityId(geyserId); + addPlayerPacket.setUniqueEntityId(geyserId); addPlayerPacket.setPosition(position); - addPlayerPacket.setMotion(motion); addPlayerPacket.setRotation(rotation); + addPlayerPacket.setMotion(motion); addPlayerPacket.setHand(hand); - addPlayerPacket.getMetadata().putAll(getMetadata()); addPlayerPacket.setPlayerFlags(0); addPlayerPacket.setCommandPermission(0); addPlayerPacket.setWorldFlags(0); addPlayerPacket.setPlayerPermission(0); addPlayerPacket.setCustomFlags(0); - addPlayerPacket.setDeviceId("WIN10"); + addPlayerPacket.setDeviceId(""); + addPlayerPacket.setPlatformChatId(""); + addPlayerPacket.getMetadata().putAll(getMetadata()); valid = true; session.getUpstream().sendPacket(addPlayerPacket); +// System.out.println("Spawned player "+uuid+" "+username+" "+geyserId); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java index 929ed9d5b..b11652430 100644 --- a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java +++ b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java @@ -40,8 +40,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { } private boolean translateAndDefault(BedrockPacket packet) { - Registry.BEDROCK.translate(packet.getClass(), packet, session); - return defaultHandler(packet); + return Registry.BEDROCK.translate(packet.getClass(), packet, session); } @Override @@ -66,7 +65,6 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { @Override public boolean handle(ResourcePackClientResponsePacket textPacket) { - connector.getLogger().debug("Handled " + textPacket.getClass().getSimpleName()); switch (textPacket.getStatus()) { case COMPLETED: session.connect(connector.getRemoteServer()); @@ -88,7 +86,6 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { @Override public boolean handle(ModalFormResponsePacket packet) { - connector.getLogger().debug("Handled packet: " + packet.getClass().getSimpleName()); return LoginEncryptionUtils.authenticateFromForm(session, connector, packet.getFormData()); } @@ -111,7 +108,6 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { @Override public boolean handle(MovePlayerPacket packet) { - connector.getLogger().debug("Handled packet: " + packet.getClass().getSimpleName()); if (!session.isLoggedIn() && !session.isLoggingIn()) { // TODO it is safer to key authentication on something that won't change (UUID, not username) if (!couldLoginUserByName(session.getAuthenticationData().getName())) { @@ -119,7 +115,8 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { } // else we were able to log the user in return true; - } else if (session.isLoggingIn()) { + } + if (session.isLoggingIn()) { session.sendMessage("Please wait until you are logged in..."); } @@ -127,27 +124,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { } @Override - public boolean handle(AnimatePacket packet) { - return translateAndDefault(packet); - } - - @Override - public boolean handle(CommandRequestPacket packet) { - return translateAndDefault(packet); - } - - @Override - public boolean handle(TextPacket packet) { - return translateAndDefault(packet); - } - - @Override - public boolean handle(MobEquipmentPacket packet) { - return translateAndDefault(packet); - } - - @Override - public boolean handle(PlayerActionPacket packet) { + boolean defaultHandler(BedrockPacket packet) { return translateAndDefault(packet); } } \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index 0ad983b53..d84f61a71 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -103,7 +103,7 @@ public class GeyserSession implements Player { this.scoreboardCache = new ScoreboardCache(this); this.windowCache = new WindowCache(this); - this.playerEntity = new PlayerEntity(new GameProfile(UUID.randomUUID(), "unknown"), -1, 1, new Vector3f(0, 0, 0), new Vector3f(0, 0, 0), new Vector3f(0, 0, 0)); + this.playerEntity = new PlayerEntity(new GameProfile(UUID.randomUUID(), "unknown"), 1, 1, Vector3f.ZERO, Vector3f.ZERO, Vector3f.ZERO); this.inventory = new PlayerInventory(); this.javaPacketCache = new DataCache<>(); diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityCache.java index a619533d3..1d8c15d48 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityCache.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityCache.java @@ -46,6 +46,7 @@ public class EntityCache { private Map entities = new HashMap<>(); private Map entityIdTranslations = new HashMap<>(); private Map playerEntities = new HashMap<>(); + private Map bossbars = new HashMap<>(); @Getter private AtomicLong nextEntityId = new AtomicLong(2L); @@ -62,13 +63,13 @@ public class EntityCache { } public void removeEntity(Entity entity) { - if (entity == null) return; + if (entity == null || !entity.isValid()) return; Long geyserId = entityIdTranslations.remove(entity.getEntityId()); if (geyserId != null) { entities.remove(geyserId); - if (entity instanceof PlayerEntity) { - playerEntities.remove(((PlayerEntity) entity).getUuid()); + if (entity.is(PlayerEntity.class)) { + playerEntities.remove(entity.as(PlayerEntity.class).getUuid()); } } entity.despawnEntity(session); @@ -93,4 +94,18 @@ public class EntityCache { public void removePlayerEntity(UUID uuid) { playerEntities.remove(uuid); } + + public long addBossBar(UUID uuid) { + long entityId = getNextEntityId().incrementAndGet(); + bossbars.put(uuid, entityId); + return entityId; + } + + public long getBossBar(UUID uuid) { + return bossbars.containsKey(uuid) ? bossbars.get(uuid) : -1; + } + + public long removeBossBar(UUID uuid) { + return bossbars.remove(uuid); + } } diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/ScoreboardCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/ScoreboardCache.java index 147a5355d..c24a0a9d5 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/cache/ScoreboardCache.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/ScoreboardCache.java @@ -25,27 +25,31 @@ package org.geysermc.connector.network.session.cache; -import com.nukkitx.protocol.bedrock.packet.RemoveObjectivePacket; import lombok.Getter; -import lombok.Setter; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.scoreboard.Objective; import org.geysermc.connector.scoreboard.Scoreboard; -public class ScoreboardCache { +import java.util.Collection; +@Getter +public class ScoreboardCache { private GeyserSession session; + private Scoreboard scoreboard; public ScoreboardCache(GeyserSession session) { this.session = session; + this.scoreboard = new Scoreboard(session); } - @Getter - @Setter - private Scoreboard scoreboard; - public void removeScoreboard() { - RemoveObjectivePacket removeObjectivePacket = new RemoveObjectivePacket(); - removeObjectivePacket.setObjectiveId(scoreboard.getObjective().getObjectiveName()); - session.getUpstream().sendPacket(removeObjectivePacket); + if (scoreboard != null) { + Collection objectives = scoreboard.getObjectives().values(); + scoreboard = new Scoreboard(session); + + for (Objective objective : objectives) { + scoreboard.despawnObjective(objective); + } + } } } \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/Registry.java b/connector/src/main/java/org/geysermc/connector/network/translators/Registry.java index a4efde575..e13ce9748 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/Registry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/Registry.java @@ -34,7 +34,6 @@ import java.util.HashMap; import java.util.Map; public class Registry { - private final Map, PacketTranslator> MAP = new HashMap<>(); public static final Registry JAVA = new Registry<>(); @@ -48,14 +47,15 @@ public class Registry { BEDROCK.MAP.put(clazz, translator); } - public

void translate(Class clazz, P packet, GeyserSession session) { + public

boolean translate(Class clazz, P packet, GeyserSession session) { try { if (MAP.containsKey(clazz)) { ((PacketTranslator

) MAP.get(clazz)).translate(packet, session); + return true; } } catch (NullPointerException ex) { - GeyserLogger.DEFAULT.debug("Could not translate packet " + packet.getClass().getSimpleName()); - ex.printStackTrace(); + GeyserLogger.DEFAULT.error("Could not translate packet " + packet.getClass().getSimpleName(), ex); } + return false; } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java b/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java index 88e142cd0..b48483a41 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java @@ -34,6 +34,7 @@ import com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.Serv import com.github.steveice10.mc.protocol.packet.ingame.server.entity.spawn.*; import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerDisplayScoreboardPacket; import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerScoreboardObjectivePacket; +import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerTeamPacket; import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerUpdateScorePacket; import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerOpenWindowPacket; import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerSetSlotPacket; @@ -50,11 +51,7 @@ import org.geysermc.connector.network.translators.block.BlockTranslator; import org.geysermc.connector.network.translators.inventory.GenericInventoryTranslator; import org.geysermc.connector.network.translators.inventory.InventoryTranslator; import org.geysermc.connector.network.translators.item.ItemTranslator; -import org.geysermc.connector.network.translators.java.JavaChatTranslator; -import org.geysermc.connector.network.translators.java.JavaDifficultyTranslator; -import org.geysermc.connector.network.translators.java.JavaJoinGameTranslator; -import org.geysermc.connector.network.translators.java.JavaRespawnTranslator; -import org.geysermc.connector.network.translators.java.JavaTitleTranslator; +import org.geysermc.connector.network.translators.java.*; import org.geysermc.connector.network.translators.java.entity.*; import org.geysermc.connector.network.translators.java.entity.player.JavaPlayerHealthTranslator; import org.geysermc.connector.network.translators.java.entity.player.JavaPlayerPositionRotationTranslator; @@ -62,6 +59,7 @@ import org.geysermc.connector.network.translators.java.entity.player.JavaPlayerS import org.geysermc.connector.network.translators.java.entity.spawn.*; import org.geysermc.connector.network.translators.java.scoreboard.JavaDisplayScoreboardTranslator; import org.geysermc.connector.network.translators.java.scoreboard.JavaScoreboardObjectiveTranslator; +import org.geysermc.connector.network.translators.java.scoreboard.JavaTeamTranslator; import org.geysermc.connector.network.translators.java.scoreboard.JavaUpdateScoreTranslator; import org.geysermc.connector.network.translators.java.window.JavaOpenWindowTranslator; import org.geysermc.connector.network.translators.java.window.JavaSetSlotTranslator; @@ -119,6 +117,7 @@ public class TranslatorsInit { Registry.registerJava(ServerEntityRotationPacket.class, new JavaEntityRotationTranslator()); Registry.registerJava(ServerEntityHeadLookPacket.class, new JavaEntityHeadLookTranslator()); Registry.registerJava(ServerEntityMetadataPacket.class, new JavaEntityMetadataTranslator()); + Registry.registerJava(ServerBossBarPacket.class, new JavaBossBarTranslator()); Registry.registerJava(ServerSpawnExpOrbPacket.class, new JavaSpawnExpOrbTranslator()); Registry.registerJava(ServerSpawnGlobalEntityPacket.class, new JavaSpawnGlobalEntityTranslator()); @@ -141,6 +140,7 @@ public class TranslatorsInit { Registry.registerJava(ServerScoreboardObjectivePacket.class, new JavaScoreboardObjectiveTranslator()); Registry.registerJava(ServerDisplayScoreboardPacket.class, new JavaDisplayScoreboardTranslator()); Registry.registerJava(ServerUpdateScorePacket.class, new JavaUpdateScoreTranslator()); + Registry.registerJava(ServerTeamPacket.class, new JavaTeamTranslator()); Registry.registerJava(ServerBlockChangePacket.class, new JavaBlockChangeTranslator()); Registry.registerJava(ServerMultiBlockChangePacket.class, new JavaMultiBlockChangeTranslator()); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockMovePlayerTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockMovePlayerTranslator.java index d74bb8337..a09d0daa3 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockMovePlayerTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockMovePlayerTranslator.java @@ -49,15 +49,17 @@ public class BedrockMovePlayerTranslator extends PacketTranslator { + @Override + public void translate(ServerBossBarPacket packet, GeyserSession session) { + BossEventPacket bossEventPacket = new BossEventPacket(); + bossEventPacket.setBossUniqueEntityId(session.getEntityCache().getBossBar(packet.getUUID())); + + switch (packet.getAction()) { + case ADD: + long entityId = session.getEntityCache().addBossBar(packet.getUUID()); + addBossEntity(session, entityId); + + bossEventPacket.setType(BossEventPacket.Type.SHOW); + bossEventPacket.setBossUniqueEntityId(entityId); + bossEventPacket.setTitle(MessageUtils.getBedrockMessage(packet.getTitle())); + bossEventPacket.setHealthPercentage(packet.getHealth()); + bossEventPacket.setColor(0); //ignored by client + bossEventPacket.setOverlay(1); + bossEventPacket.setDarkenSky(0); + break; + case UPDATE_TITLE: + bossEventPacket.setType(BossEventPacket.Type.TITLE); + bossEventPacket.setTitle(MessageUtils.getBedrockMessage(packet.getTitle())); + break; + case UPDATE_HEALTH: + bossEventPacket.setType(BossEventPacket.Type.HEALTH_PERCENTAGE); + bossEventPacket.setHealthPercentage(packet.getHealth()); + break; + case REMOVE: + bossEventPacket.setType(BossEventPacket.Type.HIDE); + removeBossEntity(session, session.getEntityCache().removeBossBar(packet.getUUID())); + break; + case UPDATE_STYLE: + case UPDATE_FLAGS: + //todo + return; + } + + session.getUpstream().sendPacket(bossEventPacket); + } + + /** + * Bedrock still needs an entity to display the BossBar.
+ * Just like 1.8 but it doesn't care about which entity + */ + private void addBossEntity(GeyserSession session, long entityId) { + AddEntityPacket addEntityPacket = new AddEntityPacket(); + addEntityPacket.setUniqueEntityId(entityId); + addEntityPacket.setRuntimeEntityId(entityId); + addEntityPacket.setIdentifier("minecraft:creeper"); + addEntityPacket.setEntityType(33); + addEntityPacket.setPosition(session.getPlayerEntity().getPosition()); + addEntityPacket.setRotation(Vector3f.ZERO); + addEntityPacket.setMotion(Vector3f.ZERO); + addEntityPacket.getMetadata().put(EntityData.SCALE, 0.01F); // scale = 0 doesn't work? + + session.getUpstream().sendPacket(addEntityPacket); + } + + private void removeBossEntity(GeyserSession session, long entityId) { + RemoveEntityPacket removeEntityPacket = new RemoveEntityPacket(); + removeEntityPacket.setUniqueEntityId(entityId); + + session.getUpstream().sendPacket(removeEntityPacket); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityDestroyTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityDestroyTranslator.java index 55da3c733..065463235 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityDestroyTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityDestroyTranslator.java @@ -36,7 +36,10 @@ public class JavaEntityDestroyTranslator extends PacketTranslator { - private static ProvidedSkinData providedSkinData = ProvidedSkinData.getProvidedSkin("bedrock/skin/model_steve.json"); private static byte[] providedSkin = new ProvidedSkin("bedrock/skin/skin_steve.png").getSkin(); @Override @@ -26,8 +25,13 @@ public class JavaPlayerListEntryTranslator extends PacketTranslator { @@ -41,7 +48,7 @@ public class JavaSpawnPlayerTranslator extends PacketTranslator { + GameProfile.Property skinProperty = entity.getProfile().getProperty("textures"); + + JsonObject skinObject = SkinProvider.getGson().fromJson(new String(Base64.getDecoder().decode(skinProperty.getValue()), Charsets.UTF_8), JsonObject.class); + JsonObject textures = skinObject.getAsJsonObject("textures"); + + JsonObject skinTexture = textures.getAsJsonObject("SKIN"); + String skinUrl = skinTexture.get("url").getAsString(); + + boolean isAlex = skinTexture.has("metadata"); + + String capeUrl = null; + if (textures.has("CAPE")) { + JsonObject capeTexture = textures.getAsJsonObject("CAPE"); + capeUrl = capeTexture.get("url").getAsString(); + } + + SkinProvider.requestAndHandleSkinAndCape(entity.getUuid(), skinUrl, capeUrl) + .whenCompleteAsync((skinAndCape, throwable) -> { + SkinProvider.Skin skin = skinAndCape.getSkin(); + SkinProvider.Cape cape = skinAndCape.getCape(); + + if (entity.getLastSkinUpdate() < skin.getRequestedOn()) { + Geyser.getLogger().debug("Received Skin for " + entity.getUuid() + ", updating player.."); + entity.setLastSkinUpdate(skin.getRequestedOn()); + + PlayerListPacket.Entry updatedEntry = new PlayerListPacket.Entry(skin.getSkinOwner()); + updatedEntry.setName(entity.getUsername()); + updatedEntry.setEntityId(entity.getGeyserId()); + updatedEntry.setSkinId(entity.getUuid().toString()); + updatedEntry.setSkinData(skin.getSkinData()); + updatedEntry.setCapeData(cape.getCapeData()); + updatedEntry.setGeometryName("geometry.humanoid.custom" + (isAlex ? "Slim" : "")); + updatedEntry.setGeometryData(""); + updatedEntry.setXuid(""); + updatedEntry.setPlatformChatId(""); + + PlayerListPacket playerRemovePacket = new PlayerListPacket(); + playerRemovePacket.setType(PlayerListPacket.Type.REMOVE); + playerRemovePacket.getEntries().add(updatedEntry); + session.getUpstream().sendPacket(playerRemovePacket); + + PlayerListPacket playerAddPacket = new PlayerListPacket(); + playerAddPacket.setType(PlayerListPacket.Type.ADD); + playerAddPacket.getEntries().add(updatedEntry); + session.getUpstream().sendPacket(playerAddPacket); + } + }).isCompletedExceptionally(); + }); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaDisplayScoreboardTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaDisplayScoreboardTranslator.java index 429857aa3..cc4a04757 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaDisplayScoreboardTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaDisplayScoreboardTranslator.java @@ -27,20 +27,14 @@ package org.geysermc.connector.network.translators.java.scoreboard; import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerDisplayScoreboardPacket; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.session.cache.ScoreboardCache; import org.geysermc.connector.network.translators.PacketTranslator; -import org.geysermc.connector.scoreboard.Scoreboard; public class JavaDisplayScoreboardTranslator extends PacketTranslator { @Override public void translate(ServerDisplayScoreboardPacket packet, GeyserSession session) { - try { - ScoreboardCache cache = session.getScoreboardCache(); - Scoreboard scoreboard = new Scoreboard(session); - cache.setScoreboard(scoreboard); - } catch (Exception ex) { - ex.printStackTrace(); - } + session.getScoreboardCache().getScoreboard().registerNewObjective( + packet.getScoreboardName(), packet.getPosition() + ); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaScoreboardObjectiveTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaScoreboardObjectiveTranslator.java index fb60217cd..89fdec79c 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaScoreboardObjectiveTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaScoreboardObjectiveTranslator.java @@ -30,7 +30,7 @@ import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.cache.ScoreboardCache; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.scoreboard.Scoreboard; -import org.geysermc.connector.scoreboard.ScoreboardObjective; +import org.geysermc.connector.scoreboard.Objective; import org.geysermc.connector.utils.MessageUtils; public class JavaScoreboardObjectiveTranslator extends PacketTranslator { @@ -39,18 +39,21 @@ public class JavaScoreboardObjectiveTranslator extends PacketTranslator { @Override public void translate(ServerTeamPacket packet, GeyserSession session) { + Geyser.getLogger().debug("Team packet " + packet.getTeamName() + " " + packet.getAction()+" "+ Arrays.toString(packet.getPlayers())); + Scoreboard scoreboard = session.getScoreboardCache().getScoreboard(); + switch (packet.getAction()) { + case CREATE: + scoreboard.registerNewTeam(packet.getTeamName(), toPlayerSet(packet.getPlayers())) + .setName(MessageUtils.getBedrockMessage(packet.getDisplayName())) + .setPrefix(MessageUtils.getBedrockMessage(packet.getPrefix())) + .setSuffix(MessageUtils.getBedrockMessage(packet.getSuffix())); + break; + case UPDATE: + scoreboard.getTeam(packet.getTeamName()) + .setName(MessageUtils.getBedrockMessage(packet.getDisplayName())) + .setPrefix(MessageUtils.getBedrockMessage(packet.getPrefix())) + .setSuffix(MessageUtils.getBedrockMessage(packet.getSuffix())) + .setUpdateType(UpdateType.UPDATE); + break; + case ADD_PLAYER: + scoreboard.getTeam(packet.getTeamName()).addEntities(packet.getPlayers()); + break; + case REMOVE_PLAYER: + scoreboard.getTeam(packet.getTeamName()).removeEntities(packet.getPlayers()); + break; + case REMOVE: + scoreboard.removeTeam(packet.getTeamName()); + break; + } + scoreboard.onUpdate(); + } + + private Set toPlayerSet(String[] players) { + return new HashSet<>(Arrays.asList(players)); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaUpdateScoreTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaUpdateScoreTranslator.java index 83b01ae02..dae080a3b 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaUpdateScoreTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaUpdateScoreTranslator.java @@ -26,37 +26,33 @@ package org.geysermc.connector.network.translators.java.scoreboard; import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerUpdateScorePacket; -import com.nukkitx.protocol.bedrock.packet.SetScorePacket; +import org.geysermc.api.Geyser; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.session.cache.ScoreboardCache; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.scoreboard.Scoreboard; -import org.geysermc.connector.scoreboard.ScoreboardObjective; +import org.geysermc.connector.scoreboard.Objective; public class JavaUpdateScoreTranslator extends PacketTranslator { @Override public void translate(ServerUpdateScorePacket packet, GeyserSession session) { try { - ScoreboardCache cache = session.getScoreboardCache(); - Scoreboard scoreboard = new Scoreboard(session); - if (cache.getScoreboard() != null) - scoreboard = cache.getScoreboard(); + Scoreboard scoreboard = session.getScoreboardCache().getScoreboard(); - ScoreboardObjective objective = scoreboard.getObjective(packet.getObjective()); + Objective objective = scoreboard.getObjective(packet.getObjective()); if (objective == null) { - objective = scoreboard.registerNewObjective(packet.getObjective()); + Geyser.getLogger().info("Tried to update score without the existence of its requested objective"); + return; } switch (packet.getAction()) { case REMOVE: - objective.registerScore(packet.getEntry(), packet.getEntry(), packet.getValue(), SetScorePacket.Action.REMOVE); + objective.resetScore(packet.getEntry()); break; case ADD_OR_UPDATE: - objective.registerScore(packet.getEntry(), packet.getEntry(), packet.getValue(), SetScorePacket.Action.SET); + objective.setScore(packet.getEntry(), packet.getValue()); break; } - cache.setScoreboard(scoreboard); scoreboard.onUpdate(); } catch (Exception ex) { ex.printStackTrace(); diff --git a/connector/src/main/java/org/geysermc/connector/scoreboard/Objective.java b/connector/src/main/java/org/geysermc/connector/scoreboard/Objective.java new file mode 100644 index 000000000..d48391813 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/scoreboard/Objective.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2019 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.connector.scoreboard; + +import com.github.steveice10.mc.protocol.data.game.scoreboard.ScoreboardPosition; +import lombok.Getter; +import lombok.Setter; + +import java.util.HashMap; +import java.util.Map; + +@Getter +public class Objective { + private Scoreboard scoreboard; + private long id; + + @Setter + private UpdateType updateType = UpdateType.ADD; + private String objectiveName; + private String displaySlot; + private String displayName; + private int type; // 0 = integer, 1 = heart + + private Map scores = new HashMap<>(); + + public Objective(Scoreboard scoreboard, String objectiveName, ScoreboardPosition displaySlot, String displayName, int type) { + this(scoreboard, objectiveName, displaySlot.name().toLowerCase(), displayName, type); + } + + public Objective(Scoreboard scoreboard, String objectiveName, String displaySlot, String displayName, int type) { + this.scoreboard = scoreboard; + this.id = scoreboard.getNextId().getAndIncrement(); + this.objectiveName = objectiveName; + this.displaySlot = displaySlot; + this.displayName = displayName; + this.type = type; + } + + public void registerScore(String id, int score) { + if (!scores.containsKey(id)) { + Score score1 = new Score(this, id).setScore(score); + Team team = scoreboard.getTeamFor(id); + if (team != null) score1.setTeam(team); + scores.put(id, score1); + } + } + + public void setScore(String id, int score) { + if (scores.containsKey(id)) { + scores.get(id).setScore(score).setUpdateType(UpdateType.ADD); + } else { + registerScore(id, score); + } + } + + public void setScoreText(String oldText, String newText) { + if (!scores.containsKey(oldText) || oldText.equals(newText)) return; + Score oldScore = scores.get(oldText); + + Score newScore = new Score(this, newText).setScore(oldScore.getScore()); + Team team = scoreboard.getTeamFor(newText); + if (team != null) newScore.setTeam(team); + + scores.put(newText, newScore); + oldScore.setUpdateType(UpdateType.REMOVE); + } + + public int getScore(String id) { + if (scores.containsKey(id)) { + return scores.get(id).getScore(); + } + return 0; + } + + public Score getScore(int line) { + for (Score score : scores.values()) { + if (score.getScore() == line) return score; + } + return null; + } + + public void resetScore(String id) { + if (scores.containsKey(id)) { + scores.get(id).setUpdateType(UpdateType.REMOVE); + } + } + + public void removeScore(String id) { + scores.remove(id); + } + + public Objective setDisplayName(String displayName) { + this.displayName = displayName; + if (updateType == UpdateType.NOTHING) updateType = UpdateType.UPDATE; + return this; + } + + public Objective setType(int type) { + this.type = type; + if (updateType == UpdateType.NOTHING) updateType = UpdateType.UPDATE; + return this; + } +} diff --git a/connector/src/main/java/org/geysermc/connector/scoreboard/Score.java b/connector/src/main/java/org/geysermc/connector/scoreboard/Score.java index 9535b0947..9e4966582 100644 --- a/connector/src/main/java/org/geysermc/connector/scoreboard/Score.java +++ b/connector/src/main/java/org/geysermc/connector/scoreboard/Score.java @@ -25,43 +25,32 @@ package org.geysermc.connector.scoreboard; -import com.nukkitx.protocol.bedrock.packet.SetScorePacket; import lombok.Getter; import lombok.Setter; +import lombok.experimental.Accessors; -import java.util.Random; - -/** - * Adapted from: https://github.com/Ragnok123/GTScoreboard - */ +@Getter @Setter +@Accessors(chain = true) public class Score { + private Objective objective; + private long id; - @Getter - @Setter + private UpdateType updateType = UpdateType.ADD; + private String name; + private Team team; private int score; - @Getter - private long scoreboardId; - private ScoreboardObjective objective; - - @Getter - @Setter - private String fakePlayer; - - @Getter - @Setter - private SetScorePacket.Action action = SetScorePacket.Action.SET; - - private boolean modified = false; - - @Getter - @Setter - private String fakeId; - - public Score(ScoreboardObjective objective, String fakePlayer) { - this.scoreboardId = -new Random().nextLong(); + public Score(Objective objective, String name) { + this.id = objective.getScoreboard().getNextId().getAndIncrement(); this.objective = objective; - this.fakePlayer = fakePlayer; + this.name = name; + } + + public String getDisplayName() { + if (team != null) { + return team.getPrefix() + name + team.getSuffix(); + } + return name; } } diff --git a/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java b/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java index 6b6337993..22b080cb6 100644 --- a/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java +++ b/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java @@ -25,111 +25,178 @@ package org.geysermc.connector.scoreboard; +import com.github.steveice10.mc.protocol.data.game.scoreboard.ScoreboardPosition; import com.nukkitx.protocol.bedrock.data.ScoreInfo; import com.nukkitx.protocol.bedrock.packet.RemoveObjectivePacket; import com.nukkitx.protocol.bedrock.packet.SetDisplayObjectivePacket; import com.nukkitx.protocol.bedrock.packet.SetScorePacket; import lombok.Getter; -import lombok.Setter; +import org.geysermc.api.Geyser; +import org.geysermc.connector.console.GeyserLogger; import org.geysermc.connector.network.session.GeyserSession; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.Random; +import java.util.*; +import java.util.concurrent.atomic.AtomicLong; -/** - * Adapted from: https://github.com/Ragnok123/GTScoreboard - */ +import static org.geysermc.connector.scoreboard.UpdateType.*; + +@Getter public class Scoreboard { - - @Getter - private ScoreboardObjective objective; - private GeyserSession session; + private AtomicLong nextId = new AtomicLong(0); - @Getter - @Setter - private long id; - - private Map objectiveMap = new HashMap(); + private Map objectives = new HashMap<>(); + private Map teams = new HashMap<>(); public Scoreboard(GeyserSession session) { this.session = session; - - id = new Random().nextLong(); } - public ScoreboardObjective registerNewObjective(String objectiveName) { - ScoreboardObjective objective = new ScoreboardObjective(); - objective.setObjectiveName(objectiveName); - this.objective = objective; - if (!objectiveMap.containsKey(objectiveName)) { - objectiveMap.put(objectiveName, objective); - } - + public Objective registerNewObjective(String objectiveId, ScoreboardPosition displaySlot) { + Objective objective = new Objective(this, objectiveId, displaySlot, "unknown", 0); + if (objectives.containsKey(objectiveId)) despawnObjective(objectives.get(objectiveId)); + objectives.put(objectiveId, objective); return objective; } - public ScoreboardObjective getObjective(String objectiveName) { - ScoreboardObjective objective = null; - if (objectiveMap.containsKey(objectiveName) && this.objective.getObjectiveName().contains(objectiveName)) { - objective = this.objective; + public Team registerNewTeam(String teamName, Set players) { + if (teams.containsKey(teamName)) { + Geyser.getLogger().info("Ignoring team " + teamName + ". It overrides without removing old team."); + return getTeam(teamName); } - return objective; + Team team = new Team(this, teamName).setEntities(players); + teams.put(teamName, team); + + for (Objective objective : objectives.values()) { + for (Score score : objective.getScores().values()) { + if (players.contains(score.getName())) { + score.setTeam(team).setUpdateType(ADD); + } + } + } + return team; } - public void setObjective(String objectiveName) { - if (objectiveMap.containsKey(objectiveName)) - objective = objectiveMap.get(objectiveName); + public Objective getObjective(String objectiveName) { + return objectives.get(objectiveName); + } + + public Team getTeam(String teamName) { + return teams.get(teamName); } public void unregisterObjective(String objectiveName) { - if (!objectiveMap.containsKey(objectiveName)) - return; - - if (objective.getObjectiveName().equals(objectiveName)) { - objective = null; - } - - objectiveMap.remove(objectiveName); + Objective objective = getObjective(objectiveName); + if (objective != null) objective.setUpdateType(REMOVE); } - public void onUpdate() { - if (objective == null) - return; - - RemoveObjectivePacket removeObjectivePacket = new RemoveObjectivePacket(); - removeObjectivePacket.setObjectiveId(objective.getObjectiveName()); - session.getUpstream().sendPacket(removeObjectivePacket); - - SetDisplayObjectivePacket displayObjectivePacket = new SetDisplayObjectivePacket(); - displayObjectivePacket.setObjectiveId(objective.getObjectiveName()); - displayObjectivePacket.setDisplayName(objective.getDisplayName()); - displayObjectivePacket.setCriteria("dummy"); - displayObjectivePacket.setDisplaySlot("sidebar"); - displayObjectivePacket.setSortOrder(1); - session.getUpstream().sendPacket(displayObjectivePacket); - - Map fakeMap = new HashMap(); - for (Map.Entry entry : objective.getScores().entrySet()) { - fakeMap.put(entry.getKey(), entry.getValue()); - } - - for (String string : fakeMap.keySet()) { - Score score = fakeMap.get(string); - ScoreInfo scoreInfo = new ScoreInfo(score.getScoreboardId(), objective.getObjectiveName(), score.getScore(), score.getFakePlayer()); - - SetScorePacket setScorePacket = new SetScorePacket(); - setScorePacket.setAction(score.getAction()); - setScorePacket.setInfos(Arrays.asList(scoreInfo)); - session.getUpstream().sendPacket(setScorePacket); - - if (score.getAction() == SetScorePacket.Action.REMOVE) { - String id = score.getFakeId(); - objective.getScores().remove(id); + public void removeTeam(String teamName) { + if (teams.remove(teamName) != null) { + for (Objective objective : objectives.values()) { + for (Score score : objective.getScores().values()) { + if (score.getName().equals(teamName)) { + score.setTeam(null).setUpdateType(ADD); + } + } } } } + + public void onUpdate() { + Set changedObjectives = new HashSet<>(); + List addScores = new ArrayList<>(); + List removeScores = new ArrayList<>(); + + for (String objectiveId : new ArrayList<>(objectives.keySet())) { + Objective objective = objectives.get(objectiveId); + if (objective.getUpdateType() != NOTHING) changedObjectives.add(objective); + + for (String identifier : new HashSet<>(objective.getScores().keySet())) { + Score score = objective.getScores().get(identifier); + + boolean add = (objective.getUpdateType() != NOTHING && objective.getUpdateType() != REMOVE) && score.getUpdateType() != REMOVE || score.getUpdateType() == ADD; + boolean remove = (add && score.getUpdateType() != ADD && objective.getUpdateType() != ADD) || objective.getUpdateType() == REMOVE || score.getUpdateType() == REMOVE; + + ScoreInfo info = new ScoreInfo(score.getId(), score.getObjective().getObjectiveName(), score.getScore(), score.getDisplayName()); + if (add || (score.getTeam() != null && (score.getTeam().getUpdateType() == ADD || score.getTeam().getUpdateType() == UPDATE))) addScores.add(info); + if (remove || (score.getTeam() != null && score.getTeam().getUpdateType() != NOTHING)) removeScores.add(info); + + if (score.getUpdateType() == REMOVE) { + objective.removeScore(score.getName()); + } + + if (addScores.contains(info) || removeScores.contains(info)) changedObjectives.add(objective); + score.setUpdateType(NOTHING); + } + } + + for (Objective objective : changedObjectives) { + boolean update = objective.getUpdateType() == NOTHING || objective.getUpdateType() == UPDATE; + if (objective.getUpdateType() == REMOVE || update) { + RemoveObjectivePacket removeObjectivePacket = new RemoveObjectivePacket(); + removeObjectivePacket.setObjectiveId(objective.getObjectiveName()); + session.getUpstream().sendPacket(removeObjectivePacket); + if (objective.getUpdateType() == REMOVE) { + objectives.remove(objective.getObjectiveName()); // now we can deregister + } + } + if (objective.getUpdateType() == ADD || update) { + SetDisplayObjectivePacket displayObjectivePacket = new SetDisplayObjectivePacket(); + displayObjectivePacket.setObjectiveId(objective.getObjectiveName()); + displayObjectivePacket.setDisplayName(objective.getDisplayName()); + displayObjectivePacket.setCriteria("dummy"); + displayObjectivePacket.setDisplaySlot(objective.getDisplaySlot()); + displayObjectivePacket.setSortOrder(1); // ?? + session.getUpstream().sendPacket(displayObjectivePacket); + } + objective.setUpdateType(NOTHING); + } + + if (!removeScores.isEmpty()) { + SetScorePacket setScorePacket = new SetScorePacket(); + setScorePacket.setAction(SetScorePacket.Action.REMOVE); + setScorePacket.setInfos(removeScores); + session.getUpstream().sendPacket(setScorePacket); + } + + if (!addScores.isEmpty()) { + SetScorePacket setScorePacket = new SetScorePacket(); + setScorePacket.setAction(SetScorePacket.Action.SET); + setScorePacket.setInfos(addScores); + session.getUpstream().sendPacket(setScorePacket); + } + } + + public void despawnObjective(Objective objective) { + RemoveObjectivePacket removeObjectivePacket = new RemoveObjectivePacket(); + removeObjectivePacket.setObjectiveId(objective.getObjectiveName()); + session.getUpstream().sendPacket(removeObjectivePacket); + objectives.remove(objective.getDisplayName()); + + List toRemove = new ArrayList<>(); + for (String identifier : objective.getScores().keySet()) { + Score score = objective.getScores().get(identifier); + toRemove.add(new ScoreInfo( + score.getId(), score.getObjective().getObjectiveName(), + 0, "" + )); + } + + if (!toRemove.isEmpty()) { + SetScorePacket setScorePacket = new SetScorePacket(); + setScorePacket.setAction(SetScorePacket.Action.REMOVE); + setScorePacket.setInfos(toRemove); + session.getUpstream().sendPacket(setScorePacket); + } + } + + public Team getTeamFor(String entity) { + for (Team team : teams.values()) { + if (team.getEntities().contains(entity)) { + return team; + } + } + return null; + } } diff --git a/connector/src/main/java/org/geysermc/connector/scoreboard/ScoreboardObjective.java b/connector/src/main/java/org/geysermc/connector/scoreboard/ScoreboardObjective.java deleted file mode 100644 index f90c474e5..000000000 --- a/connector/src/main/java/org/geysermc/connector/scoreboard/ScoreboardObjective.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (c) 2019 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.connector.scoreboard; - -import com.nukkitx.protocol.bedrock.packet.SetScorePacket; -import lombok.Getter; -import lombok.Setter; - -import java.util.HashMap; -import java.util.Map; - -/** - * Adapted from: https://github.com/Ragnok123/GTScoreboard - */ -public class ScoreboardObjective { - - @Getter - @Setter - private int scoreboardTick = 0; - - @Getter - @Setter - private String objectiveName; - - @Getter - @Setter - private DisplaySlot displaySlot; - - @Getter - @Setter - private String displayName; - - @Getter - private Map scores = new HashMap(); - - public void registerScore(String id, String fake, int value) { - registerScore(id, fake, value, SetScorePacket.Action.SET); - } - - public void registerScore(String id, String fake, int value, SetScorePacket.Action action) { - Score score = new Score(this, fake); - score.setScore(value); - score.setFakeId(id); - score.setAction(action); - if (!scores.containsKey(id)) { - scores.put(id, score); - } else { - setScore(id, value); - } - } - - public void setScore(String id, int value) { - if (scores.containsKey(id)) { - Score modifiedScore = scores.get(id); - modifiedScore.setScore(value); - scores.remove(id); - scores.put(id, modifiedScore); - } - } - - public void setScoreText(String id, String text) { - if (scores.containsKey(id)) { - Score newScore = new Score(this, text); - newScore.setScore(scores.get(id).getScore()); - newScore.setFakeId(id); - scores.remove(id); - scores.put(id, newScore); - } - } - - public int getScore(String id) { - int i = 0; - if (scores.containsKey(id)) { - Score score = scores.get(id); - i = score.getScore(); - } - - return i; - } - - public Score getScore(int line) { - Score score = null; - for (Map.Entry entry : scores.entrySet()) { - if (entry.getValue().getScore() == line) - return entry.getValue(); - } - - return null; - } - - public void resetScore(String id) { - if (scores.containsKey(id)) { - Score modifiedScore = scores.get(id); - modifiedScore.setAction(SetScorePacket.Action.REMOVE); - scores.remove(id); - scores.put(id, modifiedScore); - } - } - - public enum DisplaySlot { - - SIDEBAR, - LIST, - BELOWNAME; - - } -} diff --git a/connector/src/main/java/org/geysermc/connector/scoreboard/Team.java b/connector/src/main/java/org/geysermc/connector/scoreboard/Team.java new file mode 100644 index 000000000..2106787ea --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/scoreboard/Team.java @@ -0,0 +1,64 @@ +package org.geysermc.connector.scoreboard; + +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +@Getter +@Setter +@Accessors(chain = true) +public class Team { + private final Scoreboard scoreboard; + private final String id; + + private UpdateType updateType = UpdateType.ADD; + private String name; + private String prefix; + private String suffix; + private Set entities = new HashSet<>(); + + + public Team(Scoreboard scoreboard, String id) { + this.scoreboard = scoreboard; + this.id = id; + } + + public void addEntities(String... names) { + List added = new ArrayList<>(); + for (String name : names) { + if (!entities.contains(name)) { + entities.add(name); + added.add(name); + } + } + for (Objective objective : scoreboard.getObjectives().values()) { + for (Score score : objective.getScores().values()) { + if (added.contains(score.getName())) { + score.setTeam(this).setUpdateType(UpdateType.ADD); + } + } + } + } + + public void removeEntities(String... names) { + List removed = new ArrayList<>(); + for (String name : names) { + if (entities.contains(name)) { + entities.remove(name); + removed.add(name); + } + } + for (Objective objective : scoreboard.getObjectives().values()) { + for (Score score : objective.getScores().values()) { + if (removed.contains(score.getName())) { + score.setTeam(null).setUpdateType(UpdateType.ADD); + } + } + } + } +} diff --git a/connector/src/main/java/org/geysermc/connector/scoreboard/UpdateType.java b/connector/src/main/java/org/geysermc/connector/scoreboard/UpdateType.java new file mode 100644 index 000000000..e0465db3f --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/scoreboard/UpdateType.java @@ -0,0 +1,15 @@ +package org.geysermc.connector.scoreboard; + +public enum UpdateType { + REMOVE, + /** + * Nothing has changed, it's cool + */ + NOTHING, + ADD, + /** + * Hey, something has been updated!
+ * Only used in {@link Objective Objective} + */ + UPDATE +} diff --git a/connector/src/main/java/org/geysermc/connector/utils/ProvidedSkinData.java b/connector/src/main/java/org/geysermc/connector/utils/ProvidedSkinData.java deleted file mode 100644 index 9618edcd9..000000000 --- a/connector/src/main/java/org/geysermc/connector/utils/ProvidedSkinData.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.geysermc.connector.utils; - -import com.fasterxml.jackson.core.JsonFactory; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import lombok.Getter; -import org.apache.commons.codec.Charsets; - -import java.util.Base64; - -@Getter -public class ProvidedSkinData { - private static final Gson gson = new GsonBuilder().create(); - private String skinId; - private String skinName; - private String geometryId; - private ObjectNode geometryData; - - public static ProvidedSkinData getProvidedSkin(String skinName) { - try { - ObjectMapper objectMapper = new ObjectMapper(new JsonFactory()); - return objectMapper.readValue(ProvidedSkinData.class.getClassLoader().getResource(skinName), ProvidedSkinData.class); - } catch (Exception ex) { - ex.printStackTrace(); - return null; - } - } - - public String getGeometryDataEncoded() { - try { - return new String(Base64.getEncoder().encode(geometryData.toString().getBytes(Charsets.UTF_8))); - } catch (Exception ex) { - ex.printStackTrace(); - return null; - } - } -} diff --git a/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java b/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java new file mode 100644 index 000000000..aab8574ac --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java @@ -0,0 +1,190 @@ +package org.geysermc.connector.utils; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.geysermc.api.Geyser; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.net.URL; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.*; + +public class SkinProvider { + private static final ExecutorService executorService = Executors.newFixedThreadPool(14); + @Getter private static final Gson gson = new GsonBuilder().create(); + + private static Map cachedSkins = new ConcurrentHashMap<>(); + private static Map cachedCapes = new ConcurrentHashMap<>(); + + public static final Skin EMPTY_SKIN = new Skin(-1, ""); + public static final Cape EMPTY_CAPE = new Cape("", new byte[0]); + private static final int CACHE_INTERVAL = 8 * 60 * 1000; // 8 minutes + + private static Map> requestedSkins = new ConcurrentHashMap<>(); + private static Map> requestedCapes = new ConcurrentHashMap<>(); + + public static boolean hasSkinCached(UUID uuid) { + return cachedSkins.containsKey(uuid); + } + + public static boolean hasCapeCached(String capeUrl) { + return cachedCapes.containsKey(capeUrl); + } + + public static Skin getCachedSkin(UUID uuid) { + return cachedSkins.get(uuid); + } + + public static Cape getCachedCape(String capeUrl) { + return cachedCapes.get(capeUrl); + } + + public static CompletableFuture requestAndHandleSkinAndCape(UUID playerId, String skinUrl, String capeUrl) { + return CompletableFuture.supplyAsync(() -> { + long time = System.currentTimeMillis(); + + SkinAndCape skinAndCape = new SkinAndCape( + getOrDefault(requestAndHandleSkin(playerId, skinUrl, false), EMPTY_SKIN, 5), + getOrDefault(requestAndHandleCape(capeUrl, false), EMPTY_CAPE, 5) + ); + + Geyser.getLogger().info("Took " + (System.currentTimeMillis() - time) + "ms for " + playerId); + return skinAndCape; + }, executorService); + } + + public static CompletableFuture requestAndHandleSkin(UUID playerId, String textureUrl, boolean newThread) { + if (textureUrl == null || textureUrl.isEmpty()) return CompletableFuture.completedFuture(EMPTY_SKIN); + if (requestedSkins.containsKey(playerId)) return requestedSkins.get(playerId); // already requested + + if ((System.currentTimeMillis() - CACHE_INTERVAL) < cachedSkins.getOrDefault(playerId, EMPTY_SKIN).getRequestedOn()) { + // no need to update, still cached + return CompletableFuture.completedFuture(cachedSkins.get(playerId)); + } + + CompletableFuture future; + if (newThread) { + future = CompletableFuture.supplyAsync(() -> supplySkin(playerId, textureUrl), executorService) + .whenCompleteAsync((skin, throwable) -> { + if (!cachedSkins.getOrDefault(playerId, EMPTY_SKIN).getTextureUrl().equals(textureUrl)) { + skin.updated = true; + cachedSkins.put(playerId, skin); + } + requestedSkins.remove(skin.getSkinOwner()); + }); + requestedSkins.put(playerId, future); + } else { + Skin skin = supplySkin(playerId, textureUrl); + future = CompletableFuture.completedFuture(skin); + cachedSkins.put(playerId, skin); + } + return future; + } + + public static CompletableFuture requestAndHandleCape(String capeUrl, boolean newThread) { + if (capeUrl == null || capeUrl.isEmpty()) return CompletableFuture.completedFuture(EMPTY_CAPE); + if (requestedCapes.containsKey(capeUrl)) return requestedCapes.get(capeUrl); // already requested + + if (cachedCapes.containsKey(capeUrl)) { + // no need to update the cache, capes are static :D + return CompletableFuture.completedFuture(cachedCapes.get(capeUrl)); + } + + CompletableFuture future; + if (newThread) { + future = CompletableFuture.supplyAsync(() -> supplyCape(capeUrl), executorService) + .whenCompleteAsync((cape, throwable) -> { + cachedCapes.put(capeUrl, cape); + requestedCapes.remove(capeUrl); + }); + requestedCapes.put(capeUrl, future); + } else { + Cape cape = supplyCape(capeUrl); // blocking + future = CompletableFuture.completedFuture(cape); + cachedCapes.put(capeUrl, cape); + } + return future; + } + + private static Skin supplySkin(UUID uuid, String textureUrl) { + byte[] skin = EMPTY_SKIN.getSkinData(); + try { + skin = requestImage(textureUrl); + } catch (Exception ignored) {} // just ignore I guess + return new Skin(uuid, textureUrl, skin, System.currentTimeMillis(), false); + } + + private static Cape supplyCape(String capeUrl) { + byte[] cape = EMPTY_CAPE.getCapeData(); + try { + cape = requestImage(capeUrl); + } catch (Exception ignored) {} // just ignore I guess + return new Cape(capeUrl, cape); + } + + private static byte[] requestImage(String imageUrl) throws Exception { + BufferedImage image = ImageIO.read(new URL(imageUrl)); + Geyser.getLogger().debug("Downloaded " + imageUrl); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(image.getWidth() * 4 + image.getHeight() * 4); + try { + for (int y = 0; y < image.getHeight(); y++) { + for (int x = 0; x < image.getWidth(); x++) { + int rgba = image.getRGB(x, y); + outputStream.write((rgba >> 16) & 0xFF); + outputStream.write((rgba >> 8) & 0xFF); + outputStream.write(rgba & 0xFF); + outputStream.write((rgba >> 24) & 0xFF); + } + } + image.flush(); + return outputStream.toByteArray(); + } finally { + try { + outputStream.close(); + } catch (IOException ignored) {} + } + } + + @AllArgsConstructor + @Getter + public static class SkinAndCape { + private Skin skin; + private Cape cape; + } + + @AllArgsConstructor + @Getter + public static class Skin { + private UUID skinOwner; + private String textureUrl; + private byte[] skinData = new byte[0]; + private long requestedOn; + private boolean updated; + + private Skin(long requestedOn, String textureUrl) { + this.requestedOn = requestedOn; + this.textureUrl = textureUrl; + } + } + + @AllArgsConstructor + @Getter + public static class Cape { + private String textureUrl; + private byte[] capeData; + } + + private static T getOrDefault(CompletableFuture future, T defaultValue, int timeoutInSeconds) { + try { + return future.get(timeoutInSeconds, TimeUnit.SECONDS); + } catch (Exception ignored) {} + return defaultValue; + } +} diff --git a/connector/src/main/resources/bedrock/skin/model_steve.json b/connector/src/main/resources/bedrock/skin/model_steve.json deleted file mode 100644 index 514308fe4..000000000 --- a/connector/src/main/resources/bedrock/skin/model_steve.json +++ /dev/null @@ -1,383 +0,0 @@ -{ - "skinId" : "c18e65aa-7b21-4637-9b63-8ad63622ef01_Custom", - "skinName" : "skin.Standard.Custom", - "geometryId" : "geometry.humanoid.custom", - "geometryData" : - { - "geometry.humanoid": { - "bones": [ - { - "name": "body", - "pivot": [ 0.0, 24.0, 0.0 ], - "cubes": [ - { - "origin": [ -4.0, 12.0, -2.0 ], - "size": [ 8, 12, 4 ], - "uv": [ 16, 16 ] - } - ] - }, - - { - "name": "waist", - "neverRender": true, - "pivot": [ 0.0, 12.0, 0.0 ] - }, - - { - "name": "head", - "pivot": [ 0.0, 24.0, 0.0 ], - "cubes": [ - { - "origin": [ -4.0, 24.0, -4.0 ], - "size": [ 8, 8, 8 ], - "uv": [ 0, 0 ] - } - ] - }, - - { - "name": "hat", - "pivot": [ 0.0, 24.0, 0.0 ], - "cubes": [ - { - "origin": [ -4.0, 24.0, -4.0 ], - "size": [ 8, 8, 8 ], - "uv": [ 32, 0 ], - "inflate": 0.5 - } - ], - "neverRender": true - }, - - { - "name": "rightArm", - "pivot": [ -5.0, 22.0, 0.0 ], - "cubes": [ - { - "origin": [ -8.0, 12.0, -2.0 ], - "size": [ 4, 12, 4 ], - "uv": [ 40, 16 ] - } - ] - }, - - { - "name": "leftArm", - "pivot": [ 5.0, 22.0, 0.0 ], - "cubes": [ - { - "origin": [ 4.0, 12.0, -2.0 ], - "size": [ 4, 12, 4 ], - "uv": [ 40, 16 ] - } - ], - "mirror": true - }, - - { - "name": "rightLeg", - "pivot": [ -1.9, 12.0, 0.0 ], - "cubes": [ - { - "origin": [ -3.9, 0.0, -2.0 ], - "size": [ 4, 12, 4 ], - "uv": [ 0, 16 ] - } - ] - }, - - { - "name": "leftLeg", - "pivot": [ 1.9, 12.0, 0.0 ], - "cubes": [ - { - "origin": [ -0.1, 0.0, -2.0 ], - "size": [ 4, 12, 4 ], - "uv": [ 0, 16 ] - } - ], - "mirror": true - } - ] - }, - - "geometry.cape": { - "texturewidth": 64, - "textureheight": 32, - - "bones": [ - { - "name": "cape", - "pivot": [ 0.0, 24.0, -3.0 ], - "cubes": [ - { - "origin": [ -5.0, 8.0, -3.0 ], - "size": [ 10, 16, 1 ], - "uv": [ 0, 0 ] - } - ], - "material": "alpha" - } - ] - }, - "geometry.humanoid.custom:geometry.humanoid": { - "bones": [ - { - "name": "hat", - "neverRender": false, - "material": "alpha", - "pivot": [ 0.0, 24.0, 0.0 ] - }, - { - "name": "leftArm", - "reset": true, - "mirror": false, - "pivot": [ 5.0, 22.0, 0.0 ], - "cubes": [ - { - "origin": [ 4.0, 12.0, -2.0 ], - "size": [ 4, 12, 4 ], - "uv": [ 32, 48 ] - } - ] - }, - - { - "name": "rightArm", - "reset": true, - "pivot": [ -5.0, 22.0, 0.0 ], - "cubes": [ - { - "origin": [ -8.0, 12.0, -2.0 ], - "size": [ 4, 12, 4 ], - "uv": [ 40, 16 ] - } - ] - }, - - { - "name": "rightItem", - "pivot": [ -6, 15, 1 ], - "neverRender": true, - "parent": "rightArm" - }, - - { - "name": "leftSleeve", - "pivot": [ 5.0, 22.0, 0.0 ], - "cubes": [ - { - "origin": [ 4.0, 12.0, -2.0 ], - "size": [ 4, 12, 4 ], - "uv": [ 48, 48 ], - "inflate": 0.25 - } - ], - "material": "alpha" - }, - - { - "name": "rightSleeve", - "pivot": [ -5.0, 22.0, 0.0 ], - "cubes": [ - { - "origin": [ -8.0, 12.0, -2.0 ], - "size": [ 4, 12, 4 ], - "uv": [ 40, 32 ], - "inflate": 0.25 - } - ], - "material": "alpha" - }, - - { - "name": "leftLeg", - "reset": true, - "mirror": false, - "pivot": [ 1.9, 12.0, 0.0 ], - "cubes": [ - { - "origin": [ -0.1, 0.0, -2.0 ], - "size": [ 4, 12, 4 ], - "uv": [ 16, 48 ] - } - ] - }, - - { - "name": "leftPants", - "pivot": [ 1.9, 12.0, 0.0 ], - "cubes": [ - { - "origin": [ -0.1, 0.0, -2.0 ], - "size": [ 4, 12, 4 ], - "uv": [ 0, 48 ], - "inflate": 0.25 - } - ], - "pos": [ 1.9, 12, 0 ], - "material": "alpha" - }, - - { - "name": "rightPants", - "pivot": [ -1.9, 12.0, 0.0 ], - "cubes": [ - { - "origin": [ -3.9, 0.0, -2.0 ], - "size": [ 4, 12, 4 ], - "uv": [ 0, 32 ], - "inflate": 0.25 - } - ], - "pos": [ -1.9, 12, 0 ], - "material": "alpha" - }, - - { - "name": "jacket", - "pivot": [ 0.0, 24.0, 0.0 ], - "cubes": [ - { - "origin": [ -4.0, 12.0, -2.0 ], - "size": [ 8, 12, 4 ], - "uv": [ 16, 32 ], - "inflate": 0.25 - } - ], - "material": "alpha" - } - ] - }, - "geometry.humanoid.customSlim:geometry.humanoid": { - - "bones": [ - { - "name": "hat", - "neverRender": false, - "material": "alpha" - }, - { - "name": "leftArm", - "reset": true, - "mirror": false, - "pivot": [ 5.0, 21.5, 0.0 ], - "cubes": [ - { - "origin": [ 4.0, 11.5, -2.0 ], - "size": [ 3, 12, 4 ], - "uv": [ 32, 48 ] - } - ] - }, - - { - "name": "rightArm", - "reset": true, - "pivot": [ -5.0, 21.5, 0.0 ], - "cubes": [ - { - "origin": [ -7.0, 11.5, -2.0 ], - "size": [ 3, 12, 4 ], - "uv": [ 40, 16 ] - } - ] - }, - - { - "pivot": [ -6, 14.5, 1 ], - "neverRender": true, - "name": "rightItem", - "parent": "rightArm" - }, - - { - "name": "leftSleeve", - "pivot": [ 5.0, 21.5, 0.0 ], - "cubes": [ - { - "origin": [ 4.0, 11.5, -2.0 ], - "size": [ 3, 12, 4 ], - "uv": [ 48, 48 ], - "inflate": 0.25 - } - ], - "material": "alpha" - }, - - { - "name": "rightSleeve", - "pivot": [ -5.0, 21.5, 0.0 ], - "cubes": [ - { - "origin": [ -7.0, 11.5, -2.0 ], - "size": [ 3, 12, 4 ], - "uv": [ 40, 32 ], - "inflate": 0.25 - } - ], - "material": "alpha" - }, - - { - "name": "leftLeg", - "reset": true, - "mirror": false, - "pivot": [ 1.9, 12.0, 0.0 ], - "cubes": [ - { - "origin": [ -0.1, 0.0, -2.0 ], - "size": [ 4, 12, 4 ], - "uv": [ 16, 48 ] - } - ] - }, - - { - "name": "leftPants", - "pivot": [ 1.9, 12.0, 0.0 ], - "cubes": [ - { - "origin": [ -0.1, 0.0, -2.0 ], - "size": [ 4, 12, 4 ], - "uv": [ 0, 48 ], - "inflate": 0.25 - } - ], - "material": "alpha" - }, - - { - "name": "rightPants", - "pivot": [ -1.9, 12.0, 0.0 ], - "cubes": [ - { - "origin": [ -3.9, 0.0, -2.0 ], - "size": [ 4, 12, 4 ], - "uv": [ 0, 32 ], - "inflate": 0.25 - } - ], - "material": "alpha" - }, - - { - "name": "jacket", - "pivot": [ 0.0, 24.0, 0.0 ], - "cubes": [ - { - "origin": [ -4.0, 12.0, -2.0 ], - "size": [ 8, 12, 4 ], - "uv": [ 16, 32 ], - "inflate": 0.25 - } - ], - "material": "alpha" - } - ] - } - - } - -} \ No newline at end of file From 5eb7565b0a836b8c0395449954f702aea767f7a1 Mon Sep 17 00:00:00 2001 From: Tim203 Date: Wed, 2 Oct 2019 22:45:29 +0200 Subject: [PATCH 2/8] Third party capes, rotation fixed, wait for the client and updated protocol lib --- connector/pom.xml | 2 +- .../configuration/GeyserConfiguration.java | 4 +- .../connector/console/GeyserLogger.java | 7 +- .../org/geysermc/connector/entity/Entity.java | 33 +++-- .../connector/entity/PlayerEntity.java | 13 +- .../network/session/GeyserSession.java | 10 +- .../network/session/UpstreamSession.java | 58 ++++++++ .../network/session/cache/EntityCache.java | 19 +-- .../network/translators/Registry.java | 15 +- .../network/translators/TranslatorsInit.java | 5 +- .../bedrock/BedrockMovePlayerTranslator.java | 9 +- .../bedrock/BedrockPlayerInitialized.java | 14 ++ .../translators/block/BlockTranslator.java | 2 +- .../inventory/GenericInventoryTranslator.java | 14 +- .../entity/JavaEntityDestroyTranslator.java | 2 +- .../entity/JavaEntityHeadLookTranslator.java | 2 +- .../JavaEntityPositionRotationTranslator.java | 4 +- .../entity/JavaEntityPositionTranslator.java | 4 +- .../entity/JavaEntityRotationTranslator.java | 7 +- .../entity/JavaEntityTeleportTranslator.java | 4 +- .../JavaPlayerPositionRotationTranslator.java | 4 +- .../spawn/JavaPlayerListEntryTranslator.java | 44 +++--- .../spawn/JavaSpawnExpOrbTranslator.java | 10 +- .../JavaSpawnGlobalEntityTranslator.java | 9 +- .../entity/spawn/JavaSpawnMobTranslator.java | 8 +- .../spawn/JavaSpawnObjectTranslator.java | 11 +- .../spawn/JavaSpawnPlayerTranslator.java | 15 +- .../connector/utils/InventoryUtils.java | 9 -- .../connector/utils/LoginEncryptionUtils.java | 2 +- .../connector/utils/SkinProvider.java | 128 ++++++++++++++---- connector/src/main/resources/config.yml | 10 +- 31 files changed, 320 insertions(+), 158 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/network/session/UpstreamSession.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockPlayerInitialized.java diff --git a/connector/pom.xml b/connector/pom.xml index e4b0b358f..891551cec 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -74,7 +74,7 @@ com.nukkitx.protocol bedrock-v361 - 2.1.3 + 2.2.0 compile diff --git a/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java b/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java index d90f25768..213ca100c 100644 --- a/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java +++ b/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java @@ -32,7 +32,6 @@ import java.util.Map; @Getter public class GeyserConfiguration { - private BedrockConfiguration bedrock; private RemoteConfiguration remote; @@ -50,5 +49,8 @@ public class GeyserConfiguration { @JsonProperty("general-thread-pool") private int generalThreadPool; + @JsonProperty("allow-third-party-capes") + private boolean allowThirdPartyCapes; + private MetricInfo metrics; } \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/console/GeyserLogger.java b/connector/src/main/java/org/geysermc/connector/console/GeyserLogger.java index db31bfc18..0ff122ea2 100644 --- a/connector/src/main/java/org/geysermc/connector/console/GeyserLogger.java +++ b/connector/src/main/java/org/geysermc/connector/console/GeyserLogger.java @@ -25,10 +25,11 @@ package org.geysermc.connector.console; -import org.geysermc.api.ChatColor; import io.sentry.Sentry; +import org.geysermc.api.ChatColor; -import java.io.*; +import java.io.File; +import java.io.IOException; import java.util.Date; import java.util.logging.*; @@ -108,7 +109,7 @@ public class GeyserLogger implements org.geysermc.api.logger.Logger { @Override public void error(String message, Throwable error) { waitFor(); - System.out.println(printConsole(ChatColor.RED + message + "\n" + error.getMessage(), colored)); + System.out.println(printConsole(ChatColor.RED + message + "\n" + error, colored)); } @Override diff --git a/connector/src/main/java/org/geysermc/connector/entity/Entity.java b/connector/src/main/java/org/geysermc/connector/entity/Entity.java index 735d5ac65..fb3c3d5c8 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/Entity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/Entity.java @@ -57,7 +57,9 @@ public class Entity { protected Vector3f position; protected Vector3f motion; - // 1 - pitch, 2 - yaw, 3 - roll (head yaw) + /** + * x = Yaw, y = Pitch, z = HeadYaw + */ protected Vector3f rotation; protected int scale = 1; @@ -89,7 +91,7 @@ public class Entity { addEntityPacket.setUniqueEntityId(geyserId); addEntityPacket.setPosition(position); addEntityPacket.setMotion(motion); - addEntityPacket.setRotation(rotation); + addEntityPacket.setRotation(getBedrockRotation()); addEntityPacket.setEntityType(entityType.getType()); addEntityPacket.getMetadata().putAll(getMetadata()); @@ -99,33 +101,37 @@ public class Entity { GeyserLogger.DEFAULT.debug("Spawned entity " + entityType + " at location " + position + " with id " + geyserId + " (java id " + entityId + ")"); } - public void despawnEntity(GeyserSession session) { - if (!valid) return; + /** + * @return can be deleted + */ + public boolean despawnEntity(GeyserSession session) { + if (!valid) return true; RemoveEntityPacket removeEntityPacket = new RemoveEntityPacket(); removeEntityPacket.setUniqueEntityId(geyserId); session.getUpstream().sendPacket(removeEntityPacket); valid = false; + return true; } - public void moveRelative(double relX, double relY, double relZ, float pitch, float yaw) { - moveRelative(relX, relY, relZ, new Vector3f(pitch, yaw, yaw)); + public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch) { + moveRelative(relX, relY, relZ, new Vector3f(yaw, pitch, yaw)); } public void moveRelative(double relX, double relY, double relZ, Vector3f rotation) { - this.rotation = rotation; + setRotation(rotation); this.position = new Vector3f(position.getX() + relX, position.getY() + relY, position.getZ() + relZ); this.movePending = true; } - public void moveAbsolute(Vector3f position, float pitch, float yaw) { - moveAbsolute(position, new Vector3f(pitch, yaw, yaw)); + public void moveAbsolute(Vector3f position, float yaw, float pitch) { + moveAbsolute(position, new Vector3f(yaw, pitch, yaw)); } public void moveAbsolute(Vector3f position, Vector3f rotation) { setPosition(position); - this.rotation = rotation; + setRotation(rotation); this.movePending = true; } @@ -189,6 +195,13 @@ public class Entity { this.position = position; } + /** + * x = Pitch, y = HeadYaw, z = Yaw + */ + public Vector3f getBedrockRotation() { + return new Vector3f(rotation.getY(), rotation.getZ(), rotation.getX()); + } + @SuppressWarnings("unchecked") public I as(Class entityClass) { return entityClass.isInstance(this) ? (I) this : null; diff --git a/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java index 4c65fdabe..a108d988f 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java @@ -43,13 +43,13 @@ public class PlayerEntity extends Entity { private UUID uuid; private String username; private long lastSkinUpdate = -1; - - private ItemData hand = ItemData.of(0, (short) 0, 0); + private boolean playerList = true; private ItemData helmet; private ItemData chestplate; private ItemData leggings; private ItemData boots; + private ItemData hand = ItemData.of(0, (short) 0, 0); public PlayerEntity(GameProfile gameProfile, long entityId, long geyserId, Vector3f position, Vector3f motion, Vector3f rotation) { super(entityId, geyserId, EntityType.PLAYER, position, motion, rotation); @@ -74,6 +74,12 @@ public class PlayerEntity extends Entity { session.getUpstream().sendPacket(armorEquipmentPacket); } + @Override + public boolean despawnEntity(GeyserSession session) { + super.despawnEntity(session); + return !playerList; // don't remove from cache when still on playerlist + } + @Override public void spawnEntity(GeyserSession session) { if (geyserId == 1) return; @@ -84,7 +90,7 @@ public class PlayerEntity extends Entity { addPlayerPacket.setRuntimeEntityId(geyserId); addPlayerPacket.setUniqueEntityId(geyserId); addPlayerPacket.setPosition(position); - addPlayerPacket.setRotation(rotation); + addPlayerPacket.setRotation(getBedrockRotation()); addPlayerPacket.setMotion(motion); addPlayerPacket.setHand(hand); addPlayerPacket.setPlayerFlags(0); @@ -98,6 +104,5 @@ public class PlayerEntity extends Entity { valid = true; session.getUpstream().sendPacket(addPlayerPacket); -// System.out.println("Spawned player "+uuid+" "+username+" "+geyserId); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index d84f61a71..146edefb5 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -53,7 +53,6 @@ import org.geysermc.api.session.AuthData; import org.geysermc.api.window.FormWindow; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.entity.PlayerEntity; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.inventory.PlayerInventory; import org.geysermc.connector.network.session.cache.*; import org.geysermc.connector.network.translators.Registry; @@ -65,7 +64,7 @@ import java.util.UUID; @Getter public class GeyserSession implements Player { private final GeyserConnector connector; - private final BedrockServerSession upstream; + private final UpstreamSession upstream; private RemoteServer remoteServer; private Client downstream; private AuthData authenticationData; @@ -95,7 +94,7 @@ public class GeyserSession implements Player { public GeyserSession(GeyserConnector connector, BedrockServerSession bedrockServerSession) { this.connector = connector; - this.upstream = bedrockServerSession; + this.upstream = new UpstreamSession(bedrockServerSession); this.chunkCache = new ChunkCache(this); this.entityCache = new EntityCache(this); @@ -169,8 +168,9 @@ public class GeyserSession implements Player { @Override public void packetReceived(PacketReceivedEvent event) { - if (!closed) + if (!closed) { Registry.JAVA.translate(event.getPacket().getClass(), event.getPacket(), GeyserSession.this); + } } }); @@ -297,5 +297,7 @@ public class GeyserSession implements Player { PlayStatusPacket playStatusPacket = new PlayStatusPacket(); playStatusPacket.setStatus(PlayStatusPacket.Status.PLAYER_SPAWN); upstream.sendPacket(playStatusPacket); + + upstream.setFrozen(true); // will freeze until the client decides it is ready } } diff --git a/connector/src/main/java/org/geysermc/connector/network/session/UpstreamSession.java b/connector/src/main/java/org/geysermc/connector/network/session/UpstreamSession.java new file mode 100644 index 000000000..ab3a0e0c5 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/session/UpstreamSession.java @@ -0,0 +1,58 @@ +package org.geysermc.connector.network.session; + +import com.nukkitx.protocol.bedrock.BedrockPacket; +import com.nukkitx.protocol.bedrock.BedrockServerSession; +import lombok.Getter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import org.geysermc.api.Geyser; + +import java.net.InetSocketAddress; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; + +@RequiredArgsConstructor +public class UpstreamSession { + @Getter private final BedrockServerSession session; + private Queue packets = new ConcurrentLinkedQueue<>(); + @Getter private boolean frozen = false; + + public void sendPacket(@NonNull BedrockPacket packet) { + if (frozen || !packets.isEmpty()) { + packets.add(packet); + } else { + session.sendPacket(packet); + } + } + + public void sendPacketImmediately(@NonNull BedrockPacket packet) { + session.sendPacketImmediately(packet); + } + + public void setFrozen(boolean frozen) { + if (this.frozen != frozen) { + this.frozen = frozen; + + if (!frozen) { + Geyser.getGeneralThreadPool().execute(() -> { + BedrockPacket packet; + while ((packet = packets.poll()) != null) { + session.sendPacket(packet); + } + }); + } + } + } + + public void disconnect(String reason) { + session.disconnect(reason); + } + + public boolean isClosed() { + return session.isClosed(); + } + + public InetSocketAddress getAddress() { + return session.getAddress(); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityCache.java index 1d8c15d48..41af7e579 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityCache.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityCache.java @@ -62,17 +62,18 @@ public class EntityCache { entity.spawnEntity(session); } - public void removeEntity(Entity entity) { - if (entity == null || !entity.isValid()) return; - - Long geyserId = entityIdTranslations.remove(entity.getEntityId()); - if (geyserId != null) { - entities.remove(geyserId); - if (entity.is(PlayerEntity.class)) { - playerEntities.remove(entity.as(PlayerEntity.class).getUuid()); + public boolean removeEntity(Entity entity, boolean force) { + if (entity != null && entity.isValid() && (force || entity.despawnEntity(session))) { + Long geyserId = entityIdTranslations.remove(entity.getEntityId()); + if (geyserId != null) { + entities.remove(geyserId); + if (entity.is(PlayerEntity.class)) { + playerEntities.remove(entity.as(PlayerEntity.class).getUuid()); + } } + return true; } - entity.despawnEntity(session); + return false; } public Entity getEntityByGeyserId(long geyserId) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/Registry.java b/connector/src/main/java/org/geysermc/connector/network/translators/Registry.java index e13ce9748..3867c8b52 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/Registry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/Registry.java @@ -48,13 +48,16 @@ public class Registry { } public

boolean translate(Class clazz, P packet, GeyserSession session) { - try { - if (MAP.containsKey(clazz)) { - ((PacketTranslator

) MAP.get(clazz)).translate(packet, session); - return true; + if (!session.getUpstream().isClosed() && !session.isClosed()) { + try { + if (MAP.containsKey(clazz)) { + ((PacketTranslator

) MAP.get(clazz)).translate(packet, session); + return true; + } + } catch (Throwable ex) { + GeyserLogger.DEFAULT.error("Could not translate packet " + packet.getClass().getSimpleName(), ex); + ex.printStackTrace(); } - } catch (NullPointerException ex) { - GeyserLogger.DEFAULT.error("Could not translate packet " + packet.getClass().getSimpleName(), ex); } return false; } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java b/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java index b48483a41..f9bb11cfc 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java @@ -146,10 +146,11 @@ public class TranslatorsInit { Registry.registerBedrock(AnimatePacket.class, new BedrockAnimateTranslator()); Registry.registerBedrock(CommandRequestPacket.class, new BedrockCommandRequestTranslator()); - Registry.registerBedrock(TextPacket.class, new BedrockTextTranslator()); Registry.registerBedrock(MobEquipmentPacket.class, new BedrockMobEquipmentTranslator()); - Registry.registerBedrock(PlayerActionPacket.class, new BedrockActionTranslator()); Registry.registerBedrock(MovePlayerPacket.class, new BedrockMovePlayerTranslator()); + Registry.registerBedrock(PlayerActionPacket.class, new BedrockActionTranslator()); + Registry.registerBedrock(SetLocalPlayerAsInitializedPacket.class, new BedrockPlayerInitialized()); + Registry.registerBedrock(TextPacket.class, new BedrockTextTranslator()); itemTranslator = new ItemTranslator(); blockTranslator = new BlockTranslator(); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockMovePlayerTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockMovePlayerTranslator.java index a09d0daa3..3891af6f8 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockMovePlayerTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockMovePlayerTranslator.java @@ -53,9 +53,10 @@ public class BedrockMovePlayerTranslator extends PacketTranslator 10 || yRange > 10 || zRange > 10) { + if ((xRange + yRange + zRange) > 100) { session.getConnector().getLogger().warning(session.getName() + " moved too quickly." + " current position: " + currentPosition + ", new position: " + newPosition); @@ -104,7 +105,7 @@ public class BedrockMovePlayerTranslator extends PacketTranslator { + @Override + public void translate(SetLocalPlayerAsInitializedPacket packet, GeyserSession session) { + if (session.getPlayerEntity().getGeyserId() == packet.getRuntimeEntityId()) { + session.getUpstream().setFrozen(false); + } + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java index 596bd3896..ef22b6305 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java @@ -12,7 +12,7 @@ public class BlockTranslator { public BedrockItem getBedrockBlock(BlockState state) { BedrockItem bedrockItem = Remapper.BLOCK_REMAPPER.convertToBedrockB(new ItemStack(state.getId())); if (bedrockItem == null) { - GeyserLogger.DEFAULT.debug("Missing mapping for java block " + state.getId() + "/nPlease report this to Geyser."); + GeyserLogger.DEFAULT.debug("Missing mapping for java block " + state.getId() + "\nPlease report this to Geyser."); return BedrockItem.DIRT; // so we can walk and not getting stuck x) } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/GenericInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/GenericInventoryTranslator.java index b6f36ac05..e2f4fa582 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/GenericInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/GenericInventoryTranslator.java @@ -26,7 +26,6 @@ package org.geysermc.connector.network.translators.inventory; import com.flowpowered.math.vector.Vector3i; -import com.nukkitx.protocol.bedrock.data.ContainerId; import com.nukkitx.protocol.bedrock.data.ItemData; import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket; import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket; @@ -34,7 +33,6 @@ import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket; import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.TranslatorsInit; -import org.geysermc.connector.utils.InventoryUtils; public class GenericInventoryTranslator extends InventoryTranslator { @@ -54,29 +52,21 @@ public class GenericInventoryTranslator extends InventoryTranslator { @Override public void updateInventory(GeyserSession session, Inventory inventory) { - ContainerId containerId = InventoryUtils.getContainerId(inventory.getId()); - if (containerId == null) - return; - ItemData[] bedrockItems = new ItemData[inventory.getItems().length]; for (int i = 0; i < bedrockItems.length; i++) { bedrockItems[i] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItems()[i]); } InventoryContentPacket contentPacket = new InventoryContentPacket(); - contentPacket.setContainerId(containerId); + contentPacket.setContainerId(inventory.getId()); contentPacket.setContents(bedrockItems); session.getUpstream().sendPacket(contentPacket); } @Override public void updateSlot(GeyserSession session, Inventory inventory, int slot) { - ContainerId containerId = InventoryUtils.getContainerId(inventory.getId()); - if (containerId == null) - return; - InventorySlotPacket slotPacket = new InventorySlotPacket(); - slotPacket.setContainerId(containerId); + slotPacket.setContainerId(inventory.getId()); slotPacket.setSlot(TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItems()[slot])); slotPacket.setInventorySlot(slot); session.getUpstream().sendPacket(slotPacket); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityDestroyTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityDestroyTranslator.java index 065463235..5bd481a94 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityDestroyTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityDestroyTranslator.java @@ -38,7 +38,7 @@ public class JavaEntityDestroyTranslator extends PacketTranslator { - private static byte[] providedSkin = new ProvidedSkin("bedrock/skin/skin_steve.png").getSkin(); - @Override public void translate(ServerPlayerListEntryPacket packet, GeyserSession session) { if (packet.getAction() != PlayerListEntryAction.ADD_PLAYER && packet.getAction() != PlayerListEntryAction.REMOVE_PLAYER) return; @@ -25,26 +22,27 @@ public class JavaPlayerListEntryTranslator extends PacketTranslator { @Override public void translate(ServerSpawnExpOrbPacket packet, GeyserSession session) { Vector3f position = new Vector3f(packet.getX(), packet.getY(), packet.getZ()); - Entity entity = new ExpOrbEntity(packet.getExp(), packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(), - EntityType.EXPERIENCE_ORB, position, new Vector3f(0, 0, 0), new Vector3f(0, 0, 0)); - if (entity == null) - return; + Entity entity = new ExpOrbEntity( + packet.getExp(), packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(), + EntityType.EXPERIENCE_ORB, position, Vector3f.ZERO, Vector3f.ZERO + ); session.getEntityCache().spawnEntity(entity); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnGlobalEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnGlobalEntityTranslator.java index cb6b9f126..7d8aa6a3a 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnGlobalEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnGlobalEntityTranslator.java @@ -39,11 +39,10 @@ public class JavaSpawnGlobalEntityTranslator extends PacketTranslator { GameProfile.Property skinProperty = entity.getProfile().getProperty("textures"); - JsonObject skinObject = SkinProvider.getGson().fromJson(new String(Base64.getDecoder().decode(skinProperty.getValue()), Charsets.UTF_8), JsonObject.class); + JsonObject skinObject = SkinProvider.GSON.fromJson(new String(Base64.getDecoder().decode(skinProperty.getValue()), Charsets.UTF_8), JsonObject.class); JsonObject textures = skinObject.getAsJsonObject("textures"); JsonObject skinTexture = textures.getAsJsonObject("SKIN"); @@ -82,8 +83,14 @@ public class JavaSpawnPlayerTranslator extends PacketTranslator cachedSkins = new ConcurrentHashMap<>(); - private static Map cachedCapes = new ConcurrentHashMap<>(); + public static final Gson GSON = new GsonBuilder().create(); + public static final boolean ALLOW_THIRD_PARTY_CAPES = ((GeyserConnector)Geyser.getConnector()).getConfig().isAllowThirdPartyCapes(); + private static final ExecutorService EXECUTOR_SERVICE = Executors.newFixedThreadPool(ALLOW_THIRD_PARTY_CAPES ? 21 : 14); public static final Skin EMPTY_SKIN = new Skin(-1, ""); - public static final Cape EMPTY_CAPE = new Cape("", new byte[0]); - private static final int CACHE_INTERVAL = 8 * 60 * 1000; // 8 minutes - + public static final byte[] STEVE_SKIN = new ProvidedSkin("bedrock/skin/skin_steve.png").getSkin(); + private static Map cachedSkins = new ConcurrentHashMap<>(); private static Map> requestedSkins = new ConcurrentHashMap<>(); + + public static final Cape EMPTY_CAPE = new Cape("", new byte[0], -1, true); + private static Map cachedCapes = new ConcurrentHashMap<>(); private static Map> requestedCapes = new ConcurrentHashMap<>(); + private static final int CACHE_INTERVAL = 8 * 60 * 1000; // 8 minutes + public static boolean hasSkinCached(UUID uuid) { return cachedSkins.containsKey(uuid); } @@ -54,9 +58,9 @@ public class SkinProvider { getOrDefault(requestAndHandleCape(capeUrl, false), EMPTY_CAPE, 5) ); - Geyser.getLogger().info("Took " + (System.currentTimeMillis() - time) + "ms for " + playerId); + Geyser.getLogger().debug("Took " + (System.currentTimeMillis() - time) + "ms for " + playerId); return skinAndCape; - }, executorService); + }, EXECUTOR_SERVICE); } public static CompletableFuture requestAndHandleSkin(UUID playerId, String textureUrl, boolean newThread) { @@ -70,7 +74,7 @@ public class SkinProvider { CompletableFuture future; if (newThread) { - future = CompletableFuture.supplyAsync(() -> supplySkin(playerId, textureUrl), executorService) + future = CompletableFuture.supplyAsync(() -> supplySkin(playerId, textureUrl), EXECUTOR_SERVICE) .whenCompleteAsync((skin, throwable) -> { if (!cachedSkins.getOrDefault(playerId, EMPTY_SKIN).getTextureUrl().equals(textureUrl)) { skin.updated = true; @@ -91,14 +95,17 @@ public class SkinProvider { if (capeUrl == null || capeUrl.isEmpty()) return CompletableFuture.completedFuture(EMPTY_CAPE); if (requestedCapes.containsKey(capeUrl)) return requestedCapes.get(capeUrl); // already requested - if (cachedCapes.containsKey(capeUrl)) { - // no need to update the cache, capes are static :D + boolean officialCape = capeUrl.startsWith("https://textures.minecraft.net"); + boolean validCache = (System.currentTimeMillis() - CACHE_INTERVAL) < cachedCapes.getOrDefault(capeUrl, EMPTY_CAPE).getRequestedOn(); + + if ((cachedCapes.containsKey(capeUrl) && officialCape) || validCache) { + // the cape is an official cape (static) or the cape doesn't need a update yet return CompletableFuture.completedFuture(cachedCapes.get(capeUrl)); } CompletableFuture future; if (newThread) { - future = CompletableFuture.supplyAsync(() -> supplyCape(capeUrl), executorService) + future = CompletableFuture.supplyAsync(() -> supplyCape(capeUrl), EXECUTOR_SERVICE) .whenCompleteAsync((cape, throwable) -> { cachedCapes.put(capeUrl, cape); requestedCapes.remove(capeUrl); @@ -112,26 +119,57 @@ public class SkinProvider { return future; } + public static CompletableFuture requestAndHandleUnofficialCape(Cape officialCape, UUID playerId, + String username, boolean newThread) { + if (officialCape.isFailed() && ALLOW_THIRD_PARTY_CAPES) { + for (UnofficalCape cape : UnofficalCape.VALUES) { + Cape cape1 = getOrDefault( + requestAndHandleCape(cape.getUrlFor(playerId, username), newThread), + EMPTY_CAPE, 4 + ); + if (!cape1.isFailed()) { + return CompletableFuture.completedFuture(cape1); + } + } + } + return CompletableFuture.completedFuture(officialCape); + } + private static Skin supplySkin(UUID uuid, String textureUrl) { byte[] skin = EMPTY_SKIN.getSkinData(); try { - skin = requestImage(textureUrl); + skin = requestImage(textureUrl, false); } catch (Exception ignored) {} // just ignore I guess return new Skin(uuid, textureUrl, skin, System.currentTimeMillis(), false); } private static Cape supplyCape(String capeUrl) { - byte[] cape = EMPTY_CAPE.getCapeData(); + byte[] cape = new byte[0]; try { - cape = requestImage(capeUrl); + cape = requestImage(capeUrl, true); } catch (Exception ignored) {} // just ignore I guess - return new Cape(capeUrl, cape); + + return new Cape( + capeUrl, + cape.length > 0 ? cape : EMPTY_CAPE.getCapeData(), + System.currentTimeMillis(), + cape.length == 0 + ); } - private static byte[] requestImage(String imageUrl) throws Exception { + private static byte[] requestImage(String imageUrl, boolean cape) throws Exception { BufferedImage image = ImageIO.read(new URL(imageUrl)); Geyser.getLogger().debug("Downloaded " + imageUrl); + if (cape) { + BufferedImage newImage = new BufferedImage(64, 32, BufferedImage.TYPE_INT_RGB); + + Graphics g = newImage.createGraphics(); + g.drawImage(image, 0, 0, 64, 32, null); + g.dispose(); + image = newImage; + } + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(image.getWidth() * 4 + image.getHeight() * 4); try { for (int y = 0; y < image.getHeight(); y++) { @@ -152,6 +190,13 @@ public class SkinProvider { } } + public static T getOrDefault(CompletableFuture future, T defaultValue, int timeoutInSeconds) { + try { + return future.get(timeoutInSeconds, TimeUnit.SECONDS); + } catch (Exception ignored) {} + return defaultValue; + } + @AllArgsConstructor @Getter public static class SkinAndCape { @@ -164,7 +209,7 @@ public class SkinProvider { public static class Skin { private UUID skinOwner; private String textureUrl; - private byte[] skinData = new byte[0]; + private byte[] skinData = STEVE_SKIN; private long requestedOn; private boolean updated; @@ -179,12 +224,45 @@ public class SkinProvider { public static class Cape { private String textureUrl; private byte[] capeData; + private long requestedOn; + private boolean failed; } - private static T getOrDefault(CompletableFuture future, T defaultValue, int timeoutInSeconds) { - try { - return future.get(timeoutInSeconds, TimeUnit.SECONDS); - } catch (Exception ignored) {} - return defaultValue; + /* + * Sorted by 'priority' + */ + @AllArgsConstructor + @Getter + public enum UnofficalCape { + OPTIFINE("http://s.optifine.net/capes/%s.png", CapeUrlType.USERNAME), + LABYMOD("http://capes.labymod.net/capes/%s.png", CapeUrlType.UUID_DASHED), + FIVEZIG("http://textures.5zig.net/2/%s", CapeUrlType.UUID), + MINECRAFTCAPES("https://www.minecraftcapes.co.uk/getCape/%s", CapeUrlType.UUID); + + public static final UnofficalCape[] VALUES = values(); + private String url; + private CapeUrlType type; + + public String getUrlFor(String type) { + return String.format(url, type); + } + + public String getUrlFor(UUID uuid, String username) { + return getUrlFor(toRequestedType(type, uuid, username)); + } + + public static String toRequestedType(CapeUrlType type, UUID uuid, String username) { + switch (type) { + case UUID: return uuid.toString().replace("-", ""); + case UUID_DASHED: return uuid.toString(); + default: return username; + } + } + } + + public enum CapeUrlType { + USERNAME, + UUID, + UUID_DASHED } } diff --git a/connector/src/main/resources/config.yml b/connector/src/main/resources/config.yml index d2dff626c..840614ea8 100644 --- a/connector/src/main/resources/config.yml +++ b/connector/src/main/resources/config.yml @@ -49,6 +49,10 @@ debug-mode: false # Thread pool size general-thread-pool: 32 +# Allow third party capes to be visible. Currently allowing: +# OptiFine capes, LabyMod capes, 5Zig capes and MinecraftCapes +allow-third-party-capes: true + # bStats is a stat tracker that is entirely anonymous and tracks only basic information # about Geyser, such as how many people are online, how many servers are using Geyser, # what OS is being used, etc. You can learn more about bStats here: https://bstats.org/. @@ -57,8 +61,4 @@ metrics: # If metrics should be enabled enabled: true # UUID of server, don't change! - uuid: generateduuid - - - - + uuid: generateduuid \ No newline at end of file From 89550a5ecb65efd20cee3f085115303400e0121d Mon Sep 17 00:00:00 2001 From: Tim203 Date: Fri, 4 Oct 2019 19:25:08 +0200 Subject: [PATCH 3/8] Bugfixes --- .../network/session/GeyserSession.java | 2 +- .../translators/java/JavaChatTranslator.java | 4 + .../spawn/JavaPlayerListEntryTranslator.java | 2 +- .../JavaScoreboardObjectiveTranslator.java | 46 +++++----- .../scoreboard/JavaUpdateScoreTranslator.java | 19 ++-- .../connector/scoreboard/Objective.java | 41 ++++++--- .../geysermc/connector/scoreboard/Score.java | 12 ++- .../connector/scoreboard/Scoreboard.java | 90 ++++++++++++++----- .../geysermc/connector/scoreboard/Team.java | 17 +--- 9 files changed, 154 insertions(+), 79 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index 22d20355b..1d7dfe882 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -297,7 +297,7 @@ public class GeyserSession implements Player { startGamePacket.setCurrentTick(0); startGamePacket.setEnchantmentSeed(0); startGamePacket.setMultiplayerCorrelationId(""); - startGamePacket.setCachedPalette(Toolbox.CACHED_PALLETE.copy()); + startGamePacket.setCachedPalette(Toolbox.CACHED_PALLETE.retainedDuplicate()); startGamePacket.setItemEntries(Toolbox.ITEMS); upstream.sendPacket(startGamePacket); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaChatTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaChatTranslator.java index cc5838c90..e604e20a3 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaChatTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaChatTranslator.java @@ -43,12 +43,16 @@ public class JavaChatTranslator extends PacketTranslator { switch (packet.getType()) { case CHAT: textPacket.setType(TextPacket.Type.CHAT); + break; case SYSTEM: textPacket.setType(TextPacket.Type.SYSTEM); + break; case NOTIFICATION: textPacket.setType(TextPacket.Type.TIP); + break; default: textPacket.setType(TextPacket.Type.RAW); + break; } if (packet.getMessage() instanceof TranslationMessage) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaPlayerListEntryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaPlayerListEntryTranslator.java index cddcc098e..d0653ee06 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaPlayerListEntryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaPlayerListEntryTranslator.java @@ -22,7 +22,7 @@ public class JavaPlayerListEntryTranslator extends PacketTranslator { @Override public void translate(ServerScoreboardObjectivePacket packet, GeyserSession session) { - try { - ScoreboardCache cache = session.getScoreboardCache(); - Scoreboard scoreboard = cache.getScoreboard(); + ScoreboardCache cache = session.getScoreboardCache(); + Scoreboard scoreboard = cache.getScoreboard(); - if (scoreboard.getObjective(packet.getName()) == null) { - // whoops sent objective to early, ignore it - return; - } + Objective objective = scoreboard.getObjective(packet.getName()); - switch (packet.getAction()) { - case ADD: - Objective objective = scoreboard.getObjective(packet.getName()); - objective.setDisplayName(MessageUtils.getBedrockMessage(packet.getDisplayName())); - objective.setType(packet.getType().ordinal()); - break; - case UPDATE: - Objective updateObj = scoreboard.getObjective(packet.getName()); - updateObj.setDisplayName(MessageUtils.getBedrockMessage(packet.getDisplayName())); - break; - case REMOVE: - scoreboard.unregisterObjective(packet.getName()); - break; - } - - scoreboard.onUpdate(); - } catch (Exception ex) { - ex.printStackTrace(); + if (objective == null && packet.getAction() != ObjectiveAction.REMOVE) { + objective = scoreboard.registerNewObjective(packet.getName(), true); } + + switch (packet.getAction()) { + case ADD: + case UPDATE: + objective.setDisplayName(MessageUtils.getBedrockMessage(packet.getDisplayName())); + objective.setType(packet.getType().ordinal()); + break; + case REMOVE: + scoreboard.unregisterObjective(packet.getName()); + break; + } + + if (objective != null && !objective.isTemp()) scoreboard.onUpdate(); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaUpdateScoreTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaUpdateScoreTranslator.java index dae080a3b..7d21deaf0 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaUpdateScoreTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaUpdateScoreTranslator.java @@ -25,12 +25,13 @@ package org.geysermc.connector.network.translators.java.scoreboard; +import com.github.steveice10.mc.protocol.data.game.scoreboard.ScoreboardAction; import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerUpdateScorePacket; import org.geysermc.api.Geyser; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; -import org.geysermc.connector.scoreboard.Scoreboard; import org.geysermc.connector.scoreboard.Objective; +import org.geysermc.connector.scoreboard.Scoreboard; public class JavaUpdateScoreTranslator extends PacketTranslator { @@ -40,18 +41,24 @@ public class JavaUpdateScoreTranslator extends PacketTranslator scores = new HashMap<>(); + private Objective(Scoreboard scoreboard) { + this.id = scoreboard.getNextId().getAndIncrement(); + this.scoreboard = scoreboard; + } + + /** + * /!\ This method is made for temporary objectives until the real objective is received + */ + public Objective(Scoreboard scoreboard, String objectiveName) { + this(scoreboard); + this.objectiveName = objectiveName; + this.temp = true; + } + public Objective(Scoreboard scoreboard, String objectiveName, ScoreboardPosition displaySlot, String displayName, int type) { this(scoreboard, objectiveName, displaySlot.name().toLowerCase(), displayName, type); } public Objective(Scoreboard scoreboard, String objectiveName, String displaySlot, String displayName, int type) { - this.scoreboard = scoreboard; - this.id = scoreboard.getNextId().getAndIncrement(); + this(scoreboard); this.objectiveName = objectiveName; this.displaySlot = displaySlot; this.displayName = displayName; @@ -61,9 +75,9 @@ public class Objective { public void registerScore(String id, int score) { if (!scores.containsKey(id)) { - Score score1 = new Score(this, id).setScore(score); - Team team = scoreboard.getTeamFor(id); - if (team != null) score1.setTeam(team); + Score score1 = new Score(this, id) + .setScore(score) + .setTeam(scoreboard.getTeamFor(id)); scores.put(id, score1); } } @@ -80,9 +94,9 @@ public class Objective { if (!scores.containsKey(oldText) || oldText.equals(newText)) return; Score oldScore = scores.get(oldText); - Score newScore = new Score(this, newText).setScore(oldScore.getScore()); - Team team = scoreboard.getTeamFor(newText); - if (team != null) newScore.setTeam(team); + Score newScore = new Score(this, newText) + .setScore(oldScore.getScore()) + .setTeam(scoreboard.getTeamFor(newText)); scores.put(newText, newScore); oldScore.setUpdateType(UpdateType.REMOVE); @@ -123,4 +137,11 @@ public class Objective { if (updateType == UpdateType.NOTHING) updateType = UpdateType.UPDATE; return this; } + + public void removeTemp(ScoreboardPosition displaySlot) { + if (temp) { + temp = false; + this.displaySlot = displaySlot.name().toLowerCase(); + } + } } diff --git a/connector/src/main/java/org/geysermc/connector/scoreboard/Score.java b/connector/src/main/java/org/geysermc/connector/scoreboard/Score.java index 9e4966582..f4778d953 100644 --- a/connector/src/main/java/org/geysermc/connector/scoreboard/Score.java +++ b/connector/src/main/java/org/geysermc/connector/scoreboard/Score.java @@ -39,7 +39,7 @@ public class Score { private String name; private Team team; private int score; - + private int oldScore = Integer.MIN_VALUE; public Score(Objective objective, String name) { this.id = objective.getScoreboard().getNextId().getAndIncrement(); @@ -48,9 +48,17 @@ public class Score { } public String getDisplayName() { - if (team != null) { + if (team != null && team.getUpdateType() != UpdateType.REMOVE) { return team.getPrefix() + name + team.getSuffix(); } return name; } + + public Score setScore(int score) { + if (oldScore == Integer.MIN_VALUE) { + this.oldScore = score; + } + this.score = score; + return this; + } } diff --git a/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java b/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java index 22b080cb6..948da13ae 100644 --- a/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java +++ b/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java @@ -32,7 +32,6 @@ import com.nukkitx.protocol.bedrock.packet.SetDisplayObjectivePacket; import com.nukkitx.protocol.bedrock.packet.SetScorePacket; import lombok.Getter; import org.geysermc.api.Geyser; -import org.geysermc.connector.console.GeyserLogger; import org.geysermc.connector.network.session.GeyserSession; import java.util.*; @@ -52,13 +51,30 @@ public class Scoreboard { this.session = session; } - public Objective registerNewObjective(String objectiveId, ScoreboardPosition displaySlot) { - Objective objective = new Objective(this, objectiveId, displaySlot, "unknown", 0); - if (objectives.containsKey(objectiveId)) despawnObjective(objectives.get(objectiveId)); + public Objective registerNewObjective(String objectiveId, boolean temp) { + if (!temp || objectives.containsKey(objectiveId)) return objectives.get(objectiveId); + Objective objective = new Objective(this, objectiveId); objectives.put(objectiveId, objective); return objective; } + public Objective registerNewObjective(String objectiveId, ScoreboardPosition displaySlot) { + Objective objective = null; + if (objectives.containsKey(objectiveId)) { + objective = objectives.get(objectiveId); + if (objective.isTemp()) objective.removeTemp(displaySlot); + else { + despawnObjective(objective); + objective = null; + } + } + if (objective == null) { + objective = new Objective(this, objectiveId, displaySlot, "unknown", 0); + objectives.put(objectiveId, objective); + } + return objective; + } + public Team registerNewTeam(String teamName, Set players) { if (teams.containsKey(teamName)) { Geyser.getLogger().info("Ignoring team " + teamName + ". It overrides without removing old team."); @@ -71,7 +87,7 @@ public class Scoreboard { for (Objective objective : objectives.values()) { for (Score score : objective.getScores().values()) { if (players.contains(score.getName())) { - score.setTeam(team).setUpdateType(ADD); + score.setTeam(team); } } } @@ -92,15 +108,8 @@ public class Scoreboard { } public void removeTeam(String teamName) { - if (teams.remove(teamName) != null) { - for (Objective objective : objectives.values()) { - for (Score score : objective.getScores().values()) { - if (score.getName().equals(teamName)) { - score.setTeam(null).setUpdateType(ADD); - } - } - } - } + Team remove = teams.remove(teamName); + if (remove != null) remove.setUpdateType(REMOVE); } public void onUpdate() { @@ -110,23 +119,64 @@ public class Scoreboard { for (String objectiveId : new ArrayList<>(objectives.keySet())) { Objective objective = objectives.get(objectiveId); + if (objective.isTemp()) { + Geyser.getLogger().debug("Ignoring temp Scoreboard Objective '"+ objectiveId +'\''); + continue; + } + if (objective.getUpdateType() != NOTHING) changedObjectives.add(objective); + boolean globalUpdate = objective.getUpdateType() == UPDATE; + boolean globalAdd = objective.getUpdateType() == ADD || globalUpdate; + boolean globalRemove = objective.getUpdateType() == REMOVE || globalUpdate; + + boolean hasUpdate = globalUpdate; + + List handledScores = new ArrayList<>(); for (String identifier : new HashSet<>(objective.getScores().keySet())) { Score score = objective.getScores().get(identifier); + Team team = score.getTeam(); - boolean add = (objective.getUpdateType() != NOTHING && objective.getUpdateType() != REMOVE) && score.getUpdateType() != REMOVE || score.getUpdateType() == ADD; - boolean remove = (add && score.getUpdateType() != ADD && objective.getUpdateType() != ADD) || objective.getUpdateType() == REMOVE || score.getUpdateType() == REMOVE; + boolean inTeam = team != null && team.getEntities().contains(score.getName()); - ScoreInfo info = new ScoreInfo(score.getId(), score.getObjective().getObjectiveName(), score.getScore(), score.getDisplayName()); - if (add || (score.getTeam() != null && (score.getTeam().getUpdateType() == ADD || score.getTeam().getUpdateType() == UPDATE))) addScores.add(info); - if (remove || (score.getTeam() != null && score.getTeam().getUpdateType() != NOTHING)) removeScores.add(info); + boolean teamAdd = team != null && (team.getUpdateType() == ADD || team.getUpdateType() == UPDATE); + boolean teamRemove = team != null && (team.getUpdateType() == REMOVE || team.getUpdateType() == UPDATE); + + if (team != null && (team.getUpdateType() == REMOVE || inTeam)) score.setTeam(null); + + boolean add = (hasUpdate || globalAdd || teamAdd || teamRemove || score.getUpdateType() == ADD || score.getUpdateType() == UPDATE) && (score.getUpdateType() != REMOVE); + boolean remove = hasUpdate || globalRemove || teamAdd || teamRemove || score.getUpdateType() == REMOVE || score.getUpdateType() == UPDATE; + + boolean updated = false; + if (!hasUpdate) { + updated = hasUpdate = add; + } + + if (updated) { + for (Score score1 : handledScores) { + ScoreInfo scoreInfo = new ScoreInfo(score1.getId(), score1.getObjective().getObjectiveName(), score1.getScore(), score1.getDisplayName()); + addScores.add(scoreInfo); + removeScores.add(scoreInfo); + } + } + + if (add) { + addScores.add(new ScoreInfo(score.getId(), score.getObjective().getObjectiveName(), score.getScore(), score.getDisplayName())); + } + if (remove) { + removeScores.add(new ScoreInfo(score.getId(), score.getObjective().getObjectiveName(), score.getOldScore(), score.getDisplayName())); + } + score.setOldScore(score.getScore()); if (score.getUpdateType() == REMOVE) { objective.removeScore(score.getName()); } - if (addScores.contains(info) || removeScores.contains(info)) changedObjectives.add(objective); + if (add || remove) { + changedObjectives.add(objective); + } else { // stays the same like before + handledScores.add(score); + } score.setUpdateType(NOTHING); } } diff --git a/connector/src/main/java/org/geysermc/connector/scoreboard/Team.java b/connector/src/main/java/org/geysermc/connector/scoreboard/Team.java index 2106787ea..7dcb5f3f5 100644 --- a/connector/src/main/java/org/geysermc/connector/scoreboard/Team.java +++ b/connector/src/main/java/org/geysermc/connector/scoreboard/Team.java @@ -36,29 +36,20 @@ public class Team { added.add(name); } } + setUpdateType(UpdateType.UPDATE); for (Objective objective : scoreboard.getObjectives().values()) { for (Score score : objective.getScores().values()) { if (added.contains(score.getName())) { - score.setTeam(this).setUpdateType(UpdateType.ADD); + score.setTeam(this); } } } } public void removeEntities(String... names) { - List removed = new ArrayList<>(); for (String name : names) { - if (entities.contains(name)) { - entities.remove(name); - removed.add(name); - } - } - for (Objective objective : scoreboard.getObjectives().values()) { - for (Score score : objective.getScores().values()) { - if (removed.contains(score.getName())) { - score.setTeam(null).setUpdateType(UpdateType.ADD); - } - } + entities.remove(name); } + setUpdateType(UpdateType.UPDATE); } } From a7bd9da6448a532f20d8cdcea0fa20410279386f Mon Sep 17 00:00:00 2001 From: Tim203 Date: Sat, 5 Oct 2019 00:29:33 +0200 Subject: [PATCH 4/8] Bugfixes --- .../org/geysermc/connector/entity/Entity.java | 2 +- .../geysermc/connector/entity/PlayerEntity.java | 13 +++++++++++++ .../network/session/UpstreamSession.java | 17 +++++++++++++++++ .../network/translators/TranslatorsInit.java | 2 +- .../bedrock/BedrockMovePlayerTranslator.java | 10 ++++++++-- ... => BedrockPlayerInitializedTranslator.java} | 2 +- .../translators/java/JavaBossBarTranslator.java | 2 +- .../connector/scoreboard/Scoreboard.java | 2 +- 8 files changed, 43 insertions(+), 7 deletions(-) rename connector/src/main/java/org/geysermc/connector/network/translators/bedrock/{BedrockPlayerInitialized.java => BedrockPlayerInitializedTranslator.java} (82%) diff --git a/connector/src/main/java/org/geysermc/connector/entity/Entity.java b/connector/src/main/java/org/geysermc/connector/entity/Entity.java index fb3c3d5c8..d70478a04 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/Entity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/Entity.java @@ -137,7 +137,7 @@ public class Entity { public EntityDataDictionary getMetadata() { EntityFlags flags = new EntityFlags(); - flags.setFlag(EntityFlag.HAS_GRAVITY, true); + flags.setFlag(EntityFlag.HAS_GRAVITY, !is(PlayerEntity.class) || as(PlayerEntity.class).isGravity()); flags.setFlag(EntityFlag.HAS_COLLISION, true); flags.setFlag(EntityFlag.CAN_SHOW_NAME, true); flags.setFlag(EntityFlag.CAN_CLIMB, true); diff --git a/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java index a108d988f..0b9f41801 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java @@ -30,6 +30,7 @@ import com.github.steveice10.mc.auth.data.GameProfile; import com.nukkitx.protocol.bedrock.data.ItemData; import com.nukkitx.protocol.bedrock.packet.AddPlayerPacket; import com.nukkitx.protocol.bedrock.packet.MobArmorEquipmentPacket; +import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket; import lombok.Getter; import lombok.Setter; import org.geysermc.connector.entity.type.EntityType; @@ -44,6 +45,7 @@ public class PlayerEntity extends Entity { private String username; private long lastSkinUpdate = -1; private boolean playerList = true; + private boolean gravity = false; private ItemData helmet; private ItemData chestplate; @@ -74,6 +76,17 @@ public class PlayerEntity extends Entity { session.getUpstream().sendPacket(armorEquipmentPacket); } + public void enableGravity(GeyserSession session) { + if (!gravity && session.getPlayerEntity().getGeyserId() == getGeyserId()) { + gravity = true; + + SetEntityDataPacket entityDataPacket = new SetEntityDataPacket(); + entityDataPacket.setRuntimeEntityId(geyserId); + entityDataPacket.getMetadata().putAll(getMetadata()); + session.getUpstream().sendPacket(entityDataPacket); + } + } + @Override public boolean despawnEntity(GeyserSession session) { super.despawnEntity(session); diff --git a/connector/src/main/java/org/geysermc/connector/network/session/UpstreamSession.java b/connector/src/main/java/org/geysermc/connector/network/session/UpstreamSession.java index ab3a0e0c5..af475e5c8 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/UpstreamSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/UpstreamSession.java @@ -16,6 +16,7 @@ public class UpstreamSession { @Getter private final BedrockServerSession session; private Queue packets = new ConcurrentLinkedQueue<>(); @Getter private boolean frozen = false; + private boolean queueCleared = true; public void sendPacket(@NonNull BedrockPacket packet) { if (frozen || !packets.isEmpty()) { @@ -55,4 +56,20 @@ public class UpstreamSession { public InetSocketAddress getAddress() { return session.getAddress(); } + + public boolean hasQueue() { + return !packets.isEmpty(); + } + + /** + * @return true the first time this method is called after the queue is empty.
+ * This will enable gravity because now the client is ready to explore + */ + public boolean isQueueCleared() { + if (!hasQueue() && queueCleared) { + queueCleared = false; + return true; + } + return false; + } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java b/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java index 8c80ac3b4..5f3569dcf 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java @@ -153,7 +153,7 @@ public class TranslatorsInit { Registry.registerBedrock(MobEquipmentPacket.class, new BedrockMobEquipmentTranslator()); Registry.registerBedrock(MovePlayerPacket.class, new BedrockMovePlayerTranslator()); Registry.registerBedrock(PlayerActionPacket.class, new BedrockActionTranslator()); - Registry.registerBedrock(SetLocalPlayerAsInitializedPacket.class, new BedrockPlayerInitialized()); + Registry.registerBedrock(SetLocalPlayerAsInitializedPacket.class, new BedrockPlayerInitializedTranslator()); Registry.registerBedrock(TextPacket.class, new BedrockTextTranslator()); itemTranslator = new ItemTranslator(); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockMovePlayerTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockMovePlayerTranslator.java index 3891af6f8..ad7645a4a 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockMovePlayerTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockMovePlayerTranslator.java @@ -31,18 +31,24 @@ import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlaye import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket; import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket; import org.geysermc.connector.entity.Entity; +import org.geysermc.connector.entity.PlayerEntity; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.item.BedrockItem; public class BedrockMovePlayerTranslator extends PacketTranslator { - @Override public void translate(MovePlayerPacket packet, GeyserSession session) { - Entity entity = session.getPlayerEntity(); + PlayerEntity entity = session.getPlayerEntity(); if (entity == null || !session.isSpawned()) return; + // can cause invalid moves when packet queue is not empty + if (session.getUpstream().hasQueue()) return; + if (session.getUpstream().isQueueCleared()) { + entity.enableGravity(session); + } + if (!isValidMove(session, packet.getMode(), entity.getPosition(), packet.getPosition())) { session.getConnector().getLogger().info("Recalculating position..."); recalculatePosition(session, entity, entity.getPosition()); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockPlayerInitialized.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockPlayerInitializedTranslator.java similarity index 82% rename from connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockPlayerInitialized.java rename to connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockPlayerInitializedTranslator.java index 63f4d94bf..c4f5a0793 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockPlayerInitialized.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockPlayerInitializedTranslator.java @@ -4,7 +4,7 @@ import com.nukkitx.protocol.bedrock.packet.SetLocalPlayerAsInitializedPacket; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; -public class BedrockPlayerInitialized extends PacketTranslator { +public class BedrockPlayerInitializedTranslator extends PacketTranslator { @Override public void translate(SetLocalPlayerAsInitializedPacket packet, GeyserSession session) { if (session.getPlayerEntity().getGeyserId() == packet.getRuntimeEntityId()) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaBossBarTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaBossBarTranslator.java index be8cdd728..8b89e3be3 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaBossBarTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaBossBarTranslator.java @@ -34,7 +34,7 @@ public class JavaBossBarTranslator extends PacketTranslator bossEventPacket.setTitle(MessageUtils.getBedrockMessage(packet.getTitle())); break; case UPDATE_HEALTH: - bossEventPacket.setType(BossEventPacket.Type.HEALTH_PERCENTAGE); + bossEventPacket.setType(BossEventPacket.Type.HEALTH_PERCENTAGE); bossEventPacket.setHealthPercentage(packet.getHealth()); break; case REMOVE: diff --git a/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java b/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java index 948da13ae..bb014ad4f 100644 --- a/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java +++ b/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java @@ -142,7 +142,7 @@ public class Scoreboard { boolean teamAdd = team != null && (team.getUpdateType() == ADD || team.getUpdateType() == UPDATE); boolean teamRemove = team != null && (team.getUpdateType() == REMOVE || team.getUpdateType() == UPDATE); - if (team != null && (team.getUpdateType() == REMOVE || inTeam)) score.setTeam(null); + if (team != null && (team.getUpdateType() == REMOVE || !inTeam)) score.setTeam(null); boolean add = (hasUpdate || globalAdd || teamAdd || teamRemove || score.getUpdateType() == ADD || score.getUpdateType() == UPDATE) && (score.getUpdateType() != REMOVE); boolean remove = hasUpdate || globalRemove || teamAdd || teamRemove || score.getUpdateType() == REMOVE || score.getUpdateType() == UPDATE; From 15506cf5b495b272f52a2126b2c31ea41dadbe19 Mon Sep 17 00:00:00 2001 From: Tim203 Date: Wed, 9 Oct 2019 20:39:38 +0200 Subject: [PATCH 5/8] Edited PlayerInit behavior and updated protocol lib to 2.3.0 --- connector/pom.xml | 2 +- .../org/geysermc/connector/entity/Entity.java | 12 +- .../connector/entity/ExpOrbEntity.java | 2 +- .../connector/entity/PaintingEntity.java | 2 +- .../connector/entity/PlayerEntity.java | 45 ++- .../network/session/GeyserSession.java | 16 +- .../network/session/UpstreamSession.java | 46 +-- .../network/session/cache/EntityCache.java | 14 +- .../network/translators/TranslatorsInit.java | 5 +- .../bedrock/BedrockActionTranslator.java | 2 +- .../bedrock/BedrockInteractTranslator.java | 12 +- .../bedrock/BedrockMovePlayerTranslator.java | 20 +- .../BedrockPlayerInitializedTranslator.java | 14 +- .../inventory/GenericInventoryTranslator.java | 4 +- .../java/JavaBossBarTranslator.java | 2 +- .../entity/JavaEntityHeadLookTranslator.java | 4 +- .../entity/JavaEntityTeleportTranslator.java | 4 +- .../entity/JavaEntityVelocityTranslator.java | 4 +- .../player/JavaPlayerActionAckTranslator.java | 2 +- .../player/JavaPlayerHealthTranslator.java | 4 +- .../JavaPlayerListEntryTranslator.java | 29 +- .../JavaPlayerPositionRotationTranslator.java | 14 +- .../spawn/JavaSpawnExpOrbTranslator.java | 4 +- .../JavaSpawnGlobalEntityTranslator.java | 4 +- .../entity/spawn/JavaSpawnMobTranslator.java | 8 +- .../spawn/JavaSpawnObjectTranslator.java | 8 +- .../spawn/JavaSpawnPaintingTranslator.java | 4 +- .../spawn/JavaSpawnPlayerTranslator.java | 76 +---- .../java/world/JavaBlockChangeTranslator.java | 7 +- .../java/world/JavaChunkDataTranslator.java | 4 +- .../world/JavaMultiBlockChangeTranslator.java | 7 +- .../world/JavaNotifyClientTranslator.java | 6 +- .../world/JavaSpawnPositionTranslator.java | 4 +- .../geysermc/connector/utils/GeyserUtils.java | 285 +----------------- .../connector/utils/SkinProvider.java | 16 +- .../geysermc/connector/utils/SkinUtils.java | 142 +++++++++ 36 files changed, 320 insertions(+), 514 deletions(-) rename connector/src/main/java/org/geysermc/connector/network/translators/java/entity/{spawn => player}/JavaPlayerListEntryTranslator.java (68%) create mode 100644 connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java diff --git a/connector/pom.xml b/connector/pom.xml index c135c2456..25d52a007 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -75,7 +75,7 @@ com.nukkitx.protocol bedrock-v361 - 2.2.0 + 2.3.0 compile diff --git a/connector/src/main/java/org/geysermc/connector/entity/Entity.java b/connector/src/main/java/org/geysermc/connector/entity/Entity.java index d70478a04..af8e4e31a 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/Entity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/Entity.java @@ -25,8 +25,8 @@ package org.geysermc.connector.entity; -import com.flowpowered.math.vector.Vector3f; import com.github.steveice10.mc.protocol.packet.ingame.server.entity.ServerEntityPropertiesPacket; +import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.EntityData; import com.nukkitx.protocol.bedrock.data.EntityDataDictionary; import com.nukkitx.protocol.bedrock.data.EntityFlag; @@ -116,17 +116,17 @@ public class Entity { } public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch) { - moveRelative(relX, relY, relZ, new Vector3f(yaw, pitch, yaw)); + moveRelative(relX, relY, relZ, Vector3f.from(yaw, pitch, yaw)); } public void moveRelative(double relX, double relY, double relZ, Vector3f rotation) { setRotation(rotation); - this.position = new Vector3f(position.getX() + relX, position.getY() + relY, position.getZ() + relZ); + this.position = Vector3f.from(position.getX() + relX, position.getY() + relY, position.getZ() + relZ); this.movePending = true; } public void moveAbsolute(Vector3f position, float yaw, float pitch) { - moveAbsolute(position, new Vector3f(yaw, pitch, yaw)); + moveAbsolute(position, Vector3f.from(yaw, pitch, yaw)); } public void moveAbsolute(Vector3f position, Vector3f rotation) { @@ -137,7 +137,7 @@ public class Entity { public EntityDataDictionary getMetadata() { EntityFlags flags = new EntityFlags(); - flags.setFlag(EntityFlag.HAS_GRAVITY, !is(PlayerEntity.class) || as(PlayerEntity.class).isGravity()); + flags.setFlag(EntityFlag.HAS_GRAVITY, true); flags.setFlag(EntityFlag.HAS_COLLISION, true); flags.setFlag(EntityFlag.CAN_SHOW_NAME, true); flags.setFlag(EntityFlag.CAN_CLIMB, true); @@ -199,7 +199,7 @@ public class Entity { * x = Pitch, y = HeadYaw, z = Yaw */ public Vector3f getBedrockRotation() { - return new Vector3f(rotation.getY(), rotation.getZ(), rotation.getX()); + return Vector3f.from(rotation.getY(), rotation.getZ(), rotation.getX()); } @SuppressWarnings("unchecked") diff --git a/connector/src/main/java/org/geysermc/connector/entity/ExpOrbEntity.java b/connector/src/main/java/org/geysermc/connector/entity/ExpOrbEntity.java index 036829e8a..89c1532f2 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/ExpOrbEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/ExpOrbEntity.java @@ -25,7 +25,7 @@ package org.geysermc.connector.entity; -import com.flowpowered.math.vector.Vector3f; +import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.packet.SpawnExperienceOrbPacket; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; diff --git a/connector/src/main/java/org/geysermc/connector/entity/PaintingEntity.java b/connector/src/main/java/org/geysermc/connector/entity/PaintingEntity.java index 6b1b1c117..f9f6e37fd 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/PaintingEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/PaintingEntity.java @@ -1,6 +1,6 @@ package org.geysermc.connector.entity; -import com.flowpowered.math.vector.Vector3f; +import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.packet.AddPaintingPacket; import lombok.Getter; import lombok.Setter; diff --git a/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java index 0b9f41801..67393114c 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java @@ -25,16 +25,18 @@ package org.geysermc.connector.entity; -import com.flowpowered.math.vector.Vector3f; import com.github.steveice10.mc.auth.data.GameProfile; +import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.ItemData; import com.nukkitx.protocol.bedrock.packet.AddPlayerPacket; import com.nukkitx.protocol.bedrock.packet.MobArmorEquipmentPacket; -import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket; +import com.nukkitx.protocol.bedrock.packet.PlayerListPacket; import lombok.Getter; import lombok.Setter; +import org.geysermc.api.Geyser; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.utils.SkinUtils; import java.util.UUID; @@ -45,7 +47,6 @@ public class PlayerEntity extends Entity { private String username; private long lastSkinUpdate = -1; private boolean playerList = true; - private boolean gravity = false; private ItemData helmet; private ItemData chestplate; @@ -76,17 +77,6 @@ public class PlayerEntity extends Entity { session.getUpstream().sendPacket(armorEquipmentPacket); } - public void enableGravity(GeyserSession session) { - if (!gravity && session.getPlayerEntity().getGeyserId() == getGeyserId()) { - gravity = true; - - SetEntityDataPacket entityDataPacket = new SetEntityDataPacket(); - entityDataPacket.setRuntimeEntityId(geyserId); - entityDataPacket.getMetadata().putAll(getMetadata()); - session.getUpstream().sendPacket(entityDataPacket); - } - } - @Override public boolean despawnEntity(GeyserSession session) { super.despawnEntity(session); @@ -118,4 +108,31 @@ public class PlayerEntity extends Entity { valid = true; session.getUpstream().sendPacket(addPlayerPacket); } + + public void sendPlayer(GeyserSession session) { + if (getLastSkinUpdate() == -1) { + if (playerList) { + PlayerListPacket playerList = new PlayerListPacket(); + playerList.setType(PlayerListPacket.Type.ADD); + playerList.getEntries().add(SkinUtils.buildDefaultEntry(profile, geyserId)); + session.getUpstream().sendPacket(playerList); + } + } + + if (session.getUpstream().isInitialized() && session.getEntityCache().getEntityByGeyserId(geyserId) == null) { + session.getEntityCache().spawnEntity(this); + } else { + spawnEntity(session); + } + + if (!playerList) { + // remove from playerlist if player isn't on playerlist + Geyser.getGeneralThreadPool().execute(() -> { + PlayerListPacket playerList = new PlayerListPacket(); + playerList.setType(PlayerListPacket.Type.REMOVE); + playerList.getEntries().add(new PlayerListPacket.Entry(uuid)); + session.getUpstream().sendPacket(playerList); + }); + } + } } diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index 1d7dfe882..b907187d9 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -25,10 +25,6 @@ package org.geysermc.connector.network.session; -import com.flowpowered.math.vector.Vector2f; -import com.flowpowered.math.vector.Vector2i; -import com.flowpowered.math.vector.Vector3f; -import com.flowpowered.math.vector.Vector3i; import com.github.steveice10.mc.auth.data.GameProfile; import com.github.steveice10.mc.auth.exception.request.RequestException; import com.github.steveice10.mc.protocol.MinecraftProtocol; @@ -40,6 +36,10 @@ import com.github.steveice10.packetlib.event.session.PacketReceivedEvent; import com.github.steveice10.packetlib.event.session.SessionAdapter; import com.github.steveice10.packetlib.packet.Packet; import com.github.steveice10.packetlib.tcp.TcpSessionFactory; +import com.nukkitx.math.vector.Vector2f; +import com.nukkitx.math.vector.Vector2i; +import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.math.vector.Vector3i; import com.nukkitx.protocol.bedrock.BedrockServerSession; import com.nukkitx.protocol.bedrock.data.GamePublishSetting; import com.nukkitx.protocol.bedrock.data.GameRule; @@ -257,15 +257,15 @@ public class GeyserSession implements Player { startGamePacket.setUniqueEntityId(playerEntity.getGeyserId()); startGamePacket.setRuntimeEntityId(playerEntity.getGeyserId()); startGamePacket.setPlayerGamemode(0); - startGamePacket.setPlayerPosition(new Vector3f(0, 69, 0)); - startGamePacket.setRotation(new Vector2f(1, 1)); + startGamePacket.setPlayerPosition(Vector3f.from(0, 69, 0)); + startGamePacket.setRotation(Vector2f.from(1, 1)); startGamePacket.setSeed(0); startGamePacket.setDimensionId(playerEntity.getDimension()); startGamePacket.setGeneratorId(1); startGamePacket.setLevelGamemode(0); startGamePacket.setDifficulty(1); - startGamePacket.setDefaultSpawn(new Vector3i(0, 0, 0)); + startGamePacket.setDefaultSpawn(Vector3i.ZERO); startGamePacket.setAcheivementsDisabled(true); startGamePacket.setTime(0); startGamePacket.setEduLevel(false); @@ -304,7 +304,5 @@ public class GeyserSession implements Player { PlayStatusPacket playStatusPacket = new PlayStatusPacket(); playStatusPacket.setStatus(PlayStatusPacket.Status.PLAYER_SPAWN); upstream.sendPacket(playStatusPacket); - - upstream.setFrozen(true); // will freeze until the client decides it is ready } } diff --git a/connector/src/main/java/org/geysermc/connector/network/session/UpstreamSession.java b/connector/src/main/java/org/geysermc/connector/network/session/UpstreamSession.java index af475e5c8..f8691bea6 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/UpstreamSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/UpstreamSession.java @@ -5,46 +5,24 @@ import com.nukkitx.protocol.bedrock.BedrockServerSession; import lombok.Getter; import lombok.NonNull; import lombok.RequiredArgsConstructor; -import org.geysermc.api.Geyser; +import lombok.Setter; import java.net.InetSocketAddress; -import java.util.Queue; -import java.util.concurrent.ConcurrentLinkedQueue; @RequiredArgsConstructor public class UpstreamSession { @Getter private final BedrockServerSession session; - private Queue packets = new ConcurrentLinkedQueue<>(); - @Getter private boolean frozen = false; - private boolean queueCleared = true; + @Getter @Setter + private boolean initialized = false; public void sendPacket(@NonNull BedrockPacket packet) { - if (frozen || !packets.isEmpty()) { - packets.add(packet); - } else { - session.sendPacket(packet); - } + session.sendPacket(packet); } public void sendPacketImmediately(@NonNull BedrockPacket packet) { session.sendPacketImmediately(packet); } - public void setFrozen(boolean frozen) { - if (this.frozen != frozen) { - this.frozen = frozen; - - if (!frozen) { - Geyser.getGeneralThreadPool().execute(() -> { - BedrockPacket packet; - while ((packet = packets.poll()) != null) { - session.sendPacket(packet); - } - }); - } - } - } - public void disconnect(String reason) { session.disconnect(reason); } @@ -56,20 +34,4 @@ public class UpstreamSession { public InetSocketAddress getAddress() { return session.getAddress(); } - - public boolean hasQueue() { - return !packets.isEmpty(); - } - - /** - * @return true the first time this method is called after the queue is empty.
- * This will enable gravity because now the client is ready to explore - */ - public boolean isQueueCleared() { - if (!hasQueue() && queueCleared) { - queueCleared = false; - return true; - } - return false; - } } diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityCache.java index 41af7e579..3bb702db4 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityCache.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityCache.java @@ -30,9 +30,7 @@ import org.geysermc.connector.entity.Entity; import org.geysermc.connector.entity.PlayerEntity; import org.geysermc.connector.network.session.GeyserSession; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; +import java.util.*; import java.util.concurrent.atomic.AtomicLong; /** @@ -84,6 +82,16 @@ public class EntityCache { return entities.get(entityIdTranslations.get(javaId)); } + public Set getEntitiesByType(Class entityType) { + Set entitiesOfType = new HashSet<>(); + for (Entity entity : (entityType == PlayerEntity.class ? playerEntities : entities).values()) { + if (entity.is(entityType)) { + entitiesOfType.add(entity.as(entityType)); + } + } + return entitiesOfType; + } + public void addPlayerEntity(PlayerEntity entity) { playerEntities.put(entity.getUuid(), entity); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java b/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java index 5f3569dcf..534f2286e 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java @@ -54,10 +54,7 @@ import org.geysermc.connector.network.translators.inventory.InventoryTranslator; import org.geysermc.connector.network.translators.item.ItemTranslator; import org.geysermc.connector.network.translators.java.*; import org.geysermc.connector.network.translators.java.entity.*; -import org.geysermc.connector.network.translators.java.entity.player.JavaPlayerActionAckTranslator; -import org.geysermc.connector.network.translators.java.entity.player.JavaPlayerHealthTranslator; -import org.geysermc.connector.network.translators.java.entity.player.JavaPlayerPositionRotationTranslator; -import org.geysermc.connector.network.translators.java.entity.player.JavaPlayerSetExperienceTranslator; +import org.geysermc.connector.network.translators.java.entity.player.*; import org.geysermc.connector.network.translators.java.entity.spawn.*; import org.geysermc.connector.network.translators.java.scoreboard.JavaDisplayScoreboardTranslator; import org.geysermc.connector.network.translators.java.scoreboard.JavaScoreboardObjectiveTranslator; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockActionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockActionTranslator.java index fdab51ba6..b8382b8ce 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockActionTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockActionTranslator.java @@ -25,7 +25,6 @@ package org.geysermc.connector.network.translators.bedrock; -import com.flowpowered.math.vector.Vector3i; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; import com.github.steveice10.mc.protocol.data.game.entity.player.Hand; import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction; @@ -34,6 +33,7 @@ import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace; import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerActionPacket; import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPlaceBlockPacket; import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerStatePacket; +import com.nukkitx.math.vector.Vector3i; import com.nukkitx.protocol.bedrock.packet.PlayerActionPacket; import org.geysermc.connector.entity.Entity; import org.geysermc.connector.network.session.GeyserSession; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInteractTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInteractTranslator.java index 2fad8c219..9b2060183 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInteractTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInteractTranslator.java @@ -25,21 +25,11 @@ package org.geysermc.connector.network.translators.bedrock; -import com.flowpowered.math.vector.Vector3f; -import com.flowpowered.math.vector.Vector3i; -import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; import com.github.steveice10.mc.protocol.data.game.entity.player.Hand; import com.github.steveice10.mc.protocol.data.game.entity.player.InteractAction; -import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction; -import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerState; -import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace; -import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerActionPacket; import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerInteractEntityPacket; -import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPlaceBlockPacket; -import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerStatePacket; +import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.packet.InteractPacket; -import com.nukkitx.protocol.bedrock.packet.PlayerActionPacket; -import org.geysermc.connector.entity.Entity; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockMovePlayerTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockMovePlayerTranslator.java index ad7645a4a..e4ab1b1d3 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockMovePlayerTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockMovePlayerTranslator.java @@ -25,9 +25,10 @@ package org.geysermc.connector.network.translators.bedrock; -import com.flowpowered.math.vector.Vector3f; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPositionRotationPacket; +import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.packet.MoveEntityAbsolutePacket; import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket; import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket; import org.geysermc.connector.entity.Entity; @@ -44,9 +45,15 @@ public class BedrockMovePlayerTranslator extends PacketTranslator { @Override public void translate(SetLocalPlayerAsInitializedPacket packet, GeyserSession session) { if (session.getPlayerEntity().getGeyserId() == packet.getRuntimeEntityId()) { - session.getUpstream().setFrozen(false); + if (!session.getUpstream().isInitialized()) { + session.getUpstream().setInitialized(true); + + for (PlayerEntity entity : session.getEntityCache().getEntitiesByType(PlayerEntity.class)) { + if (!entity.isValid()) { + entity.sendPlayer(session); + // async skin loading + SkinUtils.requestAndHandleSkinAndCape(entity, session, skinAndCape -> entity.sendPlayer(session)); + } + } + } } } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/GenericInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/GenericInventoryTranslator.java index e2f4fa582..862117f92 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/GenericInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/GenericInventoryTranslator.java @@ -25,7 +25,7 @@ package org.geysermc.connector.network.translators.inventory; -import com.flowpowered.math.vector.Vector3i; +import com.nukkitx.math.vector.Vector3i; import com.nukkitx.protocol.bedrock.data.ItemData; import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket; import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket; @@ -46,7 +46,7 @@ public class GenericInventoryTranslator extends InventoryTranslator { ContainerOpenPacket containerOpenPacket = new ContainerOpenPacket(); containerOpenPacket.setWindowId((byte) inventory.getId()); containerOpenPacket.setType((byte) 0); - containerOpenPacket.setBlockPosition(new Vector3i(0, 0, 0)); + containerOpenPacket.setBlockPosition(Vector3i.ZERO); session.getUpstream().sendPacket(containerOpenPacket); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaBossBarTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaBossBarTranslator.java index 8b89e3be3..3073e469c 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaBossBarTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaBossBarTranslator.java @@ -1,7 +1,7 @@ package org.geysermc.connector.network.translators.java; -import com.flowpowered.math.vector.Vector3f; import com.github.steveice10.mc.protocol.packet.ingame.server.ServerBossBarPacket; +import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.EntityData; import com.nukkitx.protocol.bedrock.packet.AddEntityPacket; import com.nukkitx.protocol.bedrock.packet.BossEventPacket; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityHeadLookTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityHeadLookTranslator.java index f583ea91d..34addcee1 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityHeadLookTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityHeadLookTranslator.java @@ -25,8 +25,8 @@ package org.geysermc.connector.network.translators.java.entity; -import com.flowpowered.math.vector.Vector3f; import com.github.steveice10.mc.protocol.packet.ingame.server.entity.ServerEntityHeadLookPacket; +import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.packet.MoveEntityAbsolutePacket; import org.geysermc.connector.entity.Entity; import org.geysermc.connector.network.session.GeyserSession; @@ -43,7 +43,7 @@ public class JavaEntityHeadLookTranslator extends PacketTranslator { @Override @@ -16,16 +15,15 @@ public class JavaPlayerListEntryTranslator extends PacketTranslator { // #slowdownbrother, just don't execute it directly PaintingEntity entity = new PaintingEntity( diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnPlayerTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnPlayerTranslator.java index 968b0b3ab..688a00bae 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnPlayerTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnPlayerTranslator.java @@ -25,27 +25,21 @@ package org.geysermc.connector.network.translators.java.entity.spawn; -import com.flowpowered.math.vector.Vector3f; -import com.github.steveice10.mc.auth.data.GameProfile; import com.github.steveice10.mc.protocol.packet.ingame.server.entity.spawn.ServerSpawnPlayerPacket; -import com.google.gson.JsonObject; -import com.nukkitx.protocol.bedrock.packet.PlayerListPacket; -import org.apache.commons.codec.Charsets; +import com.nukkitx.math.vector.Vector3f; import org.geysermc.api.Geyser; import org.geysermc.connector.entity.PlayerEntity; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; -import org.geysermc.connector.utils.SkinProvider; - -import java.util.Base64; +import org.geysermc.connector.utils.SkinUtils; public class JavaSpawnPlayerTranslator extends PacketTranslator { @Override public void translate(ServerSpawnPlayerPacket packet, GeyserSession session) { - Vector3f position = new Vector3f(packet.getX(), packet.getY() - EntityType.PLAYER.getOffset(), packet.getZ()); - Vector3f rotation = new Vector3f(packet.getYaw(), packet.getPitch(), packet.getYaw()); + Vector3f position = Vector3f.from(packet.getX(), packet.getY() - EntityType.PLAYER.getOffset(), packet.getZ()); + Vector3f rotation = Vector3f.from(packet.getYaw(), packet.getPitch(), packet.getYaw()); PlayerEntity entity = session.getEntityCache().getPlayerEntity(packet.getUUID()); if (entity == null) { @@ -57,64 +51,8 @@ public class JavaSpawnPlayerTranslator extends PacketTranslator { - GameProfile.Property skinProperty = entity.getProfile().getProperty("textures"); - - JsonObject skinObject = SkinProvider.GSON.fromJson(new String(Base64.getDecoder().decode(skinProperty.getValue()), Charsets.UTF_8), JsonObject.class); - JsonObject textures = skinObject.getAsJsonObject("textures"); - - JsonObject skinTexture = textures.getAsJsonObject("SKIN"); - String skinUrl = skinTexture.get("url").getAsString(); - - boolean isAlex = skinTexture.has("metadata"); - - String capeUrl = null; - if (textures.has("CAPE")) { - JsonObject capeTexture = textures.getAsJsonObject("CAPE"); - capeUrl = capeTexture.get("url").getAsString(); - } - - SkinProvider.requestAndHandleSkinAndCape(entity.getUuid(), skinUrl, capeUrl) - .whenCompleteAsync((skinAndCape, throwable) -> { - SkinProvider.Skin skin = skinAndCape.getSkin(); - SkinProvider.Cape cape = skinAndCape.getCape(); - - if (cape.isFailed() && SkinProvider.ALLOW_THIRD_PARTY_CAPES) { - cape = SkinProvider.getOrDefault(SkinProvider.requestAndHandleUnofficialCape( - cape, entity.getUuid(), - entity.getUsername(), false - ), SkinProvider.EMPTY_CAPE, SkinProvider.UnofficalCape.VALUES.length * 3); - } - - if (entity.getLastSkinUpdate() < skin.getRequestedOn()) { - entity.setLastSkinUpdate(skin.getRequestedOn()); - - PlayerListPacket.Entry updatedEntry = new PlayerListPacket.Entry(skin.getSkinOwner()); - updatedEntry.setName(entity.getUsername()); - updatedEntry.setEntityId(entity.getGeyserId()); - updatedEntry.setSkinId(entity.getUuid().toString()); - updatedEntry.setSkinData(skin.getSkinData()); - updatedEntry.setCapeData(cape.getCapeData()); - updatedEntry.setGeometryName("geometry.humanoid.custom" + (isAlex ? "Slim" : "")); - updatedEntry.setGeometryData(""); - updatedEntry.setXuid(""); - updatedEntry.setPlatformChatId(""); - - PlayerListPacket playerRemovePacket = new PlayerListPacket(); - playerRemovePacket.setType(PlayerListPacket.Type.REMOVE); - playerRemovePacket.getEntries().add(updatedEntry); - session.getUpstream().sendPacket(playerRemovePacket); - - PlayerListPacket playerAddPacket = new PlayerListPacket(); - playerAddPacket.setType(PlayerListPacket.Type.ADD); - playerAddPacket.getEntries().add(updatedEntry); - session.getUpstream().sendPacket(playerAddPacket); - } - }).isCompletedExceptionally(); - }); + entity.sendPlayer(session); + // async skin loading + SkinUtils.requestAndHandleSkinAndCape(entity, session, skinAndCape -> entity.sendPlayer(session)); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockChangeTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockChangeTranslator.java index 63d1b927d..6ce675338 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockChangeTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaBlockChangeTranslator.java @@ -1,8 +1,8 @@ package org.geysermc.connector.network.translators.java.world; -import com.flowpowered.math.vector.Vector3i; import com.github.steveice10.mc.protocol.data.game.world.block.BlockChangeRecord; import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerBlockChangePacket; +import com.nukkitx.math.vector.Vector3i; import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; @@ -16,10 +16,11 @@ public class JavaBlockChangeTranslator extends PacketTranslator> 38), (int) (l & 0xFFF), (int) ((l << 26) >> 38) ); } @@ -70,7 +55,7 @@ public class GeyserUtils { } public static Vector3d readLegacyPositionI(ByteBuf from) { - return new Vector3d(from.readInt(), from.readInt(), from.readInt()); + return Vector3d.from(from.readInt(), from.readInt(), from.readInt()); } public static void writePosition(ByteBuf to, Vector3i position) { @@ -106,11 +91,11 @@ public class GeyserUtils { } public static Vector2i readIntChunkCoord(ByteBuf from) { - return new Vector2i(from.readInt(), from.readInt()); + return Vector2i.from(from.readInt(), from.readInt()); } public static Vector2i readVarIntChunkCoord(ByteBuf from) { - return new Vector2i(readVarInt(from), readVarInt(from)); + return Vector2i.from(readVarInt(from), readVarInt(from)); } public static void writeIntChunkCoord(ByteBuf to, Vector2i chunk) { @@ -119,7 +104,7 @@ public class GeyserUtils { } public static Vector2i readPEChunkCoord(ByteBuf from) { - return new Vector2i(readSVarInt(from), readSVarInt(from)); + return Vector2i.from(readSVarInt(from), readSVarInt(from)); } public static void writePEChunkCoord(ByteBuf to, Vector2i chunk) { @@ -140,513 +125,269 @@ public class GeyserUtils { writeVarInt(to, chunk.getY()); } - - - public static final int MAX_LENGTH = 5; - - + public static final int MAX_VARINT_LENGTH = 5; public static void writeFixedSizeVarInt(ByteBuf to, int i) { - int writerIndex = to.writerIndex(); - while ((i & 0xFFFFFF80) != 0x0) { - to.writeByte(i | 0x80); - i >>>= 7; } - - int paddingBytes = MAX_LENGTH - (to.writerIndex() - writerIndex) - 1; + int paddingBytes = MAX_VARINT_LENGTH - (to.writerIndex() - writerIndex) - 1; if (paddingBytes == 0) { - to.writeByte(i); - } else { - to.writeByte(i | 0x80); - while (--paddingBytes > 0) { - to.writeByte(0x80); - } - to.writeByte(0); - } - } - - public static int readVarInt(ByteBuf from) { - int value = 0; - int length = 0; byte part; - do { - part = from.readByte(); - value |= (part & 0x7F) << (length++ * 7); - - if (length > MAX_LENGTH) { - + if (length > MAX_VARINT_LENGTH) { throw new DecoderException("VarInt too big"); - } - } while (part < 0); - return value; - } - - public static void writeVarInt(ByteBuf to, int i) { - while ((i & 0xFFFFFF80) != 0x0) { - to.writeByte(i | 0x80); - i >>>= 7; - } - to.writeByte(i); - } - - public static int readSVarInt(ByteBuf from) { - int varint = readVarInt(from); - return (varint >> 1) ^ -(varint & 1); - } - public static void writeSVarInt(ByteBuf to, int varint) { - writeVarInt(to, (varint << 1) ^ (varint >> 31)); - } - - public static long readVarLong(ByteBuf from) { - long varlong = 0L; - int length = 0; byte part; - do { - part = from.readByte(); - varlong |= (part & 0x7F) << (length++ * 7); if (length > 10) { - throw new RuntimeException("VarLong too big"); - } - } while ((part & 0x80) == 0x80); - return varlong; - } - - public static void writeVarLong(ByteBuf to, long varlong) { - while ((varlong & 0xFFFFFFFFFFFFFF80L) != 0x0L) { - to.writeByte((int) (varlong & 0x7FL) | 0x80); - varlong >>>= 7; - } - to.writeByte((int) varlong); - } - - public static long readSVarLong(ByteBuf from) { - long varlong = readVarLong(from); - return (varlong >> 1) ^ -(varlong & 1); - } - public static void writeSVarLong(ByteBuf to, long varlong) { - writeVarLong(to, (varlong << 1) ^ (varlong >> 63)); } - public static ByteBuf readShortByteArraySlice(ByteBuf from, int limit) { - int length = from.readShort(); - checkLimit(length, limit); - return from.readSlice(length); } - - @SuppressWarnings("unchecked") - public static T[] readShortTArray(ByteBuf from, Class tclass, Function elementReader) { - T[] array = (T[]) Array.newInstance(tclass, from.readShort()); - for (int i = 0; i < array.length; i++) { - array[i] = elementReader.apply(from); - } - return array; - } - - - - public static byte[] readVarIntByteArray(ByteBuf from) { - return readBytes(from, readVarInt(from)); - } - - public static ByteBuf readVarIntByteArraySlice(ByteBuf from, int limit) { - int length = readVarInt(from); - checkLimit(length, limit); - return from.readSlice(length); - } - - public static ByteBuf readVarIntByteArraySlice(ByteBuf from) { - return from.readSlice(readVarInt(from)); - } - - @SuppressWarnings("unchecked") - public static T[] readVarIntTArray(ByteBuf from, Class tclass, Function elementReader) { - T[] array = (T[]) Array.newInstance(tclass, readVarInt(from)); - for (int i = 0; i < array.length; i++) { - array[i] = elementReader.apply(from); - } - return array; - } - - public static int[] readVarIntVarIntArray(ByteBuf from) { - int[] array = new int[readVarInt(from)]; - for (int i = 0; i < array.length; i++) { - array[i] = readVarInt(from); - } - return array; - } - - - - public static void writeShortByteArray(ByteBuf to, ByteBuf data) { - to.writeShort(data.readableBytes()); - to.writeBytes(data); - } - - public static void writeShortByteArray(ByteBuf to, byte[] data) { - to.writeShort(data.length); - to.writeBytes(data); - } - - public static void writeShortByteArray(ByteBuf to, Consumer dataWriter) { - - writeLengthPrefixedBytes(to, (lTo, length) -> lTo.writeShort(length), dataWriter); - + writeLengthPrefixedBytes(to, ByteBuf::writeShort, dataWriter); } - public static void writeShortTArray(ByteBuf to, T[] array, BiConsumer elementWriter) { - to.writeShort(array.length); - for (T element : array) { - elementWriter.accept(to, element); - } - } - - - - public static void writeVarIntByteArray(ByteBuf to, ByteBuf data) { - writeVarInt(to, data.readableBytes()); - to.writeBytes(data); - } - public static void writeVarIntByteArray(ByteBuf to, byte[] data) { - writeVarInt(to, data.length); - to.writeBytes(data); - } - - public static void writeVarIntByteArray(ByteBuf to, Consumer dataWriter) { - writeLengthPrefixedBytes(to, GeyserUtils::writeFixedSizeVarInt, dataWriter); - } - - public static void writeVarIntTArray(ByteBuf to, ToIntFunction arrayWriter) { - writeSizePrefixedData(to, GeyserUtils::writeFixedSizeVarInt, arrayWriter); - } - - public static void writeVarIntTArray(ByteBuf to, T[] array, BiConsumer elementWriter) { - writeVarInt(to, array.length); - for (T element : array) { - elementWriter.accept(to, element); - } - } - - public static void writeVarIntTArray(ByteBuf to, List array, BiConsumer elementWriter) { - writeVarInt(to, array.size()); - for (T element : array) { - elementWriter.accept(to, element); - } - } - public static void writeVarIntEnum(ByteBuf to, Enum e) { - writeVarInt(to, e.ordinal()); - } - - public static void writeByteEnum(ByteBuf to, Enum e) { - to.writeByte(e.ordinal()); - } - - public static UUID readUUID(ByteBuf from) { - return new UUID(from.readLong(), from.readLong()); - } - - public static void writeUUID(ByteBuf to, UUID uuid) { - to.writeLong(uuid.getMostSignificantBits()); - to.writeLong(uuid.getLeastSignificantBits()); - } - - public static void writePEUUID(ByteBuf to, UUID uuid) { - to.writeLongLE(uuid.getMostSignificantBits()); - to.writeLongLE(uuid.getLeastSignificantBits()); - } - - public static byte[] readAllBytes(ByteBuf buf) { - return readBytes(buf, buf.readableBytes()); - } - - public static ByteBuf readAllBytesSlice(ByteBuf from) { - return from.readSlice(from.readableBytes()); - } - - public static ByteBuf readAllBytesSlice(ByteBuf buf, int limit) { - checkLimit(buf.readableBytes(), limit); - return readAllBytesSlice(buf); - } - - public static byte[] readBytes(ByteBuf buf, int length) { - byte[] result = new byte[length]; - buf.readBytes(result); - return result; - } - - protected static void checkLimit(int length, int limit) { - if (length > limit) { - throw new DecoderException(MessageFormat.format("Size {0} is bigger than allowed {1}", length, limit)); - } - } - - public static void writeLengthPrefixedBytes(ByteBuf to, ObjIntConsumer lengthWriter, Consumer dataWriter) { - int lengthWriterIndex = to.writerIndex(); - lengthWriter.accept(to, 0); - int writerIndexDataStart = to.writerIndex(); - dataWriter.accept(to); - int writerIndexDataEnd = to.writerIndex(); - to.writerIndex(lengthWriterIndex); - lengthWriter.accept(to, writerIndexDataEnd - writerIndexDataStart); - to.writerIndex(writerIndexDataEnd); - } - - public static void writeSizePrefixedData(ByteBuf to, ObjIntConsumer sizeWriter, ToIntFunction dataWriter) { - int sizeWriterIndex = to.writerIndex(); - sizeWriter.accept(to, 0); - int size = dataWriter.applyAsInt(to); - int writerIndexDataEnd = to.writerIndex(); - to.writerIndex(sizeWriterIndex); - sizeWriter.accept(to, size); - to.writerIndex(writerIndexDataEnd); - } private static int getAnvilIndex(int x, int y, int z) { - return (y << 8) + (z << 4) + x; - } public static boolean instanceOf(Class clazz, Object o) { @@ -657,6 +398,4 @@ public class GeyserUtils { return false; } } - - } diff --git a/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java b/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java index 298661c0d..3483a927c 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java +++ b/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java @@ -49,13 +49,13 @@ public class SkinProvider { return cachedCapes.get(capeUrl); } - public static CompletableFuture requestAndHandleSkinAndCape(UUID playerId, String skinUrl, String capeUrl) { + public static CompletableFuture requestSkinAndCape(UUID playerId, String skinUrl, String capeUrl) { return CompletableFuture.supplyAsync(() -> { long time = System.currentTimeMillis(); SkinAndCape skinAndCape = new SkinAndCape( - getOrDefault(requestAndHandleSkin(playerId, skinUrl, false), EMPTY_SKIN, 5), - getOrDefault(requestAndHandleCape(capeUrl, false), EMPTY_CAPE, 5) + getOrDefault(requestSkin(playerId, skinUrl, false), EMPTY_SKIN, 5), + getOrDefault(requestCape(capeUrl, false), EMPTY_CAPE, 5) ); Geyser.getLogger().debug("Took " + (System.currentTimeMillis() - time) + "ms for " + playerId); @@ -63,7 +63,7 @@ public class SkinProvider { }, EXECUTOR_SERVICE); } - public static CompletableFuture requestAndHandleSkin(UUID playerId, String textureUrl, boolean newThread) { + public static CompletableFuture requestSkin(UUID playerId, String textureUrl, boolean newThread) { if (textureUrl == null || textureUrl.isEmpty()) return CompletableFuture.completedFuture(EMPTY_SKIN); if (requestedSkins.containsKey(playerId)) return requestedSkins.get(playerId); // already requested @@ -91,7 +91,7 @@ public class SkinProvider { return future; } - public static CompletableFuture requestAndHandleCape(String capeUrl, boolean newThread) { + public static CompletableFuture requestCape(String capeUrl, boolean newThread) { if (capeUrl == null || capeUrl.isEmpty()) return CompletableFuture.completedFuture(EMPTY_CAPE); if (requestedCapes.containsKey(capeUrl)) return requestedCapes.get(capeUrl); // already requested @@ -119,12 +119,12 @@ public class SkinProvider { return future; } - public static CompletableFuture requestAndHandleUnofficialCape(Cape officialCape, UUID playerId, - String username, boolean newThread) { + public static CompletableFuture requestUnofficialCape(Cape officialCape, UUID playerId, + String username, boolean newThread) { if (officialCape.isFailed() && ALLOW_THIRD_PARTY_CAPES) { for (UnofficalCape cape : UnofficalCape.VALUES) { Cape cape1 = getOrDefault( - requestAndHandleCape(cape.getUrlFor(playerId, username), newThread), + requestCape(cape.getUrlFor(playerId, username), newThread), EMPTY_CAPE, 4 ); if (!cape1.isFailed()) { diff --git a/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java b/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java new file mode 100644 index 000000000..7254637af --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java @@ -0,0 +1,142 @@ +package org.geysermc.connector.utils; + +import com.github.steveice10.mc.auth.data.GameProfile; +import com.google.gson.JsonObject; +import com.nukkitx.protocol.bedrock.packet.PlayerListPacket; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.apache.commons.codec.Charsets; +import org.geysermc.api.Geyser; +import org.geysermc.connector.entity.PlayerEntity; +import org.geysermc.connector.network.session.GeyserSession; + +import java.util.Base64; +import java.util.UUID; +import java.util.function.Consumer; + +public class SkinUtils { + public static PlayerListPacket.Entry buildCachedEntry(GameProfile profile, long geyserId) { + GameProfileData data = GameProfileData.from(profile); + + return buildEntryManually( + profile.getId(), + profile.getName(), + geyserId, + profile.getIdAsString(), + SkinProvider.getCachedSkin(profile.getId()).getSkinData(), + SkinProvider.getCachedCape(data.getCapeUrl()).getCapeData(), + "geometry.humanoid.custom" + (data.isAlex() ? "Slim" : ""), + "" + ); + } + + public static PlayerListPacket.Entry buildDefaultEntry(GameProfile profile, long geyserId) { + return buildEntryManually( + profile.getId(), + profile.getName(), + geyserId, + profile.getIdAsString(), + SkinProvider.STEVE_SKIN, + SkinProvider.EMPTY_CAPE.getCapeData(), + "geometry.humanoid", + "" + ); + } + + public static PlayerListPacket.Entry buildEntryManually(UUID uuid, String username, long geyserId, + String skinId, byte[] skinData, byte[] capeData, + String geometryName, String geometryData) { + PlayerListPacket.Entry entry = new PlayerListPacket.Entry(uuid); + entry.setName(username); + entry.setEntityId(geyserId); + entry.setSkinId(skinId); + entry.setSkinData(skinData != null ? skinData : SkinProvider.STEVE_SKIN); + entry.setCapeData(capeData); + entry.setGeometryName(geometryName); + entry.setGeometryData(geometryData); + entry.setXuid(""); + entry.setPlatformChatId(""); + return entry; + } + + @AllArgsConstructor + @Getter + public static class GameProfileData { + private String skinUrl; + private String capeUrl; + private boolean alex; + + public static GameProfileData from(GameProfile profile) { + GameProfile.Property skinProperty = profile.getProperty("textures"); + + JsonObject skinObject = SkinProvider.GSON.fromJson(new String(Base64.getDecoder().decode(skinProperty.getValue()), Charsets.UTF_8), JsonObject.class); + JsonObject textures = skinObject.getAsJsonObject("textures"); + + JsonObject skinTexture = textures.getAsJsonObject("SKIN"); + String skinUrl = skinTexture.get("url").getAsString(); + + boolean isAlex = skinTexture.has("metadata"); + + String capeUrl = null; + if (textures.has("CAPE")) { + JsonObject capeTexture = textures.getAsJsonObject("CAPE"); + capeUrl = capeTexture.get("url").getAsString(); + } + + return new GameProfileData(skinUrl, capeUrl, isAlex); + } + } + + public static void requestAndHandleSkinAndCape(PlayerEntity entity, GeyserSession session, + Consumer skinAndCapeConsumer) { + Geyser.getGeneralThreadPool().execute(() -> { + SkinUtils.GameProfileData data = SkinUtils.GameProfileData.from(entity.getProfile()); + + SkinProvider.requestSkinAndCape(entity.getUuid(), data.getSkinUrl(), data.getCapeUrl()) + .whenCompleteAsync((skinAndCape, throwable) -> { + try { + SkinProvider.Skin skin = skinAndCape.getSkin(); + SkinProvider.Cape cape = skinAndCape.getCape(); + + if (cape.isFailed() && SkinProvider.ALLOW_THIRD_PARTY_CAPES) { + cape = SkinProvider.getOrDefault(SkinProvider.requestUnofficialCape( + cape, entity.getUuid(), + entity.getUsername(), false + ), SkinProvider.EMPTY_CAPE, SkinProvider.UnofficalCape.VALUES.length * 3); + } + + if (entity.getLastSkinUpdate() < skin.getRequestedOn()) { + entity.setLastSkinUpdate(skin.getRequestedOn()); + + if (session.getUpstream().isInitialized()) { + PlayerListPacket.Entry updatedEntry = SkinUtils.buildEntryManually( + entity.getUuid(), + entity.getUsername(), + entity.getGeyserId(), + entity.getUuid().toString(), + skin.getSkinData(), + cape.getCapeData(), + "geometry.humanoid.custom" + (data.isAlex() ? "Slim" : ""), + "" + ); + + PlayerListPacket playerRemovePacket = new PlayerListPacket(); + playerRemovePacket.setType(PlayerListPacket.Type.REMOVE); + playerRemovePacket.getEntries().add(updatedEntry); + session.getUpstream().sendPacket(playerRemovePacket); + + PlayerListPacket playerAddPacket = new PlayerListPacket(); + playerAddPacket.setType(PlayerListPacket.Type.ADD); + playerAddPacket.getEntries().add(updatedEntry); + session.getUpstream().sendPacket(playerAddPacket); + } + } + } catch (Exception e) { + Geyser.getLogger().error("Failed getting skin for " + entity.getUuid(), e); + } + + if (skinAndCapeConsumer != null) skinAndCapeConsumer.accept(skinAndCape); + }); + }); + } +} From 46cb14fc0ad3a17ff9a86a62ab4696499be5d190 Mon Sep 17 00:00:00 2001 From: Tim203 Date: Thu, 10 Oct 2019 23:16:07 +0200 Subject: [PATCH 6/8] Removed unused class and made PlayerList show every player again --- .../player/JavaPlayerListEntryTranslator.java | 7 +- .../geysermc/connector/utils/GeyserUtils.java | 401 ------------------ .../connector/utils/SkinProvider.java | 4 +- 3 files changed, 7 insertions(+), 405 deletions(-) delete mode 100644 connector/src/main/java/org/geysermc/connector/utils/GeyserUtils.java diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerListEntryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerListEntryTranslator.java index d9877e4f2..fc6f58f83 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerListEntryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerListEntryTranslator.java @@ -8,6 +8,7 @@ import com.nukkitx.protocol.bedrock.packet.PlayerListPacket; import org.geysermc.connector.entity.PlayerEntity; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; +import org.geysermc.connector.utils.SkinUtils; public class JavaPlayerListEntryTranslator extends PacketTranslator { @Override @@ -15,7 +16,7 @@ public class JavaPlayerListEntryTranslator extends PacketTranslator> 38), (int) (l & 0xFFF), (int) ((l << 26) >> 38) - ); - } - - public static void readPEPosition(ByteBuf from) { - readSVarInt(from); - readVarInt(from); - readSVarInt(from); - } - - public static Vector3d readLegacyPositionI(ByteBuf from) { - return Vector3d.from(from.readInt(), from.readInt(), from.readInt()); - } - - public static void writePosition(ByteBuf to, Vector3i position) { - to.writeLong(((position.getX() & 0x3FFFFFFL) << 38) | ((position.getZ() & 0x3FFFFFFL) << 12) | (position.getY() & 0xFFFL)); - } - - public static void writeLegacyPositionL(ByteBuf to, Vector3d position) { - to.writeLong((((int) position.getX() & 0x3FFFFFFL) << 38) | (((int) position.getY() & 0xFFFL) << 26) | ((int) position.getZ() & 0x3FFFFFFL)); - } - - public static void writePEPosition(ByteBuf to, Vector3d position) { - writeSVarInt(to, (int) position.getX()); - writeVarInt(to, (int) position.getY()); - writeSVarInt(to, (int) position.getZ()); - } - - public static void writeLegacyPositionB(ByteBuf to, Vector3d position) { - to.writeInt((int) position.getX()); - to.writeByte((int) position.getY()); - to.writeInt((int) position.getZ()); - } - - public static void writeLegacyPositionS(ByteBuf to, Vector3d position) { - to.writeInt((int) position.getX()); - to.writeShort((int) position.getY()); - to.writeInt((int) position.getZ()); - } - - public static void writeLegacyPositionI(ByteBuf to, Vector3d position) { - to.writeInt((int) position.getX()); - to.writeInt((int) position.getY()); - to.writeInt((int) position.getZ()); - } - - public static Vector2i readIntChunkCoord(ByteBuf from) { - return Vector2i.from(from.readInt(), from.readInt()); - } - - public static Vector2i readVarIntChunkCoord(ByteBuf from) { - return Vector2i.from(readVarInt(from), readVarInt(from)); - } - - public static void writeIntChunkCoord(ByteBuf to, Vector2i chunk) { - to.writeInt(chunk.getX()); - to.writeInt(chunk.getY()); - } - - public static Vector2i readPEChunkCoord(ByteBuf from) { - return Vector2i.from(readSVarInt(from), readSVarInt(from)); - } - - public static void writePEChunkCoord(ByteBuf to, Vector2i chunk) { - writeSVarInt(to, chunk.getX()); - writeSVarInt(to, chunk.getY()); - } - - public static int readLocalCoord(ByteBuf from) { - return from.readUnsignedShort(); - } - - public static void writeLocalCoord(ByteBuf to, int coord) { - to.writeShort(coord); - } - - public static void writeVarIntChunkCoord(ByteBuf to, Vector2i chunk) { - writeVarInt(to, chunk.getX()); - writeVarInt(to, chunk.getY()); - } - - public static final int MAX_VARINT_LENGTH = 5; - - public static void writeFixedSizeVarInt(ByteBuf to, int i) { - int writerIndex = to.writerIndex(); - while ((i & 0xFFFFFF80) != 0x0) { - to.writeByte(i | 0x80); - i >>>= 7; - - } - int paddingBytes = MAX_VARINT_LENGTH - (to.writerIndex() - writerIndex) - 1; - - if (paddingBytes == 0) { - to.writeByte(i); - } else { - to.writeByte(i | 0x80); - while (--paddingBytes > 0) { - to.writeByte(0x80); - } - to.writeByte(0); - } - } - - public static int readVarInt(ByteBuf from) { - int value = 0; - int length = 0; - - byte part; - do { - part = from.readByte(); - value |= (part & 0x7F) << (length++ * 7); - if (length > MAX_VARINT_LENGTH) { - throw new DecoderException("VarInt too big"); - } - } while (part < 0); - return value; - } - - public static void writeVarInt(ByteBuf to, int i) { - while ((i & 0xFFFFFF80) != 0x0) { - to.writeByte(i | 0x80); - i >>>= 7; - } - to.writeByte(i); - } - - public static int readSVarInt(ByteBuf from) { - int varint = readVarInt(from); - return (varint >> 1) ^ -(varint & 1); - } - - - public static void writeSVarInt(ByteBuf to, int varint) { - writeVarInt(to, (varint << 1) ^ (varint >> 31)); - } - - public static long readVarLong(ByteBuf from) { - long varlong = 0L; - int length = 0; - - byte part; - do { - part = from.readByte(); - varlong |= (part & 0x7F) << (length++ * 7); - - if (length > 10) { - throw new RuntimeException("VarLong too big"); - } - } while ((part & 0x80) == 0x80); - return varlong; - } - - public static void writeVarLong(ByteBuf to, long varlong) { - while ((varlong & 0xFFFFFFFFFFFFFF80L) != 0x0L) { - to.writeByte((int) (varlong & 0x7FL) | 0x80); - varlong >>>= 7; - } - to.writeByte((int) varlong); - } - - public static long readSVarLong(ByteBuf from) { - long varlong = readVarLong(from); - return (varlong >> 1) ^ -(varlong & 1); - } - - - public static void writeSVarLong(ByteBuf to, long varlong) { - writeVarLong(to, (varlong << 1) ^ (varlong >> 63)); - - } - - public static ByteBuf readShortByteArraySlice(ByteBuf from, int limit) { - int length = from.readShort(); - checkLimit(length, limit); - return from.readSlice(length); - - } - - @SuppressWarnings("unchecked") - public static T[] readShortTArray(ByteBuf from, Class tclass, Function elementReader) { - T[] array = (T[]) Array.newInstance(tclass, from.readShort()); - for (int i = 0; i < array.length; i++) { - array[i] = elementReader.apply(from); - } - return array; - } - - public static byte[] readVarIntByteArray(ByteBuf from) { - return readBytes(from, readVarInt(from)); - } - - public static ByteBuf readVarIntByteArraySlice(ByteBuf from, int limit) { - int length = readVarInt(from); - checkLimit(length, limit); - return from.readSlice(length); - } - - public static ByteBuf readVarIntByteArraySlice(ByteBuf from) { - return from.readSlice(readVarInt(from)); - } - - @SuppressWarnings("unchecked") - public static T[] readVarIntTArray(ByteBuf from, Class tclass, Function elementReader) { - T[] array = (T[]) Array.newInstance(tclass, readVarInt(from)); - for (int i = 0; i < array.length; i++) { - array[i] = elementReader.apply(from); - } - return array; - } - - public static int[] readVarIntVarIntArray(ByteBuf from) { - int[] array = new int[readVarInt(from)]; - for (int i = 0; i < array.length; i++) { - array[i] = readVarInt(from); - } - return array; - } - - public static void writeShortByteArray(ByteBuf to, ByteBuf data) { - to.writeShort(data.readableBytes()); - to.writeBytes(data); - } - - public static void writeShortByteArray(ByteBuf to, byte[] data) { - to.writeShort(data.length); - to.writeBytes(data); - } - - public static void writeShortByteArray(ByteBuf to, Consumer dataWriter) { - writeLengthPrefixedBytes(to, ByteBuf::writeShort, dataWriter); - } - - - public static void writeShortTArray(ByteBuf to, T[] array, BiConsumer elementWriter) { - to.writeShort(array.length); - for (T element : array) { - elementWriter.accept(to, element); - } - } - - public static void writeVarIntByteArray(ByteBuf to, ByteBuf data) { - writeVarInt(to, data.readableBytes()); - to.writeBytes(data); - } - - - public static void writeVarIntByteArray(ByteBuf to, byte[] data) { - writeVarInt(to, data.length); - to.writeBytes(data); - } - - public static void writeVarIntByteArray(ByteBuf to, Consumer dataWriter) { - writeLengthPrefixedBytes(to, GeyserUtils::writeFixedSizeVarInt, dataWriter); - } - - public static void writeVarIntTArray(ByteBuf to, ToIntFunction arrayWriter) { - writeSizePrefixedData(to, GeyserUtils::writeFixedSizeVarInt, arrayWriter); - } - - public static void writeVarIntTArray(ByteBuf to, T[] array, BiConsumer elementWriter) { - writeVarInt(to, array.length); - for (T element : array) { - elementWriter.accept(to, element); - } - } - - public static void writeVarIntTArray(ByteBuf to, List array, BiConsumer elementWriter) { - writeVarInt(to, array.size()); - for (T element : array) { - elementWriter.accept(to, element); - } - } - - public static void writeVarIntEnum(ByteBuf to, Enum e) { - writeVarInt(to, e.ordinal()); - } - - public static void writeByteEnum(ByteBuf to, Enum e) { - to.writeByte(e.ordinal()); - } - - public static UUID readUUID(ByteBuf from) { - return new UUID(from.readLong(), from.readLong()); - } - - public static void writeUUID(ByteBuf to, UUID uuid) { - to.writeLong(uuid.getMostSignificantBits()); - to.writeLong(uuid.getLeastSignificantBits()); - } - - public static void writePEUUID(ByteBuf to, UUID uuid) { - to.writeLongLE(uuid.getMostSignificantBits()); - to.writeLongLE(uuid.getLeastSignificantBits()); - } - - public static byte[] readAllBytes(ByteBuf buf) { - return readBytes(buf, buf.readableBytes()); - } - - public static ByteBuf readAllBytesSlice(ByteBuf from) { - return from.readSlice(from.readableBytes()); - } - - public static ByteBuf readAllBytesSlice(ByteBuf buf, int limit) { - checkLimit(buf.readableBytes(), limit); - return readAllBytesSlice(buf); - } - - public static byte[] readBytes(ByteBuf buf, int length) { - byte[] result = new byte[length]; - buf.readBytes(result); - return result; - } - - protected static void checkLimit(int length, int limit) { - if (length > limit) { - throw new DecoderException(MessageFormat.format("Size {0} is bigger than allowed {1}", length, limit)); - } - } - - public static void writeLengthPrefixedBytes(ByteBuf to, ObjIntConsumer lengthWriter, Consumer dataWriter) { - int lengthWriterIndex = to.writerIndex(); - lengthWriter.accept(to, 0); - int writerIndexDataStart = to.writerIndex(); - dataWriter.accept(to); - int writerIndexDataEnd = to.writerIndex(); - to.writerIndex(lengthWriterIndex); - lengthWriter.accept(to, writerIndexDataEnd - writerIndexDataStart); - to.writerIndex(writerIndexDataEnd); - } - - public static void writeSizePrefixedData(ByteBuf to, ObjIntConsumer sizeWriter, ToIntFunction dataWriter) { - int sizeWriterIndex = to.writerIndex(); - sizeWriter.accept(to, 0); - int size = dataWriter.applyAsInt(to); - int writerIndexDataEnd = to.writerIndex(); - to.writerIndex(sizeWriterIndex); - sizeWriter.accept(to, size); - to.writerIndex(writerIndexDataEnd); - } - - - private static int getAnvilIndex(int x, int y, int z) { - return (y << 8) + (z << 4) + x; - } - - public static boolean instanceOf(Class clazz, Object o) { - try { - T t = (T) o; - return true; - } catch (Exception e) { - return false; - } - } -} diff --git a/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java b/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java index 3483a927c..2d2aceabf 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java +++ b/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java @@ -42,11 +42,11 @@ public class SkinProvider { } public static Skin getCachedSkin(UUID uuid) { - return cachedSkins.get(uuid); + return cachedSkins.getOrDefault(uuid, EMPTY_SKIN); } public static Cape getCachedCape(String capeUrl) { - return cachedCapes.get(capeUrl); + return capeUrl != null ? cachedCapes.getOrDefault(capeUrl, EMPTY_CAPE) : EMPTY_CAPE; } public static CompletableFuture requestSkinAndCape(UUID playerId, String skinUrl, String capeUrl) { From 0e97e5233762be5406fd6e819cb2de47e15c192b Mon Sep 17 00:00:00 2001 From: Tim203 Date: Thu, 10 Oct 2019 23:27:30 +0200 Subject: [PATCH 7/8] Only notify debuggers that parsing GameProfile data failed and use the default skin and cape --- .../geysermc/connector/utils/SkinUtils.java | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java b/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java index 7254637af..1202b4df8 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java @@ -67,23 +67,29 @@ public class SkinUtils { private boolean alex; public static GameProfileData from(GameProfile profile) { - GameProfile.Property skinProperty = profile.getProperty("textures"); + try { + GameProfile.Property skinProperty = profile.getProperty("textures"); - JsonObject skinObject = SkinProvider.GSON.fromJson(new String(Base64.getDecoder().decode(skinProperty.getValue()), Charsets.UTF_8), JsonObject.class); - JsonObject textures = skinObject.getAsJsonObject("textures"); + JsonObject skinObject = SkinProvider.GSON.fromJson(new String(Base64.getDecoder().decode(skinProperty.getValue()), Charsets.UTF_8), JsonObject.class); + JsonObject textures = skinObject.getAsJsonObject("textures"); - JsonObject skinTexture = textures.getAsJsonObject("SKIN"); - String skinUrl = skinTexture.get("url").getAsString(); + JsonObject skinTexture = textures.getAsJsonObject("SKIN"); + String skinUrl = skinTexture.get("url").getAsString(); - boolean isAlex = skinTexture.has("metadata"); + boolean isAlex = skinTexture.has("metadata"); - String capeUrl = null; - if (textures.has("CAPE")) { - JsonObject capeTexture = textures.getAsJsonObject("CAPE"); - capeUrl = capeTexture.get("url").getAsString(); + String capeUrl = null; + if (textures.has("CAPE")) { + JsonObject capeTexture = textures.getAsJsonObject("CAPE"); + capeUrl = capeTexture.get("url").getAsString(); + } + + return new GameProfileData(skinUrl, capeUrl, isAlex); + } catch (Exception exception) { + // return default skin with default cape when texture data is invalid + Geyser.getLogger().debug("Got invalid texture data for " + profile.getName() + " " + exception.getMessage()); + return new GameProfileData("", "", false); } - - return new GameProfileData(skinUrl, capeUrl, isAlex); } } From c4620171495f25d7500197fb606befaedc677824 Mon Sep 17 00:00:00 2001 From: Tim203 Date: Fri, 11 Oct 2019 00:15:23 +0200 Subject: [PATCH 8/8] Improved remapper performance --- .../geysermc/connector/utils/Remapper.java | 58 ++++++------------- 1 file changed, 19 insertions(+), 39 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/utils/Remapper.java b/connector/src/main/java/org/geysermc/connector/utils/Remapper.java index bccc4fd4e..8f846b20b 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/Remapper.java +++ b/connector/src/main/java/org/geysermc/connector/utils/Remapper.java @@ -3,20 +3,15 @@ package org.geysermc.connector.utils; import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; import com.nukkitx.protocol.bedrock.data.ItemData; import org.geysermc.connector.network.translators.block.JavaBlock; +import org.geysermc.connector.network.translators.block.type.*; import org.geysermc.connector.network.translators.item.BedrockItem; import org.geysermc.connector.network.translators.item.JavaItem; -import org.geysermc.connector.network.translators.block.type.ColoredBlock; -import org.geysermc.connector.network.translators.block.type.DyeColor; -import org.geysermc.connector.network.translators.block.type.StoneType; -import org.geysermc.connector.network.translators.block.type.WoodBlock; -import org.geysermc.connector.network.translators.block.type.WoodType; import java.util.HashMap; import java.util.Map; public class Remapper { - - public static final String MINECRAFT = "minecraft:"; + public static final String MINECRAFT_PREFIX = "minecraft:"; public static final Remapper ITEM_REMAPPER = new Remapper(); public static final Remapper BLOCK_REMAPPER = new Remapper(); @@ -37,7 +32,7 @@ public class Remapper { // Colorable block remapping for (ColoredBlock coloredBlock : ColoredBlock.values()) { - if (!getBedrockIdentifier(coloredBlock.name()).equalsIgnoreCase(bedrockItem.getIdentifier().replace(MINECRAFT, ""))) + if (!getBedrockIdentifier(coloredBlock.name()).equalsIgnoreCase(bedrockItem.getIdentifier().replace(MINECRAFT_PREFIX, ""))) continue; // The item must be colorable @@ -46,13 +41,13 @@ public class Remapper { continue; // Add the color to the identifier - identifier = MINECRAFT + color.name().toLowerCase() + "_" + coloredBlock.name().toLowerCase(); + identifier = MINECRAFT_PREFIX + color.name().toLowerCase() + "_" + coloredBlock.name().toLowerCase(); } } // Wood remapping for (WoodBlock woodBlock : WoodBlock.values()) { - if (!getBedrockIdentifier(woodBlock.name()).equalsIgnoreCase(bedrockItem.getIdentifier().replace(MINECRAFT, ""))) + if (!getBedrockIdentifier(woodBlock.name()).equalsIgnoreCase(bedrockItem.getIdentifier().replace(MINECRAFT_PREFIX, ""))) continue; if (isTool(bedrockItem.getIdentifier())) @@ -65,36 +60,36 @@ public class Remapper { if (woodType.getId() != bedrockItem.getData()) continue; - identifier = MINECRAFT + woodType.name().toLowerCase() + "_" + woodBlock.name().toLowerCase(); + identifier = MINECRAFT_PREFIX + woodType.name().toLowerCase() + "_" + woodBlock.name().toLowerCase(); } } // Stone remapping - if (bedrockItem.getIdentifier().replace(MINECRAFT, "").equalsIgnoreCase("stone") && !isTool(bedrockItem.getIdentifier())) { + if (bedrockItem.getIdentifier().replace(MINECRAFT_PREFIX, "").equalsIgnoreCase("stone") && !isTool(bedrockItem.getIdentifier())) { for (StoneType stoneType : StoneType.values()) { if (stoneType.getId() != bedrockItem.getData()) continue; // Set the identifier to stone - identifier = MINECRAFT + stoneType.name().toLowerCase(); + identifier = MINECRAFT_PREFIX + stoneType.name().toLowerCase(); } } // Grass remapping - if (bedrockItem.getIdentifier().replace(MINECRAFT, "").equalsIgnoreCase("grass")) { - identifier = MINECRAFT + "grass_block"; + if (bedrockItem.getIdentifier().replace(MINECRAFT_PREFIX, "").equalsIgnoreCase("grass")) { + identifier = MINECRAFT_PREFIX + "grass_block"; } - if (bedrockItem.getIdentifier().replace(MINECRAFT, "").equalsIgnoreCase("tallgrass")) { - identifier = MINECRAFT + "grass"; + if (bedrockItem.getIdentifier().replace(MINECRAFT_PREFIX, "").equalsIgnoreCase("tallgrass")) { + identifier = MINECRAFT_PREFIX + "grass"; } // Dirt remapping - if (bedrockItem.getIdentifier().replace(MINECRAFT, "").equalsIgnoreCase("dirt")) { + if (bedrockItem.getIdentifier().replace(MINECRAFT_PREFIX, "").equalsIgnoreCase("dirt")) { if (bedrockItem.getData() == 0) - identifier = MINECRAFT + "dirt"; + identifier = MINECRAFT_PREFIX + "dirt"; else - identifier = MINECRAFT + "coarse_dirt"; + identifier = MINECRAFT_PREFIX + "coarse_dirt"; } for (Map.Entry javaItemEntry : javaItems.entrySet()) { @@ -113,34 +108,19 @@ public class Remapper { return bedrockToJava.get(bedrockItem.getValue()); } - return null; } public BedrockItem convertToBedrock(ItemStack item) { - for (Map.Entry javaItem : Toolbox.JAVA_ITEMS.entrySet()) { - if (javaItem.getValue().getId() != item.getId()) - continue; - - return javaToBedrock.get(javaItem.getValue()); - } - - return null; + JavaItem javaItem = Toolbox.JAVA_ITEMS.get(item.getId()); + return javaItem != null ? javaToBedrock.get(javaItem) : null; } public BedrockItem convertToBedrockB(ItemStack block) { - for (Map.Entry javaItem : Toolbox.JAVA_BLOCKS.entrySet()) { - if (javaItem.getValue().getId() != block.getId()) - continue; - - return javaToBedrock.get(javaItem.getValue()); - } - - return null; + JavaBlock javaBlock = Toolbox.JAVA_BLOCKS.get(block.getId()); + return javaBlock != null ? javaToBedrock.get(javaBlock) : null; } - - private static String getBedrockIdentifier(String javaIdentifier) { javaIdentifier = javaIdentifier.toLowerCase(); javaIdentifier = javaIdentifier.replace("terracotta", "stained_hardened_clay");