diff --git a/connector/pom.xml b/connector/pom.xml
index c135c245..25d52a00 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 d70478a0..af8e4e31 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 036829e8..89c1532f 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 6b1b1c11..f9f6e37f 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 0b9f4180..67393114 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 1d7dfe88..b907187d 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 af475e5c..f8691bea 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 41af7e57..3bb702db 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 5f3569dc..534f2286 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 fdab51ba..b8382b8c 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 2fad8c21..9b206018 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 ad7645a4..e4ab1b1d 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 e2f4fa58..862117f9 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 8b89e3be..3073e469 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 f583ea91..34addcee 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 968b0b3a..688a00ba 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 63d1b927..6ce67533 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 298661c0..3483a927 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 00000000..7254637a
--- /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);
+ });
+ });
+ }
+}